Como tratar valores nulos no PySpark: passo a passo
Os valores nulos (NULL) aparecem em quase todos os conjuntos de dados reais: um campo que ficou por preencher, uma junção sem correspondência ou um sensor que falhou. Se não forem tratados, distorcem médias, produzem contagens enganadoras e chegam a provocar erros em modelos de machine learning. Saber tratar valores nulos no PySpark à escala é, por isso, uma competência essencial, e o Spark oferece três abordagens simples e rápidas: remover, preencher ou substituir por um valor alternativo.
Pré-requisitos
- Python 3.8 ou superior instalado.
- PySpark instalado com
pip install pysparke Java 8, 11 ou 17 no sistema. - Noções básicas de DataFrames no PySpark (como ler dados e listar colunas).
Passo 1: Criar uma SparkSession e um DataFrame de exemplo
Para experimentar cada técnica de forma segura, começamos por criar uma SparkSession e um pequeno DataFrame com alguns None — que o Spark converte automaticamente em NULL. Ter dados de exemplo controlados torna muito mais fácil perceber o efeito de cada função.
from pyspark.sql import SparkSession
spark = SparkSession.builder.appName("valores-nulos").getOrCreate()
dados = [
("Ana", "Porto", 30),
("Bruno", None, None),
(None, "Lisboa", 25),
("Rita", None, 41),
]
df = spark.createDataFrame(dados, ["nome", "cidade", "idade"])
df.show()
Passo 2: Contar valores nulos por coluna
Antes de decidir o que fazer, é boa prática medir o problema. A expressão seguinte percorre cada coluna e conta quantas linhas têm valor nulo, dando-te um retrato imediato da qualidade dos dados.
from pyspark.sql.functions import col, count, when
df.select([
count(when(col(c).isNull(), c)).alias(c)
for c in df.columns
]).show()
O resultado indica o número de nulos em nome, cidade e idade. Com estes números fica claro que coluna precisa de mais atenção e qual a estratégia mais adequada.
Passo 3: Remover linhas com na.drop
Quando uma linha sem dados não acrescenta valor à análise, o mais simples é removê-la com na.drop() (também disponível com o nome dropna()). O parâmetro how decide se apagamos linhas com qualquer nulo ou apenas as que estão totalmente vazias.
# Remove linhas com QUALQUER valor nulo
df.na.drop(how="any").show()
# Remove apenas linhas totalmente vazias
df.na.drop(how="all").show()
# Mantem linhas com pelo menos 2 valores preenchidos
df.na.drop(thresh=2).show()
# Verifica os nulos so em colunas especificas
df.na.drop(subset=["cidade"]).show()
Por defeito, na.drop() usa how="any", ou seja, basta um nulo para a linha ser removida. Usa subset quando só te interessam as colunas críticas.
Passo 4: Preencher nulos com na.fill
Muitas vezes queremos manter todas as linhas e apenas substituir os nulos por um valor por defeito. É isso que faz na.fill() (ou fillna()). Com um dicionário podes indicar um valor diferente para cada coluna.
# Preenche as colunas de texto nulas com "Desconhecido"
df.na.fill("Desconhecido").show()
# Um valor diferente por coluna
df.na.fill({"cidade": "Sem cidade", "idade": 0}).show()
Atenção a um erro comum: o Spark só preenche colunas cujo tipo seja compatível com o valor indicado. Um texto não preenche colunas numéricas nem o contrário, por isso o dicionário por coluna é a forma mais segura de evitar surpresas.
Passo 5: Substituir nulos por outra coluna com coalesce
A função coalesce devolve o primeiro valor não nulo de uma lista de colunas ou expressões. É ideal para criar um valor de recurso: se a primeira opção estiver vazia, usa a seguinte.
from pyspark.sql.functions import coalesce, lit
df.withColumn(
"cidade_final",
coalesce(col("cidade"), lit("N/D"))
).show()
Sempre que cidade for nula, a nova coluna cidade_final fica com "N/D"; caso contrário mantém o valor original. Também podes encadear várias colunas, como coalesce(col("telemovel"), col("fixo"), lit("sem contacto")).
Verificar o resultado
Para confirmar que a limpeza correu bem, volta a correr a contagem do Passo 2 sobre o DataFrame já tratado: se todas as colunas mostrarem 0, não restam nulos. Em alternativa, testa uma coluna específica com df.filter(col("idade").isNull()).count() e confirma que devolve 0.
Conclusão
Com na.drop, na.fill e coalesce ficas com as três abordagens fundamentais para tratar valores nulos no PySpark: remover as linhas, preencher com um valor por defeito ou substituir por uma coluna alternativa. O passo seguinte é escolher a estratégia com base no negócio — por exemplo, preencher a idade com a mediana em vez de zero, para não enviesar as estatísticas. Qual destas três técnicas faz mais sentido para o teu conjunto de dados?