(+351) 21 24 10006  ·  info@bconcepts.pt
Carnaxide, Lisboa

Como fazer pivot no PySpark: transformar linhas em colunas

João Barros 13 de July de 2026 5 min de leitura

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 SparkSession ativa. Em Databricks e no Fabric já existe uma, chamada spark.
  • 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?