Polars
A biblioteca DataFrame rápida para Rust - otimização de consulta lazy, execução vetorizada e layout de memória com base em Arrow.
Receita
Cartão de receita de referência rápida - pronto para copiar e colar.
use polars::prelude::*;
fn top_spenders(path: &str) -> PolarsResult<DataFrame> {
LazyCsvReader::new(path.into())
.with_has_header(true)
.finish()?
.group_by([col("customer_id")])
.agg([col("amount").sum().alias("total")])
.sort(["total"], SortMultipleOptions::default())
.limit(10)
.collect()
}Quando usar isso:
- Substituindo scripts pandas que gargalam em loops Python single-threaded
- ETL onde você precisa de pushdown de predicado lazy em CSV ou Parquet
- Análises in-process sem configurar Spark ou DuckDB como um serviço
- Serviços Rust que já usam Arrow e precisam de uma API DataFrame
Exemplo de Trabalho
use polars::prelude::*;
fn main() -> PolarsResult<()> {
let df = df! {
"region" => ["west", "east", "west", "north"],
"amount" => [120i64, 80, 200, 50],
}?;
let summary = df
.lazy()
.filter(col("amount").gt(lit(60)))
.group_by([col("region")])
.agg([
col("amount").sum().alias("total"),
col("amount").mean().alias("avg"),
])
.sort(["total"], SortMultipleOptions::default())
.collect()?;
println!("{}", summary);
Ok(())
}O que isso demonstra:
- Construindo um pequeno frame em memória para testes e protótipos
- Mudando para o modo lazy com
.lazy()para execução otimizada - Agregações group-by com múltiplas expressões em um único plano
Mergulho Profundo
Como Funciona
- Polars armazena colunas em buffers compatíveis com Arrow para varreduras amigáveis ao cache.
- A API Lazy constrói um plano lógico; o otimizador reordena filtros e projeções.
collect()executa o plano - esse é o limite do seu pico de memória e CPU.- Métodos Eager
DataFramerodam imediatamente - modelo mental mais simples, menos otimizações.
Lazy vs Eager
| Modo | API | Melhor para |
|---|---|---|
| Eager | DataFrame::filter, group_by | Testes unitários, tabelas minúsculas |
| Lazy | LazyFrame + collect() | Arquivos grandes, pipelines multi-etapas |
Notas de Rust
// Prefira aliases explícitos em agg - expressões sem nome são difíceis de identificar em planos explain.
.col("amount").sum().alias("total")
// Use .explain() em planos lazy durante o desenvolvimento.
println!("{}", lf.explain(true)?);Armadilhas
- Coletando muito cedo - chamar
collect()após cada etapa anula a otimização lazy. Correção: encadeie transformações, colete uma vez no final. - Colunas numéricas com tipo string - a inferência de CSV padrão é Utf8. Correção: passe
with_dtypesou faça o cast após o carregamento. - Discrepância de dtype da chave de join - chaves
i32vsi64silenciosamente descartam linhas. Correção: faça ocastde ambos os lados para o mesmo tipo antes do join. - Assumindo semântica de índice pandas - Polars não tem índice oculto; joins usam chaves explícitas. Correção: adicione uma coluna
idquando precisar de identidade de linha estável. - OOM em Parquet largo -
select *em 500 colunas carrega tudo. Correção: projete apenas as colunas necessárias na varredura lazy.
Alternativas
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
| DataFusion SQL | Equipe pensa em SQL sobre tabelas registradas | Você quer um DSL de expressão Rust no código da aplicação |
| pandas (Python) | Exploração de notebook com um ecossistema enorme | ETL de produção que necessita de deploy de binário único |
| DuckDB embarcado | Análises SQL-first em arquivos | Você está padronizando dependências puras de Rust |
| ndarray + loops manuais | Pequenos kernels numéricos | Transformações relacionais e pipelines intensivos em I/O |
FAQs
Devo usar lazy ou eager por padrão?
Use lazy por padrão para pipelines baseados em arquivos. Use eager para testes, exploração estilo REPL e frames que já cabem na memória.
Como depuro um plano lento?
Chame .explain(true) no LazyFrame antes de collect() para ver o pushdown de predicado e a poda de projeção.
O Polars usa múltiplos threads?
Sim. Muitas operações paralelizam entre colunas e grupos de linhas. Controle a contagem de threads com POLARS_MAX_THREADS ao colocar serviços em co-localização.
O Polars pode ler armazenamento de objetos na nuvem?
Use scan_parquet / scan_csv com caminhos compatíveis e flags de recursos para backends de nuvem, ou prepare arquivos localmente para operações mais simples.
Como lidar com nulos?
Use expressões fill_null, drop_nulls ou coalesce. dtypes anuláveis devem corresponder às expectativas do esquema na escrita.
O que mudou no Polars 0.46+?
Mantenha-se na API de expressões (col, lit, when) documentada para 0.46+. Padrões mais antigos de lazy::dsl podem diferir - fixe versões em CI.
Como exporto para Python?
Converta para Arrow RecordBatch e passe via PyArrow - veja Interoperabilidade com Python/pandas.
O Polars é bom para streaming?
Para memória limitada, combine leituras de CSV em chunks ou escaneie grupos de linhas Parquet - veja Streaming e Dados Grandes.
Como persisto resultados?
Use ParquetWriter ou CsvWriter em frames eager após o collect() final.
Posso misturar Rust e SQL?
Registre a saída do Polars como tabelas Arrow no DataFusion para camadas SQL - veja DataFusion.
Relacionados
- Noções Básicas de Dados - orientação da seção
- Apache Arrow - memória colunar sob Polars
- I/O de CSV/Parquet - ajuste de I/O
- Streaming e Dados Grandes - processamento em chunks
Versões da Stack: Esta página foi escrita para Rust 1.97.0 (edição 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, e Polars 0.46+.