Pandas: Tratamento de Valores Ausentes
Última atualização: 2026-08-26
Valores ausentes (NaN) são o inimigo número um da limpeza de dados — dados do mundo real nunca são perfeitos. Uma falha de sensor deixa leituras de temperatura faltando, um usuário pula um campo opcional e a idade vai embora, uma fusão de sistemas apaga IDs. O Pandas fornece uma ferramenta completa para detectar, preencher, remover e interpolar valores ausentes. Esta seção orienta você em cada um desses processos e explica como operar com segurança sob o modelo de Cópia em Gravação (Copy-on-Write).
1. O que você aprenderá
- ❶ A natureza e semântica do NaN
- ❷ Detecção com isnull / notnull
- ❸ Remoção de linhas/colunas faltantes com dropna
- ❹ Preenchimento de valores ausentes com fillna
- ❺ Métodos de interpolação com interpolate
2. A Lacuna nos Dados de Treino da Carol
(1) O Problema: 30% dos Dados de Frequência Cardíaca Estão Faltando
O monitor de condicionamento da Carol registrou 30 dias de dados de frequência cardíaca, mas o sinal caiu (NaN) em 9 desses dias durante seus treinos:
import pandas as pd
import numpy as np
hr = pd.DataFrame({
'date': pd.date_range('2024-01-01', periods=30),
'heart_rate': [72, 75, np.nan, 78, np.nan, 80, 82, np.nan,
85, 88, np.nan, 90, 87, np.nan, 83, 80,
np.nan, 78, 76, np.nan, 74, 72, np.nan, 70,
68, np.nan, 65, 63, np.nan, 60]
})
print(f"Missing: {hr['heart_rate'].isnull().sum()} / {len(hr)}")
# Missing: 9 / 30
> **Saída:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instale-o — instale-o localmente (`pip install pandas`) para acompanhar. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
(2) A Solução: Quatro Estratégias, Cada Uma com Seu Caso de Uso
| Estratégia | Método | Quando Usar |
|---|---|---|
| Detectar | isnull / notnull | Entender a distribuição de valores faltantes |
| Descartar | dropna | Poucos valores faltantes, linhas não são importantes |
| Preencher | fillna | Existe um valor padrão razoável |
| Interpolar | interpolate | Dados de séries temporais com tendência clara |
3. A Natureza do NaN
(1) NaN Não é Igual a NaN
▶ Exemplo: O Comportamento Especial do NaN (Dificuldade ⭐)
import numpy as np
# NaN NÃO é igual a si mesmo — isso é o padrão IEEE 754
print(np.nan == np.nan) # False!
print(np.nan != np.nan) # True!
# Use np.isnan() para detectar NaN
print(np.isnan(np.nan)) # True
# Pandas lida com isso internamente — isnull() funciona corretamente
import pandas as pd
s = pd.Series([1, np.nan, 3, None, 5])
print(s.isnull())
# 0 False
# 1 True
# 2 False
# 3 True
# 4 False
> **Saída:** Execute isto em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instale — instale-o localmente (`pip install pandas`) para acompanhar. Os valores reais podem variar ligeramente dependendo da sua versão do pandas.
(2) Tipos de Valores Ausentes no Pandas
| Tipo | Representação do Ausente | Exemplo |
|---|---|---|
| float64 | np.nan | Valor ausente padrão para ponto flutuante |
| int64 | Não pode representar NaN nativamente → converte automaticamente para float64 | [1, None, 3] → float64 |
| objeto | None / np.nan | Colunas de tipos mistos |
| string | pd.NA | String anulável do Pandas 1.x+ |
| Int64 | pd.NA | inteiro anulável (I maiúsculo) |
| datetime64 | NaT | Valor de tempo ausente |
Int64, boolean e string) usam pd.NA em vez de np.nan, resolvendo o problema de que colunas de inteiros não podem representar valores ausentes. Converta com: df['col'].astype('Int64').
4. Detecção com isnull / notnull
(1) Detecção Básica
▶ Exemplo
> **Saída:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado — instale localmente (`pip install pandas`) para acompanhar. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
: Detecção de Valores Ausentes (Dificuldade ⭐)
import pandas as pd
import numpy as np
df = pd.DataFrame({
'name': ['Alice', 'Bob', 'Charlie', 'Carol', 'David'],
'age': [28, np.nan, 25, 30, np.nan],
'salary': [75000, 92000, np.nan, 88000, 105000],
'department': ['Sales', 'Engineering', np.nan, 'Sales', 'Management']
})
# Per-element detection
print(df.isnull())
# name age salary department
# 0 False False False False
# 1 False True False False
# 2 False False True True
# 3 False False False False
# 4 False True False False
# Per-column count
print(df.isnull().sum())
# name 0
# age 2
# salary 1
# department 1
# Total missing
print(f"Total missing: {df.isnull().sum().sum()}") # 4
# Not-null (inverse)
print(df.notnull().sum()) # count of non-missing per column
> **Saída:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado — instale localmente (`pip install pandas`) para acompanhar. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
(2) Visualizando e Analisando Valores Ausentes
▶ Exemplo
> **Saída:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado — instale localmente (`pip install pandas`) para acompanhar. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
: Análise de Padrões de Ausência (Dificuldade ⭐⭐)
import pandas as pd
import numpy as np
df = pd.DataFrame({
'name': ['Alice', 'Bob', 'Charlie', 'Carol', 'David', 'Eve'],
'age': [28, np.nan, 25, 30, np.nan, 27],
'salary': [75000, 92000, np.nan, 88000, 105000, np.nan],
'department': ['Sales', 'Engineering', np.nan, 'Sales', 'Management', 'HR']
})
# Which rows have ANY missing?
rows_with_missing = df[df.isnull().any(axis=1)]
print(f"Rows with missing values: {len(rows_with_missing)}")
# 3 rows (Bob, Charlie, David)
# Which columns have missing?
cols_with_missing = df.columns[df.isnull().any()].tolist()
print(f"Columns with missing: {cols_with_missing}")
# ['age', 'salary', 'department']
# Missing percentage per column
missing_pct = (df.isnull().sum() / len(df) * 100).round(1)
print(missing_pct[missing_pct > 0])
# age 33.3%
# salary 33.3%
# department 16.7%
> **Saída:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado — instale localmente (`pip install pandas`) para acompanhar. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
5. Removendo Valores Ausentes com dropna
(1) Remoção Básica
▶ Exemplo
> **Output:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado — instale-o localmente (`pip install pandas`) para acompanhar. Os valores reais podem variar um pouco dependendo da sua versão do pandas.
: Removendo com dropna (Dificuldade ⭐)
import pandas as pd
import numpy as np
df = pd.DataFrame({
'name': ['Alice', 'Bob', 'Charlie', 'Carol', 'David'],
'age': [28, np.nan, 25, 30, np.nan],
'salary': [75000, 92000, np.nan, 88000, 105000]
})
# Remove linhas com QUALQUER valor ausente (padrão)
print(df.dropna())
# name age salary
# 0 Alice 28.0 75000.0
# 3 Carol 30.0 88000.0
# Remove linhas onde TODOS os valores são ausentes
print(df.dropna(how='all'))
# Remove colunas com qualquer valor ausente
print(df.dropna(axis=1))
# name
# 0 Alice
# 1 Bob
# ...
# Remove linhas com limite (mantém linhas com >= N valores não ausentes)
print(df.dropna(thresh=2)) # pelo menos 2 valores não-NaN
# name age salary
# 0 Alice 28.0 75000.0
# 1 Bob NaN 92000.0
# 3 Carol 30.0 88000.0
# 4 David NaN 105000.0
> **Output:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado — instale-o localmente (`pip install pandas`) para acompanhar. Os valores reais podem variar um pouco dependendo da sua versão do pandas.
(2) Parâmetros do dropna em Detalhe
| Parâmetro | Padrão | Descrição |
|---|---|---|
| axis | 0 | 0 = remove linhas, 1 = remove colunas |
| how | 'any' | 'any' = remove se qualquer NaN, 'all' = remove apenas se todos NaN |
| thresh | None | Mantém linhas com pelo menos N valores não ausentes |
| subset | None | Verifica apenas as colunas especificadas |
▶ Exemplo
> **Output:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado — instale-o localmente (`pip install pandas`) para acompanhar. Os valores reais podem variar um pouco dependendo da sua versão do pandas.
: Verificação Específica de Coluna com dropna (Dificuldade ⭐⭐)
import pandas as pd
import numpy as np
df = pd.DataFrame({
'name': ['Alice', 'Bob', 'Charlie', 'Carol', 'David'],
'age': [28, np.nan, 25, 30, np.nan],
'salary': [75000, 92000, np.nan, 88000, 105000],
'department': ['Sales', 'Engineering', 'Marketing', 'Sales', 'Management']
})
# Remove apenas linhas onde 'salary' está ausente
# (mantém linhas com 'age' ausente — podemos imputar a idade)
print(df.dropna(subset=['salary']))
# name age salary department
# 0 Alice 28.0 75000.0 Sales
# 1 Bob NaN 92000.0 Engineering
# 3 Carol 30.0 88000.0 Sales
# 4 David NaN 105000.0 Management
> **Output:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instalado — instale-o localmente (`pip install pandas`) para acompanhar. Os valores reais podem variar um pouco dependendo da sua versão do pandas.
6. Preenchendo Valores Ausentes com fillna
(1) Preenchimento com uma Constante
▶ Exemplo
> **Saída:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado — instale-o localmente (`pip install pandas`) para acompanhar. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
: Preenchimento Constante e Estatístico com fillna (Dificuldade ⭐⭐)
import pandas as pd
import numpy as np
df = pd.DataFrame({
'name': ['Alice', 'Bob', 'Charlie', 'Carol', 'David'],
'age': [28, np.nan, 25, 30, np.nan],
'salary': [75000, 92000, np.nan, 88000, 105000]
})
# Preencher com constante
df_const = df.fillna({'age': 0, 'salary': 0})
print(df_const)
# Preencher com a média (mais comum para numéricos)
df_mean = df.copy()
df_mean['age'] = df_mean['age'].fillna(df_mean['age'].mean())
df_mean['salary'] = df_mean['salary'].fillna(df_mean['salary'].median())
print(df_mean)
# age: preenchido com 27.67, salary: preenchido com 88000.0
# Preencher com preenchimento para frente (usar valor anterior)
df_ffill = df.fillna(method='ffill')
print(df_ffill)
> **Saída:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado — instale-o localmente (`pip install pandas`) para acompanhar. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
fillna(method=...) está descontinuado — use df.ffill() / df.bfill() como alternativa.
(2) Preenchimento para Frente / para Trás
▶ Exemplo
> **Saída:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado — instale-o localmente (`pip install pandas`) para acompanhar. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
: ffill e bfill (Dificuldade ⭐)
import pandas as pd
import numpy as np
# Dados de série temporal — ffill faz sentido aqui
ts = pd.DataFrame({
'date': pd.date_range('2024-01-01', periods=8),
'temperature': [22.5, np.nan, np.nan, 23.1, np.nan, 24.0, np.nan, 24.5]
})
# Preenchimento para frente (propagar último valor válido)
print(ts.assign(temperature_ffill=ts['temperature'].ffill()))
# NaN → usar o valor não-NaN anterior
# Preenchimento para trás (propagar próximo valor válido)
print(ts.assign(temperature_bfill=ts['temperature'].bfill()))
# NaN → usar o próximo valor não-NaN
# Limite: preencher apenas N NaNs consecutivos
print(ts.assign(temperature_limited=ts['temperature'].ffill(limit=1)))
# Preencher apenas 1 NaN consecutivo, deixar o restante
> **Saída:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado — instale-o localmente (`pip install pandas`) para acompanhar. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
7. Interpolação com interpolate
(1) Interpolação Linear
▶ Exemplo
> **Saída:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado — instale localmente (`pip install pandas`) para acompanhar. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
: Interpolação Linear com interpolate (Dificuldade ⭐⭐)
import pandas as pd
import numpy as np
# Carol's heart rate data — linear interpolation is natural
hr = pd.DataFrame({
'time_min': list(range(10)),
'heart_rate': [72, 75, np.nan, 78, np.nan, 80, 82, np.nan, 85, 88]
})
# Linear interpolation (default)
hr['hr_linear'] = hr['heart_rate'].interpolate()
print(hr[['time_min', 'heart_rate', 'hr_linear']])
# time 2: (75+78)/2 = 76.5
# time 4: (78+80)/2 = 79.0
# time 7: (82+85)/2 = 83.5
# Index-based vs time-based interpolation
hr_ts = pd.DataFrame({
'heart_rate': [72, 75, np.nan, 78, np.nan, 80]
}, index=pd.to_timedelta([0, 5, 15, 20, 40, 45], unit='min'))
# Time-weighted interpolation
hr_ts['hr_time'] = hr_ts['heart_rate'].interpolate(method='time')
print(hr_ts)
> **Saída:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado — instale localmente (`pip install pandas`) para acompanhar. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
(2) Comparando Métodos de Interpolação
| Método | método | Quando Usar |
|---|---|---|
| Linear | 'linear' | Dados amostrados uniformemente, tendências suaves |
| Ponderado no tempo | 'time' | Intervalos de tempo desiguais |
| Ponderado pelo índice | 'index' | Espaçamento de índice desigual |
| Quadrático | 'quadratic' | Tendências curvas |
| Cúbico | 'cubic' | Curvas suaves |
| Mais próximo | 'nearest' | Dados discretos / de degrau |
| Spline | 'spline' | Interpolação suave (requer o parâmetro order) |
▶ Exemplo
> **Saída:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado — instale localmente (`pip install pandas`) para acompanhar. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
: Comparando Métodos de Interpolação (Dificuldade ⭐⭐)
import pandas as pd
import numpy as np
s = pd.Series([0.0, np.nan, np.nan, np.nan, np.nan, 5.0, 10.0])
print("linear: ", s.interpolate(method='linear').tolist())
# [0.0, ~1.67, ~3.33, 5.0, ~6.67, 5.0, 10.0]
# But this includes NaN boundaries.
print("linear (trim):", s.dropna().interpolate(method='linear').tolist())
# Demonstrates linear interpolation only.
# quadratic needs 1 non-NaN value on each side of the boundary; here we use 7 data points
print("quadratic:", s.interpolate(method='quadratic').tolist())
# Fits a quadratic curve; requires at least 3 non-NaN points
> **Saída:** Execute isso em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado — instale localmente (`pip install pandas`) para acompanhar. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
8. Copy-on-Write e Operações com Valores Ausentes
(1) Gravando Seguramente sob CoW
▶ Exemplo
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado — instale localmente (`pip install pandas`) para acompanhar. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
: Operações Seguras sob CoW (Dificuldade ⭐⭐)
import pandas as pd
import numpy as np
pd.options.mode.copy_on_write = True # padrão do Pandas 3.x
df = pd.DataFrame({
'name': ['Alice', 'Bob', 'Charlie'],
'age': [28, np.nan, 25],
'salary': [75000, 92000, np.nan]
})
# ✅ Seguro: atribuir o resultado de volta
df['age'] = df['age'].fillna(df['age'].mean())
# ✅ Seguro: usar inplace (funciona com CoW)
df.fillna({'salary': 0}, inplace=True)
# ❌ Perigoso (pré-CoW): atribuição encadeada
# subset = df[df['age'].notnull()]
# subset['salary'] = 100 # Pode não modificar df!
# ✅ Seguro com CoW: loc
df.loc[df['age'].notnull(), 'salary'] = df.loc[df['age'].notnull(), 'salary'] * 1.1
print(df)
> **Saída:** Execute em um ambiente Python local (pandas 2.x). O servidor Piston não tem pandas pré-instalado — instale localmente (`pip install pandas`) para acompanhar. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
9. Exemplo Completo: O Fluxo de Trabalho Completo para Valores Ausentes
(5) ▶ Árvore de Decisão para Tratamento de Valores Ausentes
graph TB
A[Detect missing values] --> B{Missing rate?}
B -->|<5%| C[Drop with dropna]
B -->|5-30%| D{Data type?}
D -->|Numeric| E[fillna with mean/median]
D -->|Categorical| F[fillna with mode]
D -->|Time-series| G[interpolate]
B -->|>50%| H[Consider dropping the column]
> **Saída:** Execute isto em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instale — instale-o localmente (`pip install pandas`) para acompanhar. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
▶ Exemplo
> **Saída:** Execute isto em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instale — instale-o localmente (`pip install pandas`) para acompanhar. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
: Fluxo de Trabalho Completo de Limpeza de Valores Ausentes (Dificuldade ⭐⭐⭐)
import pandas as pd
import numpy as np
# ============================================
# Comprehensive example: Full missing-value
# handling pipeline
# ============================================
# 1. Load data with missing values
np.random.seed(42)
df = pd.DataFrame({
'customer_id': range(1, 21),
'name': [f'Customer_{i}' for i in range(1, 21)],
'age': [25, np.nan, 35, 40, np.nan, 28, np.nan, 50, 33, np.nan,
29, np.nan, 45, 38, np.nan, 27, np.nan, 42, 31, np.nan],
'purchase_amount': [120, 85, np.nan, 200, 150, np.nan, 90, 310,
np.nan, 60, 175, np.nan, 250, 140, np.nan,
95, 180, np.nan, 110, 75],
'category': ['Electronics', np.nan, 'Home', 'Electronics', 'Clothing',
'Home', np.nan, 'Electronics', 'Clothing', np.nan',
'Home', 'Electronics', 'Clothing', np.nan, 'Home',
'Electronics', np.nan, 'Home', 'Clothing', 'Electronics']
})
# 2. Analyze missing pattern
print("=== Missing Analysis ===")
missing_report = pd.DataFrame({
'count': df.isnull().sum(),
'pct': (df.isnull().sum() / len(df) * 100).round(1)
})
print(missing_report[missing_report['count'] > 0])
# 3. Drop rows where critical fields are missing
df = df.dropna(subset=['customer_id', 'name'])
# 4. Fill numeric columns
df['age'] = df['age'].fillna(df['age'].median())
df['purchase_amount'] = df['purchase_amount'].interpolate(method='linear')
# 5. Fill categorical column
df['category'] = df['category'].fillna(df['category'].mode()[0])
# 6. Verify: no more missing
print(f"\n=== After Cleaning ===")
print(f"Remaining missing: {df.isnull().sum().sum()}")
print(df.head(10))
> **Saída:** Execute isto em um ambiente Python local (pandas 2.x). O servidor Piston não tem o pandas pré-instale — instale-o localmente (`pip install pandas`) para acompanhar. Os valores reais podem variar ligeiramente dependendo da sua versão do pandas.
❓ Perguntas Frequentes
df = df.fillna(...), o que é semanticamente mais claro. O CoW garante que ambas as abordagens produzem o mesmo resultado.df['col'].isnull().any() retorna True/False. Para contar: df['col'].isnull().sum() retorna o número de valores ausentes. Para a porcentagem: df['col'].isnull().mean() * 100. Para verificar toda a tabela: df.isnull().any().any() informa se o DataFrame tem algum NaN.df['col'].astype('Int64') (I maiúsculo), que representa valores ausentes com pd.NA enquanto preserva a semântica de inteiro.📖 Resumo
- NaN não é igual a si mesmo (IEEE 754), então você deve detectá-lo com isnull() — não pode usar ==
- isnull / notnull detectam valores ausentes; .sum() os conta e .any() faz uma verificação rápida
- dropna elimina linhas/colunas com valores ausentes: how='any'/'all', o limite thresh e subset para verificação específica de colunas
- fillna preenche valores: constante / média / mediana / moda; no Pandas 2.x use ffill()/bfill() em vez de método
- interpolate preenche por interpolação: linear é o padrão, time pondera por tempo, quadratic/cubic ajustam curvas
- Operações seguras com Escrita em Cópia: atribua de volta a uma variável ou use inplace
- O fluxo de trabalho: detectar e analisar → remover o que não pode ser corrigido → preencher/interpolar → verificar zero valores ausentes
📝 Exercícios
- Básico (Dificuldade ⭐): Crie um DataFrame contendo NaN. Use
isnull().sum()para contar os valores ausentes em cada coluna, remova as linhas com valores ausentes usandodropna(), e compare a mudança na contagem de linhas. - Intermediário (Dificuldade ⭐⭐): Crie um DataFrame de séries temporais (10 linhas com 3 valores NaN). Preencha-o respectivamente com
ffill()/bfill()/interpolate(method='linear'), e compare as diferenças entre os três resultados. - Desafio (Dificuldade ⭐⭐⭐): Simule 50 linhas de dados de clientes (idade/salário/categoria cada um com 15% de dados ausentes) e execute o fluxo completo de limpeza: análise de valores ausentes →
dropna(subset=[colunas_chave])→ preencher idade com a média → interpolar salário → preencher categoria com a moda → verificar se há zero valores ausentes.