I/O CSV/Parquet
Leia e escreva CSV e Parquet em Rust com esquemas previsíveis, compressão e desempenho de varredura.
Receita
Cartão de receita de referência rápida - pronto para copiar e colar.
use polars::prelude::*;
fn csv_to_parquet(csv_path: &str, parquet_path: &str) -> PolarsResult<()> {
let mut df = CsvReadOptions::default()
.with_has_header(true)
.try_into_reader_with_file_path(Some(csv_path.into()))?
.finish()?;
ParquetWriter::new(std::fs::File::create(parquet_path)?)
.with_compression(ParquetCompression::Zstd(None))
.finish(&mut df)?;
Ok(())
}Quando usar isso:
- ETL de zona de pouso a partir de exportações CSV de fornecedores
- Publicação de conjuntos de dados analíticos para consumidores DuckDB, Spark ou Polars
- Redução de armazenamento e egress com compressão colunar
- Validação de esquemas na ingestão antes do SQL downstream
Exemplo de Trabalho
use polars::prelude::*;
fn main() -> PolarsResult<()> {
let schema = Schema::from_iter([
Field::new("order_id".into(), DataType::Int64),
Field::new("region".into(), DataType::String),
Field::new("amount".into(), DataType::Float64),
]);
let df = CsvReadOptions::default()
.with_has_header(true)
.with_schema(Some(Arc::new(schema)))
.try_into_reader_with_file_path(Some("orders.csv".into()))?
.finish()?;
let filtered = df
.lazy()
.filter(col("amount").gt(lit(0.0)))
.collect()?;
let mut out = filtered.clone();
ParquetWriter::new(std::fs::File::create("orders_clean.parquet")?)
.with_row_group_size(Some(128_000))
.finish(&mut out)?;
Ok(())
}O que isso demonstra:
- Fornecimento de um esquema CSV explícito em vez de inferir tipos
- Filtragem de linhas inválidas antes de gravar Parquet
- Definição do tamanho do grupo de linhas para predicate pushdown posterior
Mergulho Profundo
Como Funciona
- CSV é texto de linha - os parsers devem inferir ou aceitar dtypes por coluna.
- Parquet armazena blocos de colunas tipados com estatísticas por grupo de linhas (mín/máx).
- Leitores pulam grupos de linhas quando predicados contradizem estatísticas.
- Gravadores escolhem compressão (Snappy, ZSTD, Gzip) por codificação de coluna.
Comparação de Formatos
| Formato | Força | Cuidado com |
|---|---|---|
| CSV | Exportação universal | Sem esquema embutido; custo de parsing |
| Parquet | Análise, compressão | Não legível por humanos; disciplina de evolução de esquema |
Notas de Rust
// Varredura Lazy evita carregar CSV completo:
LazyCsvReader::new("big.csv".into()).with_has_header(true).finish()?;
// Ler subconjunto de colunas Parquet:
LazyFrame::scan_parquet("big.parquet", ScanArgsParquet::default())?
.select([col("user_id"), col("event_time")]);Armadilhas
- Decimais CSV específicos de localidade -
1,5vs1.5quebram a inferência. Correção: normalize arquivos ou defina o separador decimal nas opções. - BOM UTF-8 em cabeçalhos - o primeiro nome de coluna inclui BOM invisível. Correção: remova o BOM ou use pré-processamento
utf8-lossy. - Grupos de linhas enormes - pushdown ruim e picos de memória na leitura. Correção: mire 128K-1M de linhas por grupo, dependendo da largura.
- Gravação de Utf8 para tudo - Parquet ainda armazena strings, mas perde estatísticas numéricas. Correção: converta antes de gravar.
- Anexar Parquet concatenando arquivos - grupos de linhas duplicados e conflitos de esquema. Correção: grave diretórios de conjuntos de dados particionados com esquema consistente.
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| JSON Lines | Logs de eventos semiestruturados | Agregação numérica pesada |
| Arrow IPC | Transferência zero-copy intraprocesso | Arquivamento de longo prazo |
| Avro | Evolução de esquema em pipelines Kafka | Consultas interativas de BI |
| SQLite | Pequenas fatias transacionais | Varreduras colunares de escala TB |
FAQs
Qual compressão Parquet devo usar?
ZSTD equilibra taxa e velocidade para análise. Snappy é mais rápido, arquivos maiores. Faça benchmark em seu hardware e padrões de leitura.
Como lidar com linhas CSV ruins?
Use with_ignore_errors com cautela, registre rejeições em um arquivo de "dead-letter" e conte as linhas descartadas nas métricas.
Posso ler apenas algumas colunas Parquet?
Sim - scan_parquet lazy com projeção de coluna evita a decodificação de campos não utilizados.
Como particionar a saída Parquet?
Grave diretórios no estilo Hive region=west/data.parquet. Consumidores filtram caminhos por chaves de partição antes da varredura.
O Polars preserva o fuso horário no Parquet?
Armazene timestamps como UTC com notas de metadados. Converta na leitura para fusos horários de exibição.
Qual o tamanho máximo de um único CSV?
Use leitores em chunks além do tamanho da RAM - veja Streaming & Large Data.
Devo usar gzip em CSV para arquivo?
CSV com Gzip é bom para arquivo; prefira Parquet para qualquer coisa consultada mais de uma vez.
Como validar o esquema na gravação?
Afirme os dtypes no DataFrame antes de ParquetWriter::finish e rejeite gravações que se desviam de um registro de esquema versionado.
O Python pode ler Parquet escrito em Rust?
Sim - Parquet é multilíngue. Mantenha tipos lógicos compatíveis com Arrow para uma transição suave.
E quanto ao Excel XLSX?
Converta para CSV em uma etapa de pré-processamento ou use um crate dedicado para XLSX - não ideal para pipelines de análise de produção.
Relacionados
- Polars - API de I/O principal nos exemplos
- Streaming & Large Data - CSV em chunks
- Apache Arrow - formato de destino em memória
- Building Data CLIs & Services - wrappers CLI
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+.