9 min de leitura
Pandas #4 - Analisando e Limpando Dados
Como inspecionar rapidamente um DataFrame e tratar os problemas mais comuns de dados sujos - células vazias, formatos errados, valores incorretos e duplicatas.
No post anterior você viu como fazer contas com Series e DataFrames. Mas antes de somar, multiplicar ou comparar qualquer coluna, tem um passo que sempre vem primeiro na prática: garantir que os dados que chegaram até você prestam. Células vazias, datas em formatos diferentes dentro da mesma coluna, valores absurdos e linhas duplicadas são a regra, não a exceção — e é aí que entra a limpeza de dados.
Neste post você vai aprender primeiro a dar uma “olhada rápida” num DataFrame recém-carregado, e depois a resolver os quatro problemas de dados sujos mais comuns: células vazias, formato errado, dados errados e duplicatas.
Inspecionando um DataFrame rapidamente
Antes de sair limpando qualquer coisa, o primeiro passo é entender o que você tem em mãos. Vamos usar como exemplo um DataFrame com o registro de treinos de uma pessoa ao longo de alguns dias:
import pandas as pd
dados = {
"duracao": [30, 45, 60, 45, None, 60, 450],
"pulso": [110, 117, 103, 109, 117, 102, 104],
"calorias": [280.0, 340.0, 380.5, None, 320.0, 400.0, 300.0]
}
df = pd.DataFrame(dados)
head() e tail()
.head() mostra as primeiras linhas do DataFrame (5 por padrão, mas você pode passar outro número):
print(df.head(3))
A saída é:
duracao pulso calorias
0 30.0 110 280.0
1 45.0 117 340.0
2 60.0 103 380.5
.tail() faz o mesmo, só que a partir do final:
print(df.tail(2))
duracao pulso calorias
5 60.0 102 400.0
6 450.0 104 300.0
Já dá pra notar algo estranho: uma duração de 450 no meio de valores que giram em torno de 30-60. Vamos voltar nisso mais adiante.
info()
Já .info() não mostra os dados em si, mostra metadados sobre a estrutura da tabela — quantidade de linhas, colunas, tipo de cada coluna e quantos valores não-nulos existem em cada uma:
print(df.info())
A saída é:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 7 entries, 0 to 6
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 duracao 6 non-null float64
1 pulso 7 non-null int64
2 calorias 6 non-null float64
dtypes: float64(2), int64(1)
Repare que duracao e calorias têm só 6 valores não-nulos, contra 7 linhas no total — ou seja, cada uma tem uma célula vazia. É exatamente esse tipo de sinal que o .info() ajuda a enxergar rápido, sem precisar vasculhar a tabela inteira na mão.
Nota: rodar
.head(),.tail()e.info()logo depois de carregar qualquer dataset novo é um hábito que economiza muito tempo de debug mais adiante.
Tratando células vazias
Células vazias (representadas como NaN) atrapalham cálculos estatísticos e podem até quebrar algumas operações. O Pandas dá duas saídas principais: remover as linhas com valor vazio, ou preenchê-las com algum valor.
Removendo linhas com dropna()
df_limpo = df.dropna()
print(df_limpo)
Por padrão, .dropna() devolve um novo DataFrame, sem alterar o original. Se quiser modificar o próprio df, use inplace=True:
df.dropna(inplace=True)
Preenchendo valores vazios com fillna()
Às vezes remover a linha inteira é desperdício — melhor preencher só a célula vazia. Dá pra aplicar em todo o DataFrame ou numa coluna específica:
df["calorias"].fillna(300, inplace=True)
# ou preenchendo por coluna, num único comando
df.fillna({"calorias": 300}, inplace=True)
Preenchendo com média, mediana ou moda
Chutar um número fixo (como o 300 acima) raramente é a melhor ideia. É mais comum calcular um valor a partir da própria coluna:
import pandas as pd
dados = {
"duracao": [30, 45, 60, 45, None, 60, 450],
"pulso": [110, 117, 103, 109, 117, 102, 104],
"calorias": [280.0, 340.0, 380.5, None, 320.0, 400.0, 300.0]
}
df = pd.DataFrame(dados)
media = df["calorias"].mean()
df.fillna({"calorias": media}, inplace=True)
- Média (
.mean()): soma de todos os valores dividida pela quantidade de valores. - Mediana (
.median()): o valor do meio depois de ordenar a coluna. - Moda (
.mode()[0]): o valor que mais se repete.
A média costuma ser um bom padrão pra colunas numéricas contínuas; a moda é mais indicada pra colunas categóricas.
Corrigindo o formato dos dados
Outro problema comum é uma coluna que deveria ter um tipo consistente (datas, por exemplo) mas guarda valores em formatos diferentes — "2026/03/10" numa linha, "10-03-2026" em outra.
import pandas as pd
dados = {
"data": ["2026/03/01", "2026/03/02", "10-03-2026", None],
"duracao": [30, 45, 60, 45]
}
df = pd.DataFrame(dados)
df["data"] = pd.to_datetime(df["data"], format="mixed")
print(df)
O parâmetro format="mixed" diz pro pd.to_datetime() tentar reconhecer formatos diferentes dentro da mesma coluna, em vez de exigir um padrão único. Valores que não conseguem ser convertidos (como um None) viram NaT (“Not a Time” — o equivalente a NaN, só que pra datas).
Depois de converter, dá pra remover as linhas que ficaram com data inválida:
df.dropna(subset=["data"], inplace=True)
O parâmetro subset restringe a busca por valores vazios só à coluna data, em vez de olhar o DataFrame inteiro.
Corrigindo dados errados
Diferente de uma célula vazia, um dado errado é um valor presente, só que implausível — lembra daquela duração de 450 minutos lá em cima, cercada de valores entre 30 e 60?
Corrigindo um valor pontualmente
Se você sabe exatamente qual linha e qual deveria ser o valor correto, o .loc resolve direto:
df.loc[6, "duracao"] = 45
Corrigindo em lote com uma regra
Pra datasets maiores, não dá pra corrigir linha por linha na mão. Uma abordagem comum é definir um limite e substituir tudo que ultrapassa ele:
for indice in df.index:
if df.loc[indice, "duracao"] > 120:
df.loc[indice, "duracao"] = 120
Removendo a linha em vez de corrigir
Se não dá pra saber qual seria o valor certo, às vezes a saída mais simples é descartar a linha inteira:
for indice in df.index:
if df.loc[indice, "duracao"] > 120:
df.drop(indice, inplace=True)
Vale lembrar que remover dados sempre tem um custo — você está jogando fora informação que pode fazer falta depois. Use com critério.
Removendo linhas duplicadas
Por fim, o problema mais direto de resolver: linhas repetidas inteiras, que costumam entrar no dataset por erro de coleta ou de junção de fontes diferentes.
Encontrando duplicatas com duplicated()
print(df.duplicated())
O retorno é uma Series de booleanos, uma posição por linha: True quando aquela linha é idêntica a alguma anterior, False caso contrário.
Removendo com drop_duplicates()
df.drop_duplicates(inplace=True)
Assim como nos outros métodos de limpeza, inplace=True aplica a remoção direto no df, sem precisar reatribuir o resultado a uma nova variável.
Com o DataFrame inspecionado e limpo dos problemas mais comuns, ele já está em condições pra um próximo nível de cuidado: o post seguinte aprofunda especificamente em dados ausentes, com isna(), ffill(), bfill() e interpolate() — ferramentas que complementam o dropna()/fillna() que você acabou de ver.
Fonte adaptada: Pandas - Analyzing DataFrames, Pandas - Cleaning Data, Pandas - Cleaning Empty Cells, Pandas - Cleaning Data of Wrong Format, Pandas - Fixing Wrong Data, Pandas - Removing Duplicates
Comentários