Capitulo 02
Data Acquisition de Datos
Del dato crudo en la API a tablas limpias que un modelo puede usar con confianza
Tipos de datos, formatos CSV/Excel/Parquet, adquisicion con APIs, manejo de credenciales, normalizacion de JSON, DML, TCL y herramientas distribuidas.
Un modelo no mejora solo con mejor algoritmo: mejora cuando recibe datos mas representativos, actualizados y limpios. La adquisicion es la etapa donde se decide que informacion entra al pipeline y bajo que condiciones.
El dato que nunca llegas a adquirir no puede mejorar un modelo.
— Principio de adquisicion de datos
2.1 Tipos de datos: estructurado, semiestructurado y no estructurado
Antes de consumir una API o abrir un archivo, conviene clasificar el dato por su estructura, porque cada tipo exige herramientas y reglas distintas.
| Tipo | Estructura | Ejemplo | Herramienta tipica |
|---|---|---|---|
| Estructurado | Filas y columnas con esquema fijo. | Tabla de ventas, CSV bien formado. | SQL, Pandas. |
| Semiestructurado | Jerarquias flexibles con nombres. | JSON, XML, Parquet con schema. | requests, json, pandas. |
| No estructurado | Sin esquema predefinido. | Texto libre, imagenes, audio. | NLP, vision, vectores. |
La frontera es util para decidir, no para etiquetar. Un JSON puede convertirse en tablas y un texto puede volverse estructurado con un preprocesamiento. Lo importante es saber en que forma llega el dato y que transformacion necesita.
Dato estructurado
M2Informacion organizada en filas y columnas con un esquema y tipos definidos, lista para consultas y analisis.
Ej: Una tabla de prestamos con id_prestamo, id_usuario, id_libro y fecha.
2.2 Formatos tabulares: CSV vs Excel vs Parquet
Los datos tabulares viajan en tres formatos que se confunden pero sirven para contextos distintos.
| Formato | Tipo | Ventaja principal | Riesgo |
|---|---|---|---|
| CSV | Texto plano | Universal, legible, portable. | Sin tipos ni esquema fuerte. |
| Excel | Libro de hojas | Formulas, vistas y formato de oficina. | Lento, con metadatos ocultos, no apto para volumen. |
| Parquet | Columnar binario | Comprime y lee solo columnas necesarias. | Requiere herramientas que lo entiendan. |
Parquet es el formato estandar del mundo analitico moderno: guarda el schema con los datos y permite lecturas parciales por columna, lo que acelera consultas sobre tablas grandes.
import pandas as pd
ventas = pd.read_csv('data/ventas_tecnologia.csv')
ventas.head(3)
print(ventas.dtypes)
La regla practica es: CSV para intercambio y prototipos, Excel para reportes de oficina que reciben personas, Parquet para pipelines analiticos que procesan volumen.
CSV no declara tipos: un 2026-02-11 puede leerse como texto o como fecha segun quien lo abra. Parquet y las tablas SQL declaran el tipo en el propio archivo. Preferir formatos con schema reduce sorpresas en la siguiente etapa.
2.3 Data Acquisition: el puente entre la realidad y el codigo
Data acquisition es el proceso de obtener datos desde una fuente externa y dejarlos disponibles para analisis. Es el primer eslabon del pipeline y define que informacion llega al modelo.
Fuente (API, archivo, base, sensor)
↓
Extraccion
↓
Validacion y normalizacion
↓
Tablas o almacenamiento
↓
Analisis y Machine Learning
Cada paso anterior condiciona al siguiente. Si la extraccion mezcla paginas duplicadas, no se corrige mas adelante con un buen modelo: se arrastra el error.
Data acquisition
M2Proceso de obtener informacion desde una fuente externa (API, archivo, base o sensor) y dejarla lista para validar, transformar y analizar.
Ej: Consumir el endpoint /ventas de una API, paginando los resultados y guardandolos en Parquet.
2.4 Conceptos de APIs
Una API expone datos o funcionalidad a traves de HTTP. Para consumirla necesitas entender tres piezas: endpoints, autenticacion y paginacion.
Endpoints y metodos
Un endpoint es una direccion que responde una operacion. El metodo HTTP indica la intencion.
| Metodo | Intencion | Ejemplo |
|---|---|---|
| GET | Leer informacion. | Listar libros. |
| POST | Crear un recurso. | Registrar un prestamo. |
| PUT | Reemplazar un recurso. | Actualizar un libro completo. |
| PATCH | Actualizar parcialmente. | Cambiar solo el estado. |
| DELETE | Eliminar un recurso. | Quitar un libro. |
import requests
respuesta = requests.get('https://api.example.com/v1/libros')
print(respuesta.status_code)
print(respuesta.json())
El codigo de estado importa tanto como los datos: 200 lectura correcta, 404 recurso inexistente, 401 sin autenticacion valida, 429 demasiadas peticiones.
Autenticacion
La mayoria de las APIs exigen una credencial. El patron habitual es enviar un token en el header Authorization:
import requests
headers = {'Authorization': 'Bearer TU_TOKEN'}
respuesta = requests.get('https://api.example.com/v1/libros', headers=headers)
print(respuesta.json())
Paginacion
Una API no devuelve millones de registros en una sola respuesta. Divide el resultado en paginas y el consumidor las recorre.
import requests
headers = {'Authorization': 'Bearer TU_TOKEN'}
pagina = 1
while True:
params = {'page': pagina, 'limit': 100}
respuesta = requests.get(
'https://api.example.com/v1/libros',
headers=headers,
params=params,
)
respuesta.raise_for_status()
datos = respuesta.json()
if not datos:
break
# aqui se guarda o se procesa el lote de cada pagina
pagina += 1
Si solo lees la primera pagina, tu dataset esta incompleto aunque el codigo no falle. Define el criterio de corte (pagina vacia, pagina final o cursor) y registra cuantas paginas procesaste.
2.5 Consumir APIs con requests y manejar credenciales (.env)
requests es la libreria estandar de facto para HTTP en Python. Las credenciales no deben escribirse en el codigo: se guardan en un archivo .env que no se sube al repositorio.
# .env (no lo subas a git)
API_TOKEN=tu_token_secreto
BASE_URL=https://api.example.com/v1
import os
import requests
from dotenv import load_dotenv
load_dotenv()
headers = {'Authorization': f"Bearer {os.environ['API_TOKEN']}"}
base_url = os.environ['BASE_URL']
respuesta = requests.get(f'{base_url}/libros', headers=headers)
respuesta.raise_for_status()
libros = respuesta.json()
load_dotenv() lee las variables del archivo .env hacia el entorno del proceso. Tu codigo solo referencia nombres, nunca valores secretos.
Un token en un script que se comparte, se publica o se sube a git queda expuesto para siempre. Guarda secretos en .env, agrega .env a .gitignore y rota la credencial si alguna vez se filtro.
Convertir la respuesta en tabla
Una vez obtenida la lista, se convierte en un DataFrame:
import pandas as pd
libros = pd.json_normalize(
libros,
sep='_',
)
print(libros.head())
print(libros.columns.tolist())
json_normalize aplanara el JSON y te devolvera columnas; con sep='_' convierte las claves anidadas como usuario_id en lugar de usuario.id.
2.6 Normalizacion de JSON anidado a tablas relacionales
Las APIs devuelven jerarquias. Un pedido contiene items, y cada item tiene sus propios atributos. Para trabajar en formato tabular hay que aplanar esa jerarquia y, cuando sea necesario, separarla en mas de una tabla.
import pandas as pd
respuesta = {
'pedidos': [
{
'id_pedido': 101,
'total': 250,
'items': [{'producto': 'Teclado', 'cantidad': 1}],
},
{
'id_pedido': 102,
'total': 90,
'items': [
{'producto': 'Mouse', 'cantidad': 2},
{'producto': 'Pad', 'cantidad': 1},
],
},
]
}
items = pd.json_normalize(
respuesta['pedidos'],
record_path='items',
meta=['id_pedido', 'total'],
)
print(items)
La estructura resultante conserva la relacion: cada item lleva el id_pedido de su pedido. Esa clave permite conectarlo con la tabla de pedidos, igual que una clave foranea en SQL.
Del JSON a un modelo relacional
Cuando la jerarquia tiene varios niveles, aplanar todo en una sola tabla genera repeticion. El criterio es el mismo de la normalizacion relacional: cada hecho vive en su propia tabla y se conecta por claves.
| Fuente JSON | Tabla relacional |
|---|---|
pedidos[].id_pedido | pedidos.id_pedido (PK) |
pedidos[].items[] | items.id_pedido (FK) + items.producto |
pedidos[].usuario | usuarios.id_usuario (PK) |
💡 Piensa en repetir la informacion del pedido por cada item.
2.7 DML: crear, consultar, actualizar y eliminar datos
Una vez que el dato esta normalizado, se escribe en la base. DML (Data Manipulation Language) son las operaciones sobre los registros.
-- INSERT: agregar
INSERT INTO libros (id_libro, titulo, id_categoria)
VALUES (10, 'Cien anos de soledad', 3);
-- SELECT: consultar
SELECT titulo, id_categoria
FROM libros
WHERE id_categoria = 3;
-- UPDATE: modificar con filtro seguro
UPDATE libros
SET id_categoria = 4
WHERE id_libro = 10;
-- DELETE: eliminar solo lo que corresponde
DELETE FROM libros
WHERE id_libro = 10;
Antes de ejecutar UPDATE o DELETE, convierte el mismo filtro en un SELECT y revisa las filas afectadas. Un DELETE sin WHERE puede borrar toda una tabla.
Cuando el valor viene de una aplicacion o de una persona, no lo concatentes en el SQL. Usa parametros:
import sqlite3
conn = sqlite3.connect(':memory:')
conn.execute('CREATE TABLE libros (id_libro INTEGER, titulo TEXT)')
titulo = "El tunel' OR 1=1 --"
consulta = 'SELECT * FROM libros WHERE titulo = ?'
filas = conn.execute(consulta, (titulo,)).fetchall()
El ? separa el codigo SQL del valor y evita inyeccion SQL.
2.8 TCL: asegurar cambios con transacciones y ACID
TCL (Transaction Control Language) agrupa cambios para que ocurran todos o ninguno.
BEGIN TRANSACTION;
INSERT INTO prestamos (id_usuario, id_libro, fecha_prestamo)
VALUES (2, 10, '2026-03-01');
UPDATE libros SET disponible = 0 WHERE id_libro = 10;
-- Si ambas operaciones son correctas:
COMMIT;
-- Si algo falla antes del commit:
-- ROLLBACK;
Las transacciones se describen con ACID:
- Atomicidad: la operacion ocurre completa o no ocurre.
- Consistencia: las restricciones siguen siendo verdaderas antes y despues.
- Aislamiento: una transaccion intermedia no se confunde con otra.
- Durabilidad: despues de
COMMIT, el cambio persiste aunque el proceso termine.
La idea es todo o nada: un prestamo no debe registrarse si el libro no marco como no disponible, y viceversa.
2.9 Big Data: por que escalar cambia la forma de trabajar
Cuando el dataset supera la memoria de una maquina, el problema deja de ser solo de codigo y pasa a ser de arquitectura.
| Limite de una maquina | Consecuencia |
|---|---|
| Memoria | El DataFrame no entra completo. |
| Disco | El archivo no cabe o es lento de leer. |
| CPU | El procesamiento tarda demasiado. |
| Red | Descargar todo el dataset no es viable. |
Big Data se suele describir con tres V:
- Volumen: cantidad de datos que no cabe en un proceso simple.
- Velocidad: datos que llegan continuamente, como eventos o sensores.
- Variedad: formatos y estructuras que deben convivir.
Antes de montar un cluster, revisa si el problema es un SELECT *, un join sin filtro, un formato de archivo innecesariamente pesado o una transformacion duplicada. A veces cambiar a Parquet y leer solo columnas resuelve lo que parecia “Big Data”.
2.10 Herramientas distribuidas y frameworks
Cuando de verdad necesitas escalar, aparecen herramientas que reparten el trabajo en particiones.
| Concepto | Descripcion |
|---|---|
| Pandas | Dataset manejable en una maquina Base de la mayoria de los flujos. |
| Polars | Alto rendimiento local con expresiones Misma maquina, mejor uso de CPU. |
| DuckDB | Consultar CSV/Parquet con SQL Sin cluster, gran velocidad local. |
| Dask | Escalar APIs tipo Pandas Varios procesos o una maquina grande. |
| Spark | Grandes volumenes y clusters Costo y complejidad operativa. |
Tres conceptos aparecen cuando escalas:
- Particion: fragmento de datos que puede procesarse o leerse de manera independiente.
- Lazy execution: las transformaciones se describen primero y se ejecutan cuando se pide el resultado; permite optimizar el plan completo.
- Shuffle: movimiento de datos entre particiones para agrupar o unir; suele ser una de las operaciones mas costosas.
La herramienta no reemplaza el razonamiento. Antes de distribuir, mide volumen, memoria, tiempo, costo y complejidad operativa.
- Elige una API publica (o el dataset de ventas del modulo anterior).
- Guarda la credencial en un archivo
.envy cargala conpython-dotenv. - Consume el endpoint con
requests, manejando paginacion y codigos de estado. - Normaliza el JSON con
pandas.json_normalizey separa en tablas si la jerarquia lo pide. - Escribe el resultado en SQLite con
INSERTdentro de una transaccion. - Documenta cuantas paginas procesaste y con que fecha de corte.
2.11 Preguntas de entrevista
Q: ¿Como evitarias que una credencial de API se filtre en el repositorio?
Guardando el secreto en `.env`, cargandolo con `python-dotenv` y agregando `.env` a `.gitignore` para que el codigo solo referencie nombres de variables.
🪤 Decir que basta con no compartir el script, o peor, commitear el token en un archivo de configuracion.Q: ¿Por que separar un JSON anidado en varias tablas en lugar de dejar una sola tabla ancha?
Para que cada fila conserve un unico significado. Si repites el pedido por cada item, cambiar la informacion del pedido exige actualizar muchas filas y el analisis se vuelve ambiguo. Con claves foraneas, la relacion se conserva sin repeticion.
🪤 Creer que una tabla ancha es siempre mas rapida de consultar.