Skip to content

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

PassoO que fazerExemplo
1. PerfilVer colunas/nulos/únicosdf.info(), df.describe(include="all")
2. Normalizar colunasLimpar nomes/textodf.columns = df.columns.str.strip().str.lower()
3. Corrigir tiposto_datetime, to_numeric, astype("category")df["date"] = pd.to_datetime(df["date"], errors="coerce")
4. Tratar faltantesRemover ou preencher por regradf["age"] = df["age"].fillna(df["age"].median())
5. Tratar outliersClip ou flagdf["rev_cap"] = df["revenue"].clip(upper=df["revenue"].quantile(0.99))
6. DeduplicarRemover duplicatas exatas/parciaisdf.drop_duplicates(subset=["id", "date"], keep="last")
7. ValidarGarantir faixas/categoriasassert 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 como NaT/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_equal para 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.