Como fazer pivot no PySpark: transformar linhas em colunas
Transformar linhas em colunas — o chamado pivot — é uma das operações mais pedidas quando se preparam dados para relatórios. Fazer pivot no PySpark resume-se a combinar três métodos (groupBy, pivot e agg), e o resultado sai pronto para alimentar um dashboard ou uma tabela de destino. Fica também o caminho inverso, para quando precisa de voltar ao formato longo.
Pré-requisitos
- Um ambiente com PySpark: um notebook em Databricks ou Microsoft Fabric, ou uma instalação local com
pip install pyspark. - Uma
SparkSessionativa. Em Databricks e no Fabric já existe uma, chamadaspark. - Python básico: listas, strings e chamadas encadeadas de métodos.
Passo 1: Criar um DataFrame de exemplo
Comece por um DataFrame pequeno com vendas por mês e por região. É o formato "longo" típico: uma linha por combinação de mês e região.
from pyspark.sql import SparkSession
from pyspark.sql import functions as F
spark = SparkSession.builder.appName("pivot-demo").getOrCreate()
dados = [
("2026-01", "Norte", 1200.0),
("2026-01", "Sul", 900.0),
("2026-02", "Norte", 1500.0),
("2026-02", "Sul", 1100.0),
("2026-03", "Norte", 1300.0),
("2026-03", "Centro", 700.0),
]
vendas = spark.createDataFrame(dados, ["mes", "regiao", "valor"])
vendas.show()
Passo 2: Fazer o pivot com groupBy, pivot e agg
Um pivot no PySpark precisa sempre de três peças: o que fica nas linhas (groupBy), a coluna cujos valores se transformam em colunas (pivot) e o que preenche cada célula (agg). A agregação não é opcional: como várias linhas podem cair na mesma célula, o Spark exige que lhe diga como as combinar.
pivot_vendas = (
vendas
.groupBy("mes")
.pivot("regiao")
.agg(F.sum("valor"))
)
pivot_vendas.show()
O resultado tem uma linha por mês e uma coluna por região: mes, Centro, Norte e Sul.
Passo 3: Indicar a lista de valores (mais rápido)
Sem lista, o Spark tem de percorrer os dados só para descobrir que regiões existem — um job extra que, num DataFrame grande, custa caro. Passar a lista evita esse trabalho e ainda fixa a ordem das colunas.
regioes = ["Norte", "Centro", "Sul"]
pivot_rapido = (
vendas
.groupBy("mes")
.pivot("regiao", regioes)
.agg(F.sum("valor"))
)
pivot_rapido.show()
Atenção: se um valor não constar na lista, as suas linhas são simplesmente ignoradas. A lista é também um filtro.
Passo 4: Tratar os nulos
Onde não havia dados (o Centro em janeiro, por exemplo), a célula fica a null. Em relatórios, quase sempre se quer um zero.
pivot_limpo = pivot_rapido.fillna(0, subset=regioes)
pivot_limpo.show()
Passo 5: Várias agregações de uma vez
Pode passar mais do que uma agregação ao agg. O Spark cria uma coluna por cada combinação de valor e agregação, com o nome no formato valor_agregacao.
pivot_multi = (
vendas
.groupBy("mes")
.pivot("regiao", regioes)
.agg(
F.sum("valor").alias("total"),
F.count("valor").alias("n")
)
)
pivot_multi.printSchema()
# mes, Norte_total, Norte_n, Centro_total, Centro_n, Sul_total, Sul_n
Passo 6: O erro mais comum — demasiados valores distintos
Se fizer pivot sobre uma coluna com muitos valores distintos (um ID de cliente, por exemplo), o Spark devolve um erro a avisar que a coluna de pivot tem mais valores distintos do que o limite permitido — controlado pela configuração spark.sql.pivotMaxValues, que por omissão é 10000. É uma proteção útil: cada valor distinto vira uma coluna, e uma tabela com milhares de colunas raramente é o que se quer.
Em vez de aumentar o limite, reduza os valores antes do pivot: fique pelo top N e agrupe o resto em "Outros".
top = [r["regiao"] for r in
vendas.groupBy("regiao")
.agg(F.sum("valor").alias("t"))
.orderBy(F.desc("t"))
.limit(2)
.collect()]
vendas_top = vendas.withColumn(
"regiao_grp",
F.when(F.col("regiao").isin(top), F.col("regiao")).otherwise(F.lit("Outros"))
)
vendas_top.groupBy("mes").pivot("regiao_grp").agg(F.sum("valor")).show()
Passo 7: Reverter o pivot (unpivot)
O caminho inverso — voltar ao formato longo — faz-se com stack numa expressão SQL, ou com o método unpivot a partir do Spark 3.4.
# Classic: works on any version
longo = pivot_limpo.selectExpr(
"mes",
"stack(3, 'Norte', Norte, 'Centro', Centro, 'Sul', Sul) as (regiao, valor)"
)
# Spark 3.4 or later
longo = pivot_limpo.unpivot(
ids=["mes"],
values=regioes,
variableColumnName="regiao",
valueColumnName="valor"
)
longo.show()
Verificar o resultado
Duas verificações rápidas dizem se o pivot ficou bem. Primeiro, o esquema deve ter exatamente uma coluna por região:
pivot_limpo.printSchema()
Segundo, e mais importante: o total não pode mudar. A soma de todas as células do resultado tem de bater certo com a soma da coluna original.
total_original = vendas.select(F.sum("valor").alias("total"))
total_pivot = pivot_limpo.select(
F.sum(F.col("Norte") + F.col("Centro") + F.col("Sul")).alias("total")
)
total_original.show()
total_pivot.show() # 6700.0
Se os totais não coincidirem, o mais provável é ter esquecido um valor na lista do pivot — lembre-se de que essa lista também filtra.
Conclusão
Com groupBy, pivot e agg já consegue passar qualquer tabela longa para o formato largo que os relatórios pedem, tratar os nulos e voltar atrás quando for preciso. O passo natural seguinte é gravar o resultado como tabela Delta e ligá-lo ao Power BI. Fica a dica: se o pivot estiver lento, comece sempre por passar a lista de valores do Passo 3 — é a otimização com melhor retorno por menos esforço. E na sua tabela, o pivot pertence mesmo ao Spark, ou ficaria melhor na camada de visualização?