Apache Parquet es un formato de almacenamiento columnar de código abierto diseñado para análisis de datos a gran escala. A diferencia de CSV —donde los datos se almacenan fila por fila— Parquet organiza los datos por columna, lo que lo hace ideal para consultas analíticas que acceden solo a un subconjunto de columnas.
Parquet vs CSV vs JSON: comparativa
| Característica | CSV | JSON | Parquet |
|---|---|---|---|
| Orientación | Fila | Fila | Columnar |
| Compresión integrada | No | No | Snappy / Gzip / Zstd |
| Tipos de datos | Solo strings | Básicos | Ricos (nested, decimal…) |
| Lectura parcial rápida | No | No | Sí |
| Legibilidad humana | Sí | Sí | No (binario) |
| Tamaño típico (relativo) | 100 % | 150 % | 20–30 % |
| Schema embebido | No | No | Sí |
Para datasets analíticos de más de 1 millón de filas, Parquet suele ser 5–20× más rápido de leer y 3–10× más pequeño que CSV equivalente.
Instalación
pip install pandas pyarrow
# PyArrow es el motor recomendado; fastparquet es alternativa ligera
pip install fastparquet # opcional
Leer y escribir con pandas
import pandas as pd
df = pd.DataFrame({
'nombre': ['Ana', 'Luis', 'María'],
'edad': [25, 30, 28],
'salario': [50_000.0, 65_000.0, 72_000.0],
'activo': [True, True, False],
})
# Escribir
df.to_parquet('empleados.parquet', engine='pyarrow', compression='snappy')
# Leer completo
df2 = pd.read_parquet('empleados.parquet')
print(df2.dtypes)
# nombre object
# edad int64
# salario float64
# activo bool
# Los tipos se preservan exactamente — sin conversiones manuales
Selección de columnas y predicate pushdown
import pyarrow.parquet as pq
# Leer solo columnas necesarias — mucho más rápido en archivos grandes
df_parcial = pd.read_parquet('empleados.parquet', columns=['nombre', 'salario'])
# Filtros push-down: PyArrow filtra ANTES de cargar en RAM
tabla = pq.read_table(
'empleados.parquet',
columns=['nombre', 'salario'],
filters=[('salario', '>', 60_000)],
)
df_filtrado = tabla.to_pandas()
print(df_filtrado)
# nombre salario
# 1 Luis 65000.0
# 2 María 72000.0
Codecs de compresión
# Snappy — equilibrio velocidad/compresión (recomendado)
df.to_parquet('datos.parquet', compression='snappy')
# Gzip — mejor ratio de compresión, más lento
df.to_parquet('datos.parquet', compression='gzip')
# Zstd — mejor que gzip con velocidad similar a snappy (pandas ≥ 1.5)
df.to_parquet('datos.parquet', compression='zstd')
# Sin compresión
df.to_parquet('datos.parquet', compression=None)
import os
for codec in ['snappy', 'gzip', 'zstd']:
df.to_parquet(f'datos_{codec}.parquet', compression=codec)
tam = os.path.getsize(f'datos_{codec}.parquet')
print(f'{codec:8s}: {tam:,} bytes')
Particionado de datos
El particionado divide el dataset en archivos separados por valor de columna, ideal para series temporales o datos por región:
import pandas as pd, numpy as np
n = 100_000
df_grande = pd.DataFrame({
'fecha': pd.date_range('2024-01-01', periods=n, freq='1h'),
'region': np.random.choice(['norte', 'sur', 'este', 'oeste'], n),
'ventas': np.random.uniform(100, 10_000, n),
})
df_grande['anio'] = df_grande['fecha'].dt.year
df_grande['mes'] = df_grande['fecha'].dt.month
# Escribir particionado
df_grande.to_parquet(
'ventas_particionado/',
partition_cols=['anio', 'mes'],
engine='pyarrow',
)
# Estructura: ventas_particionado/anio=2024/mes=1/xxx.parquet
# /mes=2/xxx.parquet …
# Leer solo un mes (carga únicamente los archivos de esa partición)
df_marzo = pd.read_parquet(
'ventas_particionado/',
filters=[('mes', '==', 3)],
)
print(f"Filas de marzo: {len(df_marzo):,}")
Inspeccionar schema y metadata
import pyarrow.parquet as pq
meta = pq.read_metadata('empleados.parquet')
print(f"Filas: {meta.num_rows}")
print(f"Row groups: {meta.num_row_groups}")
print(f"Columnas: {meta.num_columns}")
schema = pq.read_schema('empleados.parquet')
print(schema)
# nombre: string
# edad: int64
# salario: double
# activo: bool
Conversión CSV → Parquet
import pandas as pd
# Conversión directa
df = pd.read_csv('datos.csv')
df.to_parquet('datos.parquet', compression='zstd')
# Por chunks para archivos enormes (> 1 GB)
import pyarrow as pa
import pyarrow.parquet as pq
writer = None
for chunk in pd.read_csv('gigante.csv', chunksize=100_000):
tabla = pa.Table.from_pandas(chunk)
if writer is None:
writer = pq.ParquetWriter('gigante.parquet', tabla.schema)
writer.write_table(tabla)
if writer:
writer.close()
print("Conversión por chunks completa")
Cuándo usar Parquet
Usa Parquet cuando:
- Tienes más de 100 000 filas y necesitas consultas analíticas rápidas
- Quieres preservar tipos de datos sin conversiones manuales
- Trabajas en pipelines con Spark, Dask o DuckDB
- El espacio en disco o almacenamiento en la nube importa
Prefiere CSV / JSON cuando:
- Necesitas legibilidad humana o edición manual
- Compartes datos con usuarios no técnicos
- El dataset es pequeño (< 10 000 filas)
- La compatibilidad universal es prioritaria
Conversiones relacionadas
Conversiones frecuentes del catálogo: