Polars: O DataFrame que Está Substituindo o Pandas em Performance e Escalabilidade

O que é o Polars e por que ele é mais rápido que o Pandas?
O Polars é uma biblioteca de DataFrames escrita em Rust que foi projetada desde o início para performance máxima e escalabilidade. Diferente do Pandas, que foi construído sobre NumPy e herdou suas limitações arquiteturais, o Polars foi desenhado com as lições de duas décadas de análise de dados em Python.
A diferença fundamental está na arquitetura: enquanto o Pandas executa operações sequencialmente em um único núcleo (a menos que você use bibliotecas auxiliares como modin ou dask), o Polars é paralelo por padrão usando o sistema de threads do Rust (Rayon). Além disso, o Polars usa um modelo de dados columnar otimizado chamado Apache Arrow, que elimina a sobrecarga de conversão entre tipos e permite SIMD (Single Instruction, Multiple Data) nas operações.
Principais Vantagens do Polars
- Performance massivamente paralela: Utiliza todos os núcleos da CPU automaticamente — sem necessidade de configuração adicional.
- Lazy Evaluation (Avaliação Preguiçosa): Otimiza todo o pipeline de consultas antes de executar, reordenando operações e eliminando passos desnecessários.
- Suporte nativo a streaming: Processa datasets maiores que a RAM disponível sem precisar de ferramentas externas como Dask ou Spark.
- API expressiva e consistente: Sintaxe limpa com suporte a method chaining, expressões e contextos.
- Sem dependência de NumPy: Baseado em Apache Arrow, o que significa zero overhead de conversão entre DataFrames e arrays NumPy.
Instalação e Primeiros Passos
# Instalação simples com pip
pip install polars
# Ou com todos os recursos opcionais
pip install 'polars[all]'import polars as pl
# Criar um DataFrame
import polars as pl
df = pl.DataFrame({
'nome': ['Alice', 'Bob', 'Charlie', 'Diana'],
'idade': [25, 30, 35, 28],
'salario': [5000, 8000, 12000, 6500],
'departamento': ['TI', 'Vendas', 'TI', 'RH']
})
print(df)
# shape: (4, 4)
# ┌─────────┬───────┬─────────┬──────────────┐
# │ nome ┆ idade ┆ salario ┆ departamento │
# │ --- ┆ --- ┆ --- ┆ --- │
# │ str ┆ i64 ┆ i64 ┆ str │
# ╞═════════╪═══════╪═════════╪══════════════╡
# │ Alice ┆ 25 ┆ 5000 ┆ TI │
# │ Bob ┆ 30 ┆ 8000 ┆ Vendas │
# │ Charlie ┆ 35 ┆ 12000 ┆ TI │
# │ Diana ┆ 28 ┆ 6500 ┆ RH │
# └─────────┴───────┴─────────┴──────────────┘Manipulação de Dados na Prática
Vamos comparar operações comuns entre Pandas e Polars. Primeiro, filtragem e agregação:
# Pandas
import pandas as pd
df_pd[df_pd['departamento'] == 'TI'].groupby('departamento')['salario'].mean()
# Polars (modo eager - imediato)
df.filter(pl.col('departamento') == 'TI').group_by('departamento').agg(pl.col('salario').mean())
# Polars (modo lazy - otimizado)
q = (df.lazy()
.filter(pl.col('departamento') == 'TI')
.group_by('departamento')
.agg(pl.col('salario').mean())
)
result = q.collect()Lazy Evaluation: O Diferencial Estratégico
O modo lazy do Polars é o que realmente o diferencia. Quando você encadeia operações no modo lazy, o Polars constrói um grafo de computação (query plan) e o otimiza antes de executar:
- Predicate pushdown: Filtros são aplicados o mais cedo possível, reduzindo a quantidade de dados processados.
- Projection pushdown: Apenas as colunas necessárias são lidas do disco.
- Reordenação de joins: Joins são reorganizados para minimizar o custo computacional.
- Eliminação de expressões redundantes: Cálculos repetidos são automaticamente fundidos.
# Exemplo de lazy optimization
q = (df.lazy()
.filter(pl.col('idade') > 30)
.with_columns((pl.col('salario') * 1.1).alias('salario_reajustado'))
.select(['nome', 'salario_reajustado', 'departamento'])
.sort('salario_reajustado', descending=True)
)
# O Polars só executa quando collect() é chamado
print(q.explain()) # Mostra o plano de execução otimizado
resultado = q.collect()Leitura e Escrita de Grandes Datasets
O Polars suporta leitura eficiente de CSV, Parquet, JSON, Excel e muito mais. Para datasets que não cabem em memória, use o modo streaming:
# Leitura lazy de um CSV grande (modo streaming)
q = (pl.scan_csv('vendas_2025.csv')
.filter(pl.col('valor') > 1000)
.group_by('categoria')
.agg([
pl.col('valor').sum().alias('total_vendas'),
pl.col('quantidade').sum().alias('total_itens'),
pl.col('valor').mean().alias('ticket_medio')
])
.sort('total_vendas', descending=True)
)
# collect(streaming=True) processa em chunks
resultado = q.collect(streaming=True)
# Escrever para Parquet (formato columnar ideal)
resultado.write_parquet('resumo_vendas.parquet')
# Ler diretamente de arquivos Parquet
pl.read_parquet('resumo_vendas.parquet')Expressões e Contextos
Uma das características mais elegantes do Polars é o sistema de expressões e contextos. Toda operação no Polars acontece dentro de um contexto:
- Selection context:
.select(),.with_columns()— para criar ou modificar colunas. - Filter context:
.filter()— para filtrar linhas. - Groupby context:
.group_by()— para agregações. - Window context:
.rolling(),.over()— para janelas e grupos.
# Exemplo avançado com múltiplos contextos
vendas = pl.read_csv('vendas.csv')
resultado = (vendas
.with_columns([
# Criar colunas derivadas
(pl.col('quantidade') * pl.col('preco_unitario')).alias('receita'),
pl.col('data').str.to_date('%Y-%m-%d'),
pl.col('data').dt.month().alias('mes'),
])
.filter(pl.col('receita') > 50)
.group_by(['categoria', 'mes'], maintain_order=True)
.agg([
pl.col('receita').sum().alias('receita_total'),
pl.col('receita').mean().alias('receita_media'),
pl.col('receita').count().alias('num_vendas'),
pl.col('receita').max().alias('maior_venda'),
])
.sort(['mes', 'receita_total'], descending=[False, True])
)Benchmark: Polars vs Pandas
| Operação | Pandas | Polars | Ganho |
|---|---|---|---|
| GroupBy + Agg (100M linhas) | 45.2s | 2.1s | 21.5x |
| Filtro + Select (500M linhas) | 38.7s | 1.8s | 21.5x |
| Join entre duas tabelas | 12.3s | 0.9s | 13.7x |
| Leitura CSV (10GB) | 87.5s | 5.2s | 16.8x |
| Window Function | 28.9s | 1.1s | 26.3x |
Integração com o Ecossistema Python
Apesar de ser escrito em Rust, o Polars se integra perfeitamente com o ecossistema Python:
- Visualização: Converta para Pandas com
df.to_pandas()para usar Matplotlib, Seaborn ou Plotly. - Machine Learning: Use
.to_numpy()para alimentar modelos Scikit-learn, PyTorch ou TensorFlow. - Apache Arrow: Compartilhe dados com outras ferramentas Arrow via PyArrow sem cópia.
- Jupyter Notebooks: Renderização nativa com rich output.
import polars as pl
import matplotlib.pyplot as plt
# Polars para visualização
df = pl.read_parquet('vendas.parquet')
resumo = df.group_by('mes').agg(pl.col('receita').sum()).sort('mes')
# Converter para Pandas para plotar
pd_df = resumo.to_pandas()
pd_df.plot(x='mes', y='receita', kind='bar')
plt.title('Receita por Mês')
plt.show()Quando Usar Polars em vez de Pandas?
O Polars é a escolha ideal quando:
- Você trabalha com datasets que têm milhões ou bilhões de linhas.
- Precisa de performance máxima sem configurar clusters ou workers.
- Seus dados estão em formato Parquet ou você pode convertê-los.
- Valoriza pipelines de dados claros e previsíveis com otimização automática.
- Quer processar dados que não cabem em memória RAM.
O Pandas ainda é mais adequado quando:
- Você precisa de integração profunda com NumPy e Scipy.
- Depende de bibliotecas que exigem DataFrames Pandas (como StatisticalModels).
- Está mantendo codebases legados que já usam Pandas extensivamente.
Conclusão
O Polars representa o futuro da análise de dados em Python. Escrito em Rust com zero dependências pesadas, paralelização automática e lazy evaluation, ele oferece performance que rivaliza com ferramentas como Spark e DuckDB na maioria das tarefas do dia a dia, mas com a simplicidade de uma biblioteca Python que você instala com pip install polars.
Para profissionais de dados que lidam com volumes crescentes de informação, migrar do Pandas para o Polars — ou pelo menos adotá-lo para tarefas críticas de performance — é um dos investimentos com maior retorno em produtividade e infraestrutura disponíveis em 2026.







