Pandas Data Cleaning: Workflow prático
Updated on
Dados sujos travam a análise cedo. fillna ou substituições pontuais escondem problemas reais: tipos errados, duplicados silenciosos ou outliers que distorcem métricas.
PAS:
- Problema: Tipos mistos, nulos e duplicados quebram joins e enviesam estatísticas.
- Agitar: Correções rápidas empurram problemas para depois e geram retrabalho.
- Solução: Checklist repetível: padronizar tipos, tratar nulos com intenção, lidar com outliers, deduplicar e validar antes de exportar.
Workflow em resumo
| Passo | O que fazer | Exemplo |
|---|---|---|
| 1. Perfil | Ver colunas/nulos/únicos | df.info(), df.describe(include="all") |
| 2. Normalizar colunas | Limpar nomes/texto | df.columns = df.columns.str.strip().str.lower() |
| 3. Corrigir tipos | to_datetime, to_numeric, astype("category") | df["date"] = pd.to_datetime(df["date"], errors="coerce") |
| 4. Tratar faltantes | Remover ou preencher por regra | df["age"] = df["age"].fillna(df["age"].median()) |
| 5. Tratar outliers | Clip ou flag | df["rev_cap"] = df["revenue"].clip(upper=df["revenue"].quantile(0.99)) |
| 6. Deduplicar | Remover duplicatas exatas/parciais | df.drop_duplicates(subset=["id", "date"], keep="last") |
| 7. Validar | Garantir faixas/categorias | assert df["score"].between(0,100).all() |
1) Perfil rápido
summary = {
"rows": len(df),
"columns": df.shape[1],
"nulls": df.isna().sum(),
"unique_counts": df.nunique(),
}- Detecte tipos mistos cedo (
df.info()). - Veja caudas com
df.describe(percentiles=[0.01,0.99]).
2) Normalize colunas e texto
df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_")
df["country"] = df["country"].str.strip().str.title()- Nomes padrão simplificam joins e código.
- Para caracteres estranhos, use
.str.normalize("NFKC").
3) Corrija tipos
df["date"] = pd.to_datetime(df["date"], errors="coerce", utc=True)
df["amount"] = pd.to_numeric(df["amount"], errors="coerce")
df["segment"] = df["segment"].astype("category")errors="coerce"expõe valores ruins comoNaT/NaN.convert_dtypes()aplica tipos nulos e econômicos em lote.
4) Trate valores faltantes
df["age"] = df["age"].fillna(df["age"].median())
df["city"] = df["city"].fillna("Unknown")
df = df.dropna(subset=["id"]) # chave obrigatória- Escolha por coluna: numérica → mediana/média; categórica → modo/placeholder.
- Preserve sinal com flags:
df["age_imputed"] = df["age"].isna().
5) Trate outliers
upper = df["revenue"].quantile(0.99)
lower = df["revenue"].quantile(0.01)
df["revenue_capped"] = df["revenue"].clip(lower=lower, upper=upper)- Para razões, use z-score (
abs(z) < 3). - Em finanças, prefira clip a excluir para não alterar contagem.
6) Deduplicate com segurança
df = df.drop_duplicates(subset=["id", "date"], keep="last")- Confirme a chave única:
assert df.duplicated(["id"]).sum() == 0. - Para matches aproximados, normalize antes (e-mails em minúsculas, etc.).
7) Valide antes de exportar
assert df["score"].between(0, 100).all()
valid_segments = {"basic", "pro", "enterprise"}
assert df["segment"].isin(valid_segments).all()- Em testes, use
pd.testing.assert_frame_equalpara comparar saídas. - Adicione checagens leves de linhas/nulos em pipelines para evitar regressões.
Mini pipeline ponta a ponta
def clean(df):
df = df.copy()
df.columns = df.columns.str.strip().str.lower()
df["date"] = pd.to_datetime(df["date"], errors="coerce")
df["amount"] = pd.to_numeric(df["amount"], errors="coerce")
df["amount"] = df["amount"].fillna(df["amount"].median())
df["segment"] = df["segment"].fillna("unknown").str.lower()
df = df.dropna(subset=["id"])
df = df.drop_duplicates(subset=["id", "date"], keep="last")
assert df["amount"].ge(0).all()
return df- Copie primeiro para não mutar a entrada.
- Com
df.pipe(clean), o pipeline continua legível.
Guias relacionados
Principais pontos
- Padronize nomes, force tipos e trate nulos com intenção.
- Para outliers, prefira clip ou flag em vez de excluir silenciosamente.
- Valide chaves e faixas antes de enviar para BI/analytics.