42 · Lectura y escritura de datos
Todo proyecto de ML comienza con pd.read_csv. Domina las opciones clave, entiende las alternativas JSON y Parquet, y aprende el patrón de lectura por chunks para datasets más grandes que la RAM.
Pandas es el **importador de datos universal**. Una función — `pd.read_csv` — maneja archivos separados por comas, tabulaciones, punto y coma, ancho fijo y comprimidos con gzip. Una segunda función — `pd.read_parquet` — maneja el formato binario columnar que es 3–10× más pequeño y 5–50× más rápido de leer que CSV para cargas de trabajo analíticas. Entender las opciones de `read_csv` (`usecols`, `dtype`, `parse_dates`, `chunksize`) no es conocimiento opcional — es cómo controlas el uso de memoria, los tiempos de análisis y la corrección de tipos en toda pipeline en producción.
Sin esto:
Sin conocer `usecols`, cargar un CSV de 50 columnas para usar 3 desperdicia el 94% de la E/S. Sin `dtype`, pandas infiere tipos escaneando el archivo completo — lento y a veces incorrecto (una columna de códigos postales llena de `'01234'` se convierte en entero, perdiendo el cero inicial). Sin `chunksize`, un CSV de 10 GB colapsa una máquina con 16 GB de RAM. Estos no son casos extremos — son desafíos estándar en todo dataset del mundo real.
Pandas incluye una familia de funciones read_* que cubre casi todos los formatos de archivo que encontrarás en ML:
pd.read_csv(path)— el caballo de batalla. Maneja.csv,.tsv,.txt, incluso.csv.gz(gzip detectado automáticamente por extensión).pd.read_json(path)— archivos JSON. Usaorient='records'para el layout más común (lista de dicts).pd.read_parquet(path)— formato binario Parquet. Requierepyarrowofastparquet. Tipado, comprimido, columnar — el sucesor de nivel producción del CSV.pd.read_excel(path)—.xlsx/.xls. Requiereopenpyxl.pd.read_sql(query, conn)— consulta SQL directa a un DataFrame.
Opciones clave de read_csv:
| Opción | Propósito | Ejemplo |
|---|---|---|
| sep | carácter delimitador | sep='\t' para TSV |
| header | número de fila para nombres de columna | header=None si no hay fila de encabezado |
| index_col | columna a usar como índice de fila | index_col=0 |
| usecols | cargar solo estas columnas | usecols=['age', 'income'] |
| dtype | anular la inferencia de tipos | dtype={'zip': str} |
| nrows | cargar solo las primeras N filas | nrows=1000 |
| parse_dates | analizar estas columnas como datetime | parse_dates=['date'] |
| chunksize | devolver un iterador de DataFrames | chunksize=10_000 |
La función de escritura espejo es df.to_csv(path, index=False). Pasa index=False para evitar escribir el índice de fila como una columna extra (el error más común de .to_csv).
Python (in browser)
Pyodide corre en un sistema de archivos virtual — `/tmp/` es escribible y funciona exactamente como un sistema de archivos real para demos de E/S. `df.equals(otro)` verifica shape, dtype y cada valor — es más estricto que `==` sobre DataFrames (que devuelve un DataFrame booleano elemento a elemento). Nota que `to_csv(index=False)` es esencial: sin él, el archivo guardado gana una primera columna sin nombre con números de fila que se convierte en `Unnamed: 0` al leer de vuelta.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
Python (in browser)
`usecols` omite completamente el análisis de las columnas no deseadas — para un CSV de 50 columnas donde necesitas 5, esto es una aceleración 10× y una reducción de memoria 10×. `dtype={'zip_code': str}` evita que pandas infiera `'01234'` como el entero `1234`. `parse_dates=['date']` convierte la columna de strings a `datetime64` para que puedas hacer aritmética de fechas inmediatamente.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
Python (in browser)
`orient='records'` es el layout JSON más común para datos de ML — cada fila es un dict, el archivo completo es una lista: `[{'name': 'alice', 'score': 0.91}, ...]`. Esto es lo que producen la mayoría de las APIs REST y los front-ends JavaScript. Otros orients (`'split'`, `'index'`, `'columns'`, `'values'`) se usan en contextos específicos — `'split'` es eficiente en espacio para datos dispersos.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
El patrón de lectura por chunks es cómo entrenas con datasets que no caben en RAM. Los métodos `partial_fit` de scikit-learn (SGDClassifier, SGDRegressor, MiniBatchKMeans) aceptan un chunk a la vez — combínalos con el lector por chunks para procesar datasets arbitrariamente grandes en un portátil. El tamaño del chunk (10.000 filas es un valor predeterminado común) es un equilibrio entre uso de memoria y overhead por iteración.
¿Qué hace `pd.read_csv(path, index_col=0)`?
- `pd.read_csv(path)` es el punto de entrada universal para datos tabulares. Siempre guarda con `to_csv(path, index=False)` para evitar una columna `Unnamed: 0` espuria al leer de vuelta.
- Usa `usecols` para cargar solo las columnas necesarias, `dtype` para anular la inferencia de tipos (especialmente para columnas de código postal/ID) y `parse_dates` para columnas de fecha.
- `pd.read_csv(path, chunksize=N)` devuelve un iterador — úsalo para procesar datasets más grandes que la RAM un lote a la vez.
- Parquet (`to_parquet` / `read_parquet`) es más pequeño, tipado y más rápido que CSV para consultas analíticas. Úsalo para cualquier dataset que vayas a leer más de una vez.
La primera línea de todo proyecto de ML es `df = pd.read_csv('data.csv')` o `df = pd.read_parquet('data.parquet')`. Las funciones utilitarias `fetch_*` de scikit-learn devuelven DataFrames internamente. El patrón lector por chunks + `partial_fit` es el enfoque estándar para el entrenamiento de modelos fuera de memoria (más grande que la RAM). Los datos de competencias de Kaggle son casi siempre un archivo `.csv` leído con `pd.read_csv`.
Si lo quitas: Sin conocimiento de `read_csv`, cargar datos correctamente (tipos correctos, columnas correctas, sin truncamiento silencioso) se convierte en prueba y error para cada nuevo dataset. La opción `dtype` por sí sola previene toda una categoría de bugs donde los IDs numéricos se suman en lugar de usarse como claves, o los códigos postales con cero inicial se convierten en enteros incorrectos. Estos bugs son invisibles hasta que se manifiestan como predicciones de modelo misteriosamente incorrectas.