Limpeza e Transformação de Dados
Conjuntos de dados reais chegam com dtypes incorretos, valores ausentes e strings confusas. A limpeza torna os agregados confiáveis antes de você mesclar ou modelar.
Receita
Cartão de referência rápida - pronto para copiar e colar.
import pandas as pd
df = pd.read_csv("raw.csv", dtype={"region": "category"})
df["revenue"] = pd.to_numeric(df["revenue"], errors="coerce")
df["sku"] = df["sku"].str.strip().str.upper()
df = df.dropna(subset=["revenue"])Quando usar isso:
- Primeira passagem em qualquer ingestão de CSV, exportação de API ou planilha
- Antes de joins (chaves devem ser normalizadas)
- Antes de divisões de treino/teste em pipelines de ML
- Quando os agregados parecem impossíveis (strings somando como zero)
Exemplo de Trabalho
import pandas as pd
import numpy as np
from io import StringIO
raw = """order_id,region,revenue,sku,ordered_at
1, east , $120 , abc-1 , 01/15/2025
2,WEST,340,def-2,2025-01-16
3,EAST,N/A,abc-1,2025-01-17
"""
df = pd.read_csv(StringIO(raw), dtype={"region": "string[pyarrow]"})
# Remover espaços em branco e normalizar chaves de texto
df["region"] = df["region"].str.strip().str.title()
df["sku"] = df["sku"].str.upper()
# Analisar dinheiro e datas
df["revenue"] = (
df["revenue"]
.str.replace("$", "", regex=False)
.str.replace(",", "", regex=False)
.replace("N/A", np.nan)
)
df["revenue"] = pd.to_numeric(df["revenue"], errors="coerce")
df["ordered_at"] = pd.to_datetime(df["ordered_at"], format="mixed", utc=True)
# Imputar com política documentada: mediana por região
df["revenue"] = df.groupby("region", observed=True)["revenue"].transform(
lambda s: s.fillna(s.median())
)
# Remover linhas ainda com chaves críticas ausentes
clean = df.dropna(subset=["order_id", "ordered_at"]).astype({"order_id": "int64"})
print(clean.dtypes)
print(clean)O que isso demonstra:
- Strings com suporte Arrow para operações de texto eficientes em memória
- Cadeias de acessadores
strpara espaços em branco e capitalização to_numeric(errors="coerce")transformando tokens ruins em NA- Imputação por grupo em vez de uma média global
Mergulho Profundo
Como Funciona
- Valores ausentes são mapeados para
NaNpara floats,pd.NApara dtypes anuláveis, ouNoneem colunas de objeto. - Métodos de string vetorizados rodam por elemento sem loops Python.
astypepode copiar;convert_dtypes()atualiza para tipos de extensão pandas anuláveis.- O dtype categórico armazena os níveis uma vez - ótimo para nomes de região repetidos.
Transformações Comuns
| Tarefa | API |
|---|---|
| Analisar números | pd.to_numeric(..., errors="coerce") |
| Analisar datas | pd.to_datetime(..., utc=True) |
| Limitar outliers | s.clip(lower, upper) |
| Renomear colunas | df.rename(columns={"old": "new"}) |
| Deduplicar linhas | df.drop_duplicates(subset=[...]) |
Notas Python
import pandas as pd
# Prefira Int64 anulável quando NA for possível
df["units"] = pd.array([1, None, 3], dtype="Int64")
# map para substituições de pequeno domínio
df["status"] = df["status"].map({"A": "active", "I": "inactive"})Armadilhas
- fillna(0) global em receita ausente - trata "desconhecido" como zero venda. Correção: política de NA separada por coluna; use medianas de grupo ou flags.
- Mutação in-place em uma view -
df.dropna(inplace=True)em uma fatia corrompe o pai. Correção: atribua de volta:df = df.dropna(). - Caracteres especiais de regex em str.replace -
$e.precisam deregex=Falseou escape. Correção: passeregex=Falsepara substituições literais. - Ambiguidade de data local -
01/02/2025é analisado de forma diferente por localidade. Correção: force ISO"%Y-%m-%d"na origem ou useformat="mixed"com auditoria. - Erros de digitação de categoria -
"East"e"east "se tornam categorias diferentes antes do strip. Correção: normalize strings antes deastype("category").
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| Pandera | Validação de esquema em CI | Limpeza rápida de notebook |
| Great Expectations | Contratos de dados de produção | Limpeza de CSV única |
Polars with_columns | Grandes pipelines lazy | Equipe só conhece pandas |
| OpenRefine / SQL | Limpeza com intervenção humana em escala | ETL scriptado e repetível |
FAQs
Devo remover ou imputar valores ausentes?
- Remova quando a ausência for rara e aleatória.
- Impute quando a remoção enviesar grupos - documente a estatística usada.
Como encontro chaves duplicadas antes de uma mesclagem?
dupes = df[df.duplicated("order_id", keep=False)]O que errors="coerce" faz?
- Parses inválidos se tornam
NaNem vez de gerar erro. - Siga com
dropnaou uma tabela de quarentena para linhas ruins.
Como padronizo nomes de colunas?
df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_")Quando devo usar o dtype category?
- Strings de baixa cardinalidade usadas em groupby/plots.
- Evite quando os níveis mudam constantemente (alta cardinalidade).
Como divido uma coluna em várias?
df[["city", "state"]] = df["location"].str.split(",", expand=True)Posso limpar em blocos para CSVs enormes?
for chunk in pd.read_csv("huge.csv", chunksize=100_000):
process(chunk)- Escreva blocos limpos em partições Parquet.
Como detecto outliers?
- Regra IQR:
Q3 + 1.5*IQR- marque, não remova silenciosamente sem revisão. - Use limites de domínio (receita não pode ser negativa).
Qual a diferença entre pd.NA e np.nan?
pd.NAé o valor ausente escalar do pandas para dtypes de extensão anuláveis.np.nané o valor ausente de float - propaga em colunas float.
Como valido a limpeza em testes?
- Afirme contagens de linhas, unicidade de chaves e
df["revenue"].isna().sum() == 0após a política. - Tire um snapshot dos dtypes como um dicionário em pytest.
Relacionado
- Series & DataFrames do pandas - noções básicas de seleção
- Joins & Merges - limpe chaves antes de juntar
- Validação e Qualidade de Dados - Pandera e Great Expectations
- Desempenho e Memória - categoricals e strings Arrow
Versões da Stack: Esta página foi escrita para Python 3.14.0 (stable 3.14, maintenance 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, e uv 0.6+.