Como ler e escrever Parquet no PySpark: passo a passo
Parquet é o formato de ficheiro mais usado em projetos de dados com Spark: guarda a informação por colunas, ocupa muito menos espaço do que um CSV e permite ler apenas as colunas de que precisas. Saber ler e escrever Parquet no PySpark é, por isso, um dos passos essenciais para construir pipelines rápidos e baratos num Lakehouse. Vamos fazê-lo com exemplos mínimos que podes copiar e correr já.
Pré-requisitos
- Um ambiente com PySpark: Databricks, Microsoft Fabric, Azure Synapse ou
pip install pysparkno teu computador. - Python 3.8 ou superior e Java 11 (só necessário se correres o Spark localmente).
- Noções básicas de DataFrames:
select,filtereshow. - Uma pasta onde possas escrever ficheiros (disco local ou armazenamento cloud).
Passo 1: Criar a sessão Spark e um DataFrame de exemplo
Para escrever Parquet no PySpark precisas de uma SparkSession e de dados. Em Databricks, Fabric ou Synapse a variável spark já existe — nesse caso salta as duas primeiras linhas.
from pyspark.sql import SparkSession
spark = SparkSession.builder.appName("parquet-demo").getOrCreate()
dados = [
("Ana", "Lisboa", 2025, 1200.0),
("Bruno", "Porto", 2025, 890.5),
("Carla", "Lisboa", 2026, 1540.0),
("Diogo", "Faro", 2026, 730.25),
]
vendas = spark.createDataFrame(dados, ["vendedor", "cidade", "ano", "valor"])
vendas.show()
Passo 2: Escrever o DataFrame em Parquet
A escrita faz-se com write.parquet(). Há aqui um detalhe que confunde muita gente no início: o caminho que indicas não é um ficheiro, é uma pasta. O Spark cria lá dentro vários ficheiros .parquet, um por cada partição que tem em memória, mais alguns ficheiros de controlo.
vendas.write.mode("overwrite").parquet("/tmp/vendas_parquet")
O mode define o que acontece quando a pasta já existe:
overwrite: apaga o conteúdo anterior e volta a escrever.append: acrescenta novos ficheiros aos que já lá estão.error(predefinido): falha com o erro path already exists.ignore: não escreve nada se a pasta já existir.
Passo 3: Particionar os dados com partitionBy
Se filtras quase sempre pela mesma coluna (ano, país, data), guarda os dados particionados. O Spark cria uma subpasta por valor e, na leitura, ignora as pastas que não interessam — chama-se a isto partition pruning e é o que faz as queries ficarem rápidas.
(vendas.write
.mode("overwrite")
.partitionBy("ano")
.parquet("/tmp/vendas_por_ano"))
No disco fica assim:
/tmp/vendas_por_ano/ano=2025/part-00000-....snappy.parquet
/tmp/vendas_por_ano/ano=2026/part-00000-....snappy.parquet
Dica: particiona por colunas com poucos valores distintos (dezenas ou centenas). Particionar por uma coluna com milhares de valores cria milhares de ficheiros minúsculos e torna tudo mais lento, não mais rápido.
Passo 4: Ler ficheiros Parquet
Para ler, basta apontar para a pasta. Ao contrário do CSV, não precisas de declarar o schema nem de usar inferSchema: os nomes e os tipos das colunas estão guardados dentro do próprio ficheiro Parquet.
df = spark.read.parquet("/tmp/vendas_por_ano")
df.printSchema()
# apenas as linhas de 2026: o Spark lê só a subpasta ano=2026
df_2026 = df.filter(df.ano == 2026)
df_2026.show()
Como o Parquet é colunar, também podes pedir apenas as colunas de que precisas — o Spark nem chega a ler as restantes do disco:
df.select("vendedor", "valor").show()
Passo 5: Compressão e número de ficheiros
Por omissão o Spark comprime com snappy, que é o melhor equilíbrio entre tamanho e velocidade. Se precisares de ficheiros ainda mais pequenos, usa gzip. E quando o resultado é pequeno, junta tudo num único ficheiro com coalesce(1) para evitar o problema dos small files.
(vendas
.coalesce(1)
.write
.mode("overwrite")
.option("compression", "gzip")
.parquet("/tmp/vendas_pequeno"))
Verificar o resultado
Volta a ler o que escreveste e confirma três coisas: o número de linhas, os tipos das colunas e as partições criadas.
lido = spark.read.parquet("/tmp/vendas_por_ano")
print(lido.count()) # deve devolver 4
lido.printSchema() # valor: double, ano: integer
lido.groupBy("ano").count().show() # 2 linhas por ano
Se o count() devolver 4 e o schema mostrar valor como double, está tudo bem — os tipos sobreviveram à ida ao disco, coisa que não acontece com CSV. O erro mais comum nesta fase é Unable to infer schema for Parquet: quase sempre significa que o caminho está errado ou que a pasta está vazia.
Conclusão
Com write.parquet(), partitionBy() e read.parquet() já tens o essencial para trocar os teus CSV por um formato que poupa espaço e tempo de leitura. O passo seguinte natural é evoluir para tabelas Delta Lake, que acrescentam transações ACID, histórico de versões e MERGE por cima destes mesmos ficheiros Parquet. Antes disso, faz uma experiência: escreve o mesmo DataFrame em CSV e em Parquet e compara o tamanho das pastas — qual foi a diferença no teu caso?