Formatos de Archivo
Los formatos columnares (Parquet, Arrow) dominan los pipelines de análisis: comprimen bien, incrustan el esquema y permiten a los lectores cargar solo las columnas necesarias.
Receta
Tarjeta de referencia rápida - lista para copiar y pegar.
import pandas as pd
df = pd.read_csv("orders.csv", parse_dates=["ordered_at"])
df.to_parquet(
"lake/orders/dt=2025-01-15/data.parquet",
index=False,
compression="zstd",
)
# Leer de nuevo columnas seleccionadas
subset = pd.read_parquet("lake/orders", columns=["order_id", "revenue"], filters=[("dt", ">=", "2025-01-01")]Cuándo usar esto:
- Capas raw y mart de Lakehouse en almacenamiento de objetos
- Transferencia entre Python, Spark, Polars y DuckDB
- Reducción del costo de almacenamiento frente a CSV con gzip
- Evolución del esquema con columnas documentadas
Ejemplo de Trabajo
import pandas as pd
import pyarrow as pa
import pyarrow.parquet as pq
from pathlib import Path
df = pd.DataFrame(
{
"order_id": [1, 2, 3],
"region": pd.Categorical(["East", "West", "East"]),
"revenue": [120.0, 340.0, 150.0],
"ordered_at": pd.to_datetime(["2025-01-01", "2025-01-02", "2025-01-03"], utc=True),
}
)
# pandas -> Tabla Arrow (copia cero cuando es posible)
table = pa.Table.from_pandas(df, preserve_index=False)
out_dir = Path("lake/orders/dt=2025-01-15")
out_dir.mkdir(parents=True, exist_ok=True)
pq.write_table(
table,
out_dir / "data.parquet",
compression="zstd",
use_dictionary=True,
)
# Leer metadatos sin cargar datos
meta = pq.read_metadata(out_dir / "data.parquet")
print("filas:", meta.num_rows, "columnas:", meta.num_columns)
# Lectura con poda de columnas
loaded = pq.read_table(out_dir / "data.parquet", columns=["order_id", "revenue"])
print(loaded.to_pandas())Lo que esto demuestra:
- Directorio de partición Hive
dt=YYYY-MM-DD - Arrow como intercambio entre pandas y el escritor de Parquet
- Inspección de metadatos para verificaciones operativas
- Poda de columnas en la lectura
Inmersión Profunda
Cómo Funciona
- Parquet almacena fragmentos de columnas con estadísticas (mín/máx) por grupo de filas.
- Arrow define el diseño en memoria; pandas 2.2+ usa cadenas basadas en Arrow.
- Los lectores omiten grupos de filas usando estadísticas cuando los filtros coinciden.
- Las carpetas de partición (
clave=valor/) emulan el particionamiento de almacén en archivos.
Comparación de Formatos
| Formato | Fortaleza | Debilidad |
|---|---|---|
| CSV | Legible por humanos | Sin esquema, voluminoso, lento |
| Líneas JSON | Eventos semiestructurados | Verboso, análisis lento |
| Parquet | Estándar de análisis | No legible por humanos |
| Arrow IPC | IPC local rápido | No archivo a largo plazo |
Notas de Python
import polars as pl
# Escaneo de Polars con empuje de proyección
lf = pl.scan_parquet("lake/orders/**/*.parquet").select("order_id", "revenue")Errores Comunes
- Escribir index=True - columna
indexsorpresa en Parquet. Solución:index=Falsesiempre en pipelines. - Problema de archivos pequeños - miles de archivos de 1 MB ralentizan la listado. Solución: apuntar a 128-512 MB por archivo; compactar particiones.
- Desajuste de esquema entre días -
mergeSchemaoculta cambios disruptivos. Solución: registro y validación de esquema explícitos. - Marcas de tiempo sin zona horaria en Parquet - repeticiones ambiguas. Solución: almacenar UTC con dtype
datetime64[ns, UTC]. - CSV como formato de mart - costo de reanálisis en cada lectura. Solución: marts de Parquet; CSV solo para exportación humana.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
| Delta/Iceberg/Hudi | Upserts ACID en el lago | Particiones simples de sobrescritura diaria |
| Avro | Evolución del esquema de Kafka | Poda de columnas de análisis |
| ORC | Clústeres heredados de Hive | Lago de Python nuevo |
| Archivo SQLite DuckDB | Análisis local incrustado | Almacenamiento de objetos multi-escritor |
Preguntas Frecuentes
¿snappy o zstd?
- zstd mejor ratio de compresión; un poco más de CPU.
- zstd por defecto para archivos; snappy para escrituras de latencia ultra baja.
¿Qué tan grandes deben ser los archivos Parquet?
- Apuntar a 128-512 MB de equivalente sin comprimir por archivo.
- Los trabajos de compactación fusionan fragmentos diarios pequeños mensualmente.
¿Columna de partición también en los datos?
- Redundante si solo está en la ruta - ahorra espacio.
- Incluir si los consumidores consultan sin poda de partición.
¿Cómo leo con DuckDB?
import duckdb
duckdb.sql("SELECT region, SUM(revenue) FROM read_parquet('lake/orders/**') GROUP BY 1")¿Arrow vs Parquet?
- Arrow: intercambio en memoria.
- Parquet: formato de persistencia en disco.
¿Cómo cifro en reposo?
- SSE en políticas de bucket de S3/GCS.
- Cifrado del lado del cliente raro a menos que el cumplimiento lo exija.
¿Datos anidados?
- Parquet soporta structs/listas para eventos tipo JSON.
- Aplanar para marts amigables para BI cuando sea posible.
¿Cómo comparo esquemas?
pyarrow.parquet.read_schemaen dos rutas y comparar.- Automatizar en CI en nuevas particiones.
¿pandas vs pyarrow write?
to_parquetde pandas usa pyarrow/fastparquet internamente.pq.write_tablecuando ya tienes una Tabla Arrow.
¿Intercambio entre Polars y pandas?
- Puente Arrow:
pl.from_pandas/to_pandascopia cero cuando los dtypes coinciden.
Relacionado
- Rendimiento y Memoria - poda de columnas
- PySpark - escrituras distribuidas de Parquet
- Fundamentos de Ingeniería de Datos - sobrescritura de particiones
- Validación y Calidad de Datos - puertas de esquema
Versiones de Stack: Esta página fue escrita para Python 3.14.0 (estable 3.14, mantenimiento 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, y uv 0.6+.