Polars es una biblioteca de DataFrames orientada a expresiones, con ejecución eager y lazy. La respuesta rápida: usa pl.DataFrame para exploración inmediata y pl.scan_* con LazyFrame para pipelines sobre archivos, donde el optimizador puede adelantar filtros y leer solo las columnas necesarias. Compárala con Pandas mediante tu carga real, no con una promesa universal de velocidad.
El modelo resulta más claro al pensar en columnas y expresiones, no en bucles por fila. Una expresión describe una transformación y Polars combina expresiones independientes en un plan legible. La guía oficial de Polars presenta los conceptos, mientras la referencia de la API Python de Polars documenta firmas y tipos actuales.
Instalación y primer DataFrame
Crea un entorno aislado con la guía de venv en Python e instala el paquete:
python -m pip install polars
Este ejemplo calcula ingresos válidos por región sin modificar el DataFrame original:
import polars as pl
sales = pl.DataFrame(
{
"region": ["sur", "norte", "sur", "norte"],
"units": [2, 1, 3, 4],
"unit_price": [19.90, 35.00, 19.90, 12.50],
"cancelled": [False, False, True, False],
}
)
summary = (
sales
.filter(~pl.col("cancelled"))
.with_columns(
(pl.col("units") * pl.col("unit_price"))
.round(2)
.alias("revenue")
)
.group_by("region")
.agg(
pl.col("revenue").sum().alias("total_revenue"),
pl.len().alias("orders"),
)
.sort("total_revenue", descending=True)
)
print(summary)
pl.col no recupera una serie de inmediato: construye una expresión. with_columns añade o reemplaza columnas y group_by().agg() declara agregaciones. Los valores ausentes son null, mientras NaN es un valor flotante diferente. Trata cada uno deliberadamente con fill_null, drop_nulls u operaciones de NaN apropiadas.
Los tipos y esquemas importan
La inferencia es cómoda durante la exploración, pero las fronteras de producción deben controlar schemas. Fechas leídas como texto, enteros mezclados con valores vacíos e identificadores interpretados como números pueden crear errores de negocio sutiles. Inspecciona DataFrame.schema, especifica tipos de origen cuando sea necesario y aplica cast con una política consciente para datos inválidos.
No conviertas todo a texto para evitar errores. Eso aplaza el problema y debilita filtros, fechas y agregaciones. Tampoco elijas primero map_elements: las funciones Python por elemento impiden varias optimizaciones y suelen expresar peor la intención que las operaciones nativas. Para los fundamentos numéricos del ecosistema, consulta NumPy en Python.
Lazy API: describir antes de ejecutar
read_parquet devuelve un DataFrame eager. scan_parquet crea un LazyFrame: las transformaciones forman un plan lógico y collect() lo ejecuta. Esto permite projection pushdown y predicate pushdown: leer solo columnas seleccionadas y, cuando el formato lo permite, omitir datos que no superan el filtro.
import polars as pl
report = (
pl.scan_parquet("data/sales/*.parquet")
.filter(pl.col("date") >= pl.date(2026, 1, 1))
.select("date", "region", "units", "unit_price")
.with_columns(
(pl.col("units") * pl.col("unit_price")).alias("revenue")
)
.group_by("region")
.agg(pl.col("revenue").sum().alias("revenue"))
.sort("revenue", descending=True)
)
print(report.explain())
result = report.collect()
result.write_parquet("output/revenue_by_region.parquet")
El directorio de salida debe existir. explain() permite revisar el plan sin ejecutar todo el pipeline. Evita collect() después de cada paso: materializa resultados, interrumpe oportunidades de optimización y eleva la presión de memoria. Recolecta en la frontera donde el resultado deba consumirse o escribirse.
CSV no ofrece todas las posibilidades de poda de Parquet, aunque la interfaz lazy todavía organiza el plan. Elige formatos según interoperabilidad, tipos, compresión y patrón de acceso. La guía sobre archivos CSV y JSON con Python contextualiza los formatos de texto.
Polars frente a Pandas sin una falsa competición
Pandas tiene un ecosistema maduro, abundante conocimiento compartido e integración directa con muchas bibliotecas. Polars ofrece expresiones, paralelismo interno y un planificador lazy que pueden resultar ventajosos en pipelines analíticos. Nada de ello significa que cada operación será más rápida o que toda migración compensará su coste.
Compara resultados equivalentes: mismos tipos, política de nulos, ordenación, entradas y salidas. Decide si el reloj incluye lectura, conversiones y escritura. Calienta caches si representa producción, repite ejecuciones y observa memoria, no solo el menor tiempo. Registra versiones, hardware y tamaño. Un microbenchmark de suma no representa un ETL con joins y archivos.
Si Pandas ya soporta el volumen, el equipo lo domina y otras dependencias esperan sus objetos, permanecer en él puede ser una decisión responsable. Consulta la guía de Pandas para comparar las APIs reales. Si el perfil detecta un pipeline columnar de archivos como cuello de botella, prueba una etapa aislada con Polars e incluye el coste de conversión en cada frontera.
Joins, ordenación y resultados reproducibles
Como Polars no posee un índice implícito al estilo Pandas, conserva las claves en columnas. Antes de un join, verifica tipo, unicidad, política de nulos y cardinalidad esperada. Las claves duplicadas pueden multiplicar filas legítimamente, pero también descubrir un defecto de modelado. Renombra campos ambiguos y selecciona solo lo requerido.
No presupongas un orden que el plan no solicita. Después de agrupaciones y joins, aplica sort cuando las pruebas o la presentación exijan salida estable. Define cómo participan los nulos en agregaciones. Son reglas de negocio, no detalles menores de la biblioteca.
Buenas prácticas y errores comunes
- Prefiere expresiones nativas a bucles,
iter_rowsy UDF de Python. - Usa LazyFrame para pipelines de lectura, transformación y escritura.
- Ejecuta
collectuna vez, cerca de la frontera de consumo. - Controla schemas y normaliza claves antes de joins.
- Evita conversiones repetidas entre Polars, Pandas y NumPy.
- No supongas que streaming admite cualquier operación; comprueba plan y versión.
- No compares rendimiento con datos, tipos o salidas diferentes.
- Prueba tablas pequeñas con nulos, duplicados, fechas límite y categorías desconocidas.
Checklist de adopción
- Perfila el cuello de botella y establece una salida correcta de referencia.
- Crea un prototipo representativo que incluya I/O y conversiones.
- Elige eager para interacción simple y lazy para un plan optimizable.
- Define schemas, reglas de nulos, claves, ordenación y redondeo.
- Inspecciona
explain()y elimina materializaciones intermedias innecesarias. - Mide tiempo y pico de memoria en un entorno similar a producción.
- Verifica compatibilidad con visualización, machine learning y almacenamiento.
- Cubre transformaciones con pruebas mediante la guía de pytest.
Polars es una herramienta sólida de análisis y transformación, no una obligación universal. Una adopción exitosa empieza con semántica correcta, un pipeline legible y medición reproducible; la elección de biblioteca viene después de esa evidencia.