Pular para o conteúdo

Parquet

O formato colunar, escrito e lido em DataForge — sem pyarrow, e interoperável com ele.

Parquet é o formato em que os dados de verdade vivem. Arcane.Lago o escreve e o lê em Python puro: um arquivo gerado aqui é lido pelo pandas, pelo DuckDB e pelo Spark, e um arquivo gerado por eles é lido aqui.

dataforge
adopt Arcane.Lago as L

L.gravar_parquet("vendas.parquet", linhas)
L.ler_parquet("vendas.parquet")
L.ler_parquet("vendas.parquet", ["id", "valor"])   // só duas colunas
L.esquema_parquet("vendas.parquet")                // sem ler os dados

Por que colunar#

Um CSV guarda linha a linha. Para somar uma coluna de 40 milhões de linhas, o disco entrega as outras trinta junto — e elas são jogadas fora. O Parquet guarda coluna a coluna: ler uma coluna lê só ela.

dataforge
// 50.000 linhas, 4 colunas
L.ler_parquet("grande.parquet")            // 0,05 s
L.ler_parquet("grande.parquet", ["id"])    // 0,02 s  2,9× mais rápido

E valores do mesmo tipo, lado a lado, comprimem muito melhor. Uma coluna de datas repetidas ou de categorias encolhe uma ordem de grandeza; a mesma informação espalhada por linhas, não.

O formato, por dentro#

text
PAR1                       ← marca de abertura
[dados da coluna 1]        ← páginas, uma por bloco de valores
[dados da coluna 2]
…
[metadados em Thrift]      ← esquema, onde cada coluna começa
<4 bytes: tamanho deles>
PAR1                       ← marca de fechamento

Os metadados ficam no fim, e não no começo. É o que permite escrever um arquivo sem saber de antemão quantas linhas ele terá — e é por isso que o leitor busca o fim primeiro.

Tipos#

DataForgeParquetLido como
IntegerINT64int64
NumberDOUBLEdouble
StringBYTE_ARRAY (UTF8)string
BooleanBOOLEANbool
voidnível de definiçãonull

O tipo sai dos valores, e um inteiro grande demais para 64 bits sobe para DOUBLE. Misturar tipos numa coluna é o que mais quebra na leitura — o Parquet é tipado, e o CSV que o originou não era.

Nulos custam quando existem#

Uma coluna sem nulo nenhum é declarada REQUIRED, e não carrega níveis de definição. A que tem nulo é OPTIONAL, e paga por isso. Declarar tudo como opcional custaria os níveis à toa em toda coluna.

Dicionário#

A maioria dos Parquet do mundo é escrita com codificação por dicionário: a página guarda índices, e os valores distintos ficam numa página à parte. Uma coluna de cinco categorias vira 3 bits por linha.

Nós escrevemos PLAIN e lemos os dois — ler só PLAIN leria quase nada do que existe por aí.

O que ainda não lemos#

CompressãoEstado
sem compressãolê e escreve
gziplê e escreve (o padrão)
snappy, zstd, brotli, lz4recusado, com o nome da compressão na mensagem

Gzip está na biblioteca padrão do Python; as outras exigiriam dependência, e a linguagem não tem nenhuma. Quando o arquivo vem comprimido de outro jeito, a mensagem diz qual e como regravar.