Como usar explode no PySpark: expandir arrays em linhas
Muitas fontes de dados guardam vários valores dentro de uma única célula, na forma de um array — por exemplo, a lista de produtos de cada encomenda ou as etiquetas de um artigo. Enquanto os dados estiverem "presos" dentro do array, é difícil contá-los, filtrá-los ou juntá-los a outras tabelas. No PySpark, a função explode transforma cada elemento do array na sua própria linha, sendo uma das ferramentas mais úteis para "achatar" estruturas aninhadas antes de agregar ou filtrar. É uma operação muito comum em dados vindos de APIs ou de ficheiros JSON, onde os campos repetidos aparecem quase sempre como listas dentro de uma coluna.
Pré-requisitos
- Um ambiente com PySpark instalado (Databricks, Microsoft Fabric ou Spark local).
- Uma
SparkSessionativa (nos notebooks já existe comospark). - Conhecimentos básicos de DataFrames: criar, ver com
show()e selecionar colunas.
Passo 1: Criar um DataFrame com uma coluna de array
Para o exemplo, vamos criar um pequeno DataFrame onde cada linha representa uma encomenda e a coluna produtos contém uma lista de artigos. Assim conseguimos ver o efeito do explode sem depender de dados externos.
from pyspark.sql import SparkSession
from pyspark.sql.functions import explode
spark = SparkSession.builder.getOrCreate()
dados = [
(1, ["teclado", "rato", "monitor"]),
(2, ["portátil"]),
(3, []),
]
df = spark.createDataFrame(dados, ["encomenda_id", "produtos"])
df.show(truncate=False)
O resultado mostra três linhas, cada uma com um array na coluna produtos. Repara que a encomenda 3 tem um array vazio — vamos ver mais à frente porque isso é importante.
Passo 2: Aplicar explode para expandir o array
A função explode cria uma linha por cada elemento do array. Usamo-la dentro de um select (ou withColumn) para gerar uma nova coluna com os valores individuais.
from pyspark.sql.functions import explode
resultado = df.select("encomenda_id", explode("produtos").alias("produto"))
resultado.show(truncate=False)
Agora cada produto aparece na sua própria linha, mantendo o encomenda_id correspondente. A encomenda 1, que tinha três produtos, passou a ocupar três linhas. Repara que as restantes colunas do select são repetidas para cada elemento — é assim que o explode preserva o contexto de cada valor.
Passo 3: Manter as linhas com arrays vazios ou nulos
Há um detalhe importante: o explode elimina as linhas cujo array é vazio ou nulo. Foi por isso que a encomenda 3 desapareceu do resultado. Se precisas de manter essas linhas (com um valor nulo na nova coluna), usa explode_outer.
from pyspark.sql.functions import explode_outer
df.select("encomenda_id", explode_outer("produtos").alias("produto")).show(truncate=False)
Com explode_outer, a encomenda 3 mantém-se no resultado, com null na coluna produto. Esta diferença é fácil de esquecer e costuma explicar por que "faltam" registos depois de um explode.
Dica: se não tens a certeza se uma coluna pode conter nulos, usa explode_outer por defeito. É mais seguro do que descobrir mais tarde que perdeste linhas.
Passo 4: Obter também a posição de cada elemento
Às vezes queremos saber a ordem em que cada valor aparecia no array. A função posexplode devolve duas colunas: a posição (a começar em 0) e o valor.
from pyspark.sql.functions import posexplode
df.select("encomenda_id", posexplode("produtos").alias("posicao", "produto")).show(truncate=False)
Isto é útil, por exemplo, para identificar o primeiro produto de cada encomenda (posição 0) ou para reconstruir a ordem original mais tarde. Tal como o explode, também existe a variante posexplode_outer para manter as linhas vazias ou nulas.
Verificar o resultado
Para confirmar que a expansão correu bem, compara o número de linhas antes e depois. A soma dos tamanhos dos arrays deve corresponder ao total de linhas após o explode (com explode_outer, somam-se também as linhas vazias ou nulas).
print("Linhas originais:", df.count())
print("Linhas apos explode:", resultado.count())
resultado.groupBy("encomenda_id").count().show()
Se os números baterem certo e cada encomenda_id tiver o número de produtos esperado, a operação está correta.
Conclusão
Com explode, explode_outer e posexplode já consegues achatar arrays em linhas e escolher se queres manter (ou não) os registos vazios. O próximo passo natural é combinar o explode com groupBy para contar quantas vezes cada produto aparece, ou aplicar a mesma ideia a estruturas mais complexas. Já pensaste em como usarias o explode_outer para não perder encomendas sem produtos nos teus relatórios?