Como Migrar do Pandas para o Polars em Python: Guia Prático Completo

Aprenda a migrar do Pandas para o Polars em Python com exemplos de código lado a lado, benchmarks de desempenho reais e uma estratégia de migração gradual que funciona na prática.

Pandas para Polars 2026: Guia Completo Python

Por Que Migrar do Pandas para o Polars em 2026?

Se você trabalha com dados em Python, é bem provável que o Pandas faça parte da sua rotina há um bom tempo. Eu mesmo usei Pandas por anos sem questionar — até começar a lidar com datasets que faziam minha máquina travar. Em 2026, o cenário mudou bastante. O Polars, uma biblioteca DataFrame construída em Rust, entrega ganhos de desempenho de 4x a 14x em operações comuns e até 100x em datasets grandes, consumindo uma fração da memória.

Com metade das equipes de dados já testando ou adotando o Polars, a pergunta deixou de ser "devo considerar o Polars?" e passou a ser "como faço pra migrar?".

Neste guia, vamos ver passo a passo como converter suas operações mais comuns do Pandas para o Polars, com exemplos lado a lado, benchmarks reais e uma estratégia de migração que funciona na prática.

O Que É o Polars e Como Ele Se Diferencia

O Polars é uma biblioteca open-source de processamento de dados escrita em Rust, com bindings para Python. Enquanto o Pandas opera em thread única sobre arrays NumPy, o Polars foi projetado desde o início pra aproveitar todos os núcleos do processador, usando Apache Arrow como base de memória colunar.

As vantagens técnicas principais são:

  • Multi-threading automático — todas as operações são paralelizadas sem configuração extra
  • Lazy execution — o Polars monta um plano de consulta otimizado antes de executar, parecido com um motor SQL
  • Eficiência de memória — armazenamento colunar Apache Arrow consome 30% a 70% menos RAM que o Pandas
  • Streaming — processa datasets que não cabem na memória (algo que o Pandas simplesmente não faz nativamente)

O Polars requer Python 3.10+ e está sob licença MIT. A versão mais recente saiu em março de 2026, com atualizações a cada duas semanas mais ou menos.

Instalação e Configuração Inicial

Instalar o Polars é direto ao ponto:

pip install polars

Se você precisa de funcionalidades extras como leitura de Excel, conexão com bancos de dados ou operações com fuso horário, instale com os extras correspondentes:

# Instalar com todos os extras
pip install "polars[all]"

# Ou selecionar extras específicos
pip install "polars[xlsx2csv,sqlalchemy,timezone]"

Pra verificar se deu tudo certo:

import polars as pl
print(pl.__version__)
pl.show_versions()

Comparação de Sintaxe: Pandas vs Polars

Aqui vai a boa notícia: a sintaxe do Polars é bem intuitiva pra quem já conhece Pandas. Vamos ver as operações mais comuns lado a lado.

Criação de DataFrames

# Pandas
import pandas as pd
df_pd = pd.DataFrame({
    "nome": ["Ana", "Bruno", "Carlos"],
    "idade": [28, 35, 42],
    "salario": [5500.0, 7800.0, 9200.0]
})

# Polars
import polars as pl
df_pl = pl.DataFrame({
    "nome": ["Ana", "Bruno", "Carlos"],
    "idade": [28, 35, 42],
    "salario": [5500.0, 7800.0, 9200.0]
})

Praticamente idêntico, certo? A diferença principal é que o Polars não tem índice — e, honestamente, isso simplifica muita coisa.

Leitura de Arquivos CSV e Parquet

# Pandas
df_pd = pd.read_csv("dados.csv")
df_pd = pd.read_parquet("dados.parquet")

# Polars (eager - execução imediata)
df_pl = pl.read_csv("dados.csv")
df_pl = pl.read_parquet("dados.parquet")

# Polars (lazy - execução otimizada, recomendado)
df_lazy = pl.scan_csv("dados.csv")
df_lazy = pl.scan_parquet("dados.parquet")
# Executa só quando necessário:
resultado = df_lazy.collect()

O scan_* é onde a mágica acontece. Ele cria um LazyFrame que permite ao otimizador reorganizar e eliminar operações desnecessárias antes de executar qualquer coisa. Na prática, isso faz uma diferença enorme em pipelines com vários passos.

Seleção de Colunas e Filtragem

# Pandas
df_pd[["nome", "salario"]]
df_pd[df_pd["idade"] > 30]
df_pd.loc[df_pd["salario"] > 6000, "nome"]

# Polars
df_pl.select(["nome", "salario"])
df_pl.filter(pl.col("idade") > 30)
df_pl.filter(pl.col("salario") > 6000).select("nome")

No Polars, pl.col() é o bloco fundamental de tudo. Toda operação sobre colunas usa expressões baseadas nessa função, o que torna o código mais explícito e — mais importante — otimizável pelo motor interno.

GroupBy e Agregações

# Pandas
df_pd.groupby("departamento").agg(
    media_salario=("salario", "mean"),
    total_funcionarios=("nome", "count")
)

# Polars
df_pl.group_by("departamento").agg(
    pl.col("salario").mean().alias("media_salario"),
    pl.col("nome").count().alias("total_funcionarios")
)

Detalhe importante: no Polars o método é group_by (com underscore), enquanto no Pandas é groupby. Parece bobagem, mas isso pega muita gente no início. As agregações usam expressões encadeadas — mais verboso, sim, mas também mais poderoso e legível quando as coisas ficam complexas.

Joins (Junções)

# Pandas
resultado = pd.merge(df_vendas, df_clientes, on="cliente_id", how="left")

# Polars
resultado = df_vendas.join(df_clientes, on="cliente_id", how="left")

Os joins no Polars são entre 3x e 13x mais rápidos que no Pandas. Sério. Isso graças à implementação paralela de hash join.

Adição e Transformação de Colunas

# Pandas
df_pd["salario_anual"] = df_pd["salario"] * 12
df_pd["faixa_etaria"] = df_pd["idade"].apply(
    lambda x: "jovem" if x < 30 else "senior"
)

# Polars
df_pl = df_pl.with_columns(
    (pl.col("salario") * 12).alias("salario_anual"),
    pl.when(pl.col("idade") < 30)
      .then(pl.lit("jovem"))
      .otherwise(pl.lit("senior"))
      .alias("faixa_etaria")
)

No Polars, with_columns() substitui o assign() ou a atribuição direta do Pandas. E aqui vai um ponto crucial: pl.when().then().otherwise() substitui o temido apply() com lambda. O desempenho é muito superior porque é vetorizado — nada de Python puro rodando linha a linha.

Lazy Execution: O Grande Diferencial do Polars

Se eu tivesse que escolher uma única razão pra adotar o Polars, seria a lazy execution. Esse recurso não tem equivalente direto no Pandas, e ele muda completamente a forma como você pensa sobre pipelines de dados.

A ideia é simples: você descreve toda a sequência de operações que quer fazer, e o Polars otimiza o plano de execução antes de processar qualquer dado.

# Pipeline completo com lazy execution
resultado = (
    pl.scan_parquet("vendas_2026.parquet")
    .filter(pl.col("valor") > 100)
    .filter(pl.col("regiao") == "sudeste")
    .group_by("categoria")
    .agg([
        pl.col("valor").sum().alias("total_vendas"),
        pl.col("valor").mean().alias("ticket_medio"),
        pl.col("id_pedido").n_unique().alias("num_pedidos")
    ])
    .sort("total_vendas", descending=True)
    .collect()  # Executa tudo de uma vez, otimizado
)

O otimizador aplica automaticamente técnicas como:

  • Predicate pushdown — filtros são empurrados pra o mais cedo possível na leitura do arquivo
  • Projection pushdown — só as colunas que você realmente usa são carregadas do disco
  • Eliminação de operações redundantes — passos desnecessários são removidos automaticamente
  • Paralelização automática — operações independentes rodam em paralelo sem você fazer nada

Dá pra visualizar o plano de execução e entender exatamente o que o Polars vai fazer:

# Ver o plano de execução otimizado
lazy_query = (
    pl.scan_parquet("vendas_2026.parquet")
    .filter(pl.col("valor") > 100)
    .select(["categoria", "valor"])
)
print(lazy_query.explain())

Quando vi isso pela primeira vez, confesso que fiquei impressionado. É como ter um DBA otimizando suas queries automaticamente.

Benchmarks Reais: Pandas vs Polars em 2026

Números falam mais alto que promessas. Testes independentes com datasets de aproximadamente 1 GB mostram diferenças bem expressivas:

  • Leitura de CSV: Polars é 5x mais rápido e usa 87% menos memória (179 MB vs 1,4 GB no Pandas)
  • Filtragem de linhas: Polars é 4,6x mais rápido graças ao multi-threading
  • GroupBy e agregações: Polars é 2,6x a 10x mais rápido, dependendo da complexidade da operação
  • Joins: Polars é 9x a 13x mais rápido com hash joins paralelos
  • Ordenação (sort): Polars é 11,7x mais rápido — a maior diferença entre todas as operações testadas

Vale ressaltar: pra datasets pequenos (menos de 100 MB), a diferença é bem menos expressiva. E pra operações pesadas de strings e regex, o Pandas ainda pode ganhar em certos cenários. Não é tudo preto e branco.

Guia de Migração Passo a Passo

Migrar um projeto inteiro do Pandas pro Polars de uma tacada só? Péssima ideia. Siga esta estratégia gradual — funciona muito melhor na prática.

Passo 1: Identifique os Gargalos

Antes de sair migrando tudo, meça onde o Pandas é mais lento no seu pipeline. Use profiling simples pra descobrir as operações que mais consomem tempo e memória:

import time

inicio = time.perf_counter()
# Sua operação Pandas aqui
df_resultado = df.groupby("coluna").agg({"valor": "sum"})
tempo = time.perf_counter() - inicio
print(f"Tempo: {tempo:.2f}s")

Não precisa ser sofisticado. Às vezes um time.perf_counter() simples já mostra onde estão os problemas.

Passo 2: Converta as Operações Críticas Primeiro

Comece migrando as operações mais lentas. Leitura de arquivos grandes, joins e agregações costumam ser os candidatos ideais — é onde o Polars brilha mais.

Passo 3: Use Interoperabilidade Pandas ↔ Polars

O Polars facilita bastante a convivência entre as duas bibliotecas:

# Pandas → Polars
df_polars = pl.from_pandas(df_pandas)

# Polars → Pandas
df_pandas = df_polars.to_pandas()

# Conversão eficiente via Arrow (sem cópia de memória)
import pyarrow as pa
arrow_table = df_polars.to_arrow()
df_pandas = arrow_table.to_pandas()

Essa interoperabilidade é o que torna a migração gradual viável. Você não precisa reescrever tudo de uma vez — vai convertendo aos poucos.

Passo 4: Adapte o Código de Machine Learning

A maioria das bibliotecas de ML (scikit-learn, XGBoost, LightGBM) ainda espera Pandas ou NumPy como entrada. A dica aqui é: faça o processamento pesado com Polars e converta só na fronteira com essas bibliotecas.

from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier

# Processamento pesado com Polars (rápido)
df_processado = (
    pl.scan_parquet("features.parquet")
    .filter(pl.col("valido") == True)
    .select(["feature_1", "feature_2", "feature_3", "target"])
    .collect()
)

# Converte para NumPy apenas para o ML
X = df_processado.select(["feature_1", "feature_2", "feature_3"]).to_numpy()
y = df_processado.select("target").to_numpy().ravel()

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
modelo = RandomForestClassifier()
modelo.fit(X_train, y_train)

Passo 5: Substitua apply() por Expressões

O apply() é uma das maiores fontes de lentidão no Pandas. Ele executa Python puro linha a linha — basicamente anula qualquer vantagem de vetorização. No Polars, substitua por expressões nativas:

# Pandas (lento)
df_pd["desconto"] = df_pd.apply(
    lambda row: row["preco"] * 0.1 if row["vip"] else 0, axis=1
)

# Polars (rápido)
df_pl = df_pl.with_columns(
    pl.when(pl.col("vip"))
      .then(pl.col("preco") * 0.1)
      .otherwise(0)
      .alias("desconto")
)

Essa mudança sozinha já pode acelerar partes do seu pipeline em 10x ou mais.

Quando NÃO Migrar para o Polars

Nem tudo é sobre velocidade. Existem cenários onde manter o Pandas faz total sentido:

  • Datasets pequenos (menos de 100 MB) — o ganho é mínimo e não justifica reescrever código que já funciona
  • Manipulação intensiva de strings — regex complexo, parsing de texto e codificação categórica ainda são mais rápidos no Pandas em vários casos
  • Integração forte com ecossistema ML — se seu pipeline depende pesadamente de scikit-learn, a conversão constante entre formatos pode anular os ganhos
  • Equipe sem familiaridade — se todo mundo só conhece Pandas e o prazo tá apertado, migrar agora pode não ser a melhor escolha
  • Análise exploratória rápida — pra EDA em Jupyter Notebooks com datasets moderados, o Pandas continua excelente

O importante é usar a ferramenta certa pro problema certo. Não é porque o Polars é mais rápido que você precisa migrar tudo cegamente.

Pipeline Híbrido: O Melhor dos Dois Mundos

Na minha experiência, a abordagem mais prática em 2026 é o pipeline híbrido. Use Polars pros gargalos de ETL e processamento pesado, e converta pra Pandas nas fronteiras com bibliotecas de visualização e ML.

import polars as pl
import pandas as pd
import matplotlib.pyplot as plt

# ETL pesado com Polars (rápido)
df_resultado = (
    pl.scan_parquet("vendas_grande.parquet")
    .filter(pl.col("ano") == 2026)
    .group_by("mes")
    .agg(pl.col("receita").sum().alias("receita_total"))
    .sort("mes")
    .collect()
)

# Visualização com Matplotlib via Pandas
df_plot = df_resultado.to_pandas()
plt.figure(figsize=(10, 6))
plt.bar(df_plot["mes"], df_plot["receita_total"])
plt.title("Receita Mensal 2026")
plt.xlabel("Mês")
plt.ylabel("Receita (R$)")
plt.show()

Esse padrão permite que você aproveite a velocidade do Polars onde realmente importa, sem abrir mão do ecossistema gigante do Pandas. É pragmático, funciona, e evita aquela dor de cabeça de reescrever tudo de uma vez.

Perguntas Frequentes

O Polars vai substituir o Pandas completamente?

Não no curto prazo — e talvez nunca completamente. O Pandas tem mais de uma década de desenvolvimento, um ecossistema massivo e milhões de usuários. O Polars é uma alternativa poderosa pra cenários onde desempenho e escala são críticos, mas o Pandas continua sendo a escolha padrão pra análise exploratória e projetos menores. A tendência em 2026 é dominar ambos e usar cada um no contexto certo.

Preciso reescrever todo o meu código Pandas para usar Polars?

De jeito nenhum. A melhor estratégia é a migração gradual. Identifique os gargalos de desempenho no seu pipeline e converta apenas essas partes. O Polars oferece pl.from_pandas() e .to_pandas() justamente pra facilitar a convivência entre as duas bibliotecas no mesmo projeto.

O Polars funciona com Jupyter Notebooks?

Sim, funciona perfeitamente. O Polars exibe DataFrames de forma organizada no Jupyter, e a experiência é bem parecida com a do Pandas. A diferença é que operações sobre datasets grandes vão ser significativamente mais rápidas.

Qual a diferença entre Polars e PySpark?

O PySpark é feito pra computação distribuída em clusters — ideal pra datasets de dezenas ou centenas de gigabytes que não cabem numa máquina só. O Polars é otimizado pra processamento local, aproveitando ao máximo os recursos de uma única máquina. Pra datasets de até 10-50 GB, o Polars geralmente é mais rápido e muito mais simples de configurar.

Como o Polars lida com valores nulos?

O Polars usa null nativo do Apache Arrow pra representar valores ausentes, que é mais eficiente que o NaN do NumPy usado pelo Pandas. Pra tratar nulos, use fill_null(), drop_nulls() ou interpolate() — os nomes são parecidos com os do Pandas, então a adaptação é rápida.

Sobre o Autor Priya Raghavan

Priya is a senior data engineer with 11 years building analytics platforms, most recently at Stripe where she led the migration of the merchant analytics pipeline from pandas to polars (cut p95 batch latency from 42 minutes to under 6). Before Stripe she spent four years at Mode Analytics writing the query engine that powered customer dashboards, and two years at Etsy on the seller-insights team. She writes mainly about polars internals, lazy evaluation patterns, and the practical edges of moving production pandas code to polars without breaking analyst muscle memory. Her side project is a 12k-row benchmark suite comparing pandas 2.x, polars, and DuckDB across realistic e-commerce joins. Priya lives in Oakland, mentors through Women in Data, and is slowly learning to play go.