Quando comecei a trabalhar com dados, lembro-me de haver passado 3 horas em uma análise incrível só para descobrir que metade dos dados estavam errados. Idades negativas, e-mails sem "@", datas no futuro... foi frustrante. Apanhei muito.
Desde então, sempre valido meus dados antes de começar qualquer análise. Nesse post vou mostrar como fazer isso de forma simples, sem complicação.
Por que Eu Sempre Valido Dados?
Vou contar uma história real. Uma vez, estava analisando dados de vendas de uma empresa e encontrei um vendedor que tinha "vendido" R$ -50.000 em um mês. Obviamente era um erro, mas se não tivesse checado, essa informação iria direto para o relatório do CEO.
Dados sujos podem arruinar toda sua análise. É melhor gastar 10 minutos validando, do que horas explicando por que os números não fazem sentido.
Ferramentas que Vamos Usar
Para esse post, vamos usar apenas:
- Pandas (que você provavelmente já tem)
- Um pouco de criatividade
Se não tem o pandas ainda:
pip install pandas
Pronto. Vamos começar.
Criando Dados de Exemplo
Vou criar uns dados bagunçados para a gente trabalhar. Esses são problemas reais que já encontrei por aí:
import pandas as pd
import numpy as np
# Dados problemáticos (baseados em casos reais)
dados = {
'nome': ['Ana Silva', 'Bruno', '', 'Carolina Santos', None, 'Pedro'],
'idade': [25, 150, -5, 30, 28, 200],
'email': ['ana@email.com', 'bruno@', 'carolina.email', 'carol@teste.com', 'joao@email.com', ''],
'salario': [5000, 50000, -1000, 7500, 6000, 0],
'data_nascimento': ['1998-05-15', '1870-01-01', '2025-12-31', '1993-03-20', '1995-07-10', ''],
'telefone': ['11999887766', '123', '', '11888777666', '11777666555', 'abc123']
}
df = pd.DataFrame(dados)
print("Nossos dados problemáticos:")
print(df)
Só de olhar já dá para ver vários problemas, né?
Primeira Coisa: Caçar os Valores Nulos
Sempre começo pelos valores nulos. É o mais fácil de detectar:
print("Valores nulos por coluna:")
print(df.isnull().sum())
# Vamos ver quais linhas têm algum problema
linhas_com_problemas = df[df.isnull().any(axis=1)]
print(f"\nLinhas com algum valor nulo: {len(linhas_com_problemas)}")
Mas cuidado! Nem todo valor "vazio" é detectado pelo .isnull(). Strings vazias passam batido.
O Truque das Strings Vazias
Aprendi isso da pior forma. Strings vazias ('') não são consideradas nulas pelo pandas. Vamos caça-las:
# Função para encontrar strings vazias
def encontrar_strings_vazias(df):
for coluna in df.columns:
# Só checa colunas de texto
if df[coluna].dtype == 'object':
vazias = (df[coluna].astype(str).str.strip() == '')
if vazias.any():
print(f"Coluna '{coluna}': {vazias.sum()} strings vazias")
encontrar_strings_vazias(df)
Essa função salvou minha vida várias vezes.
Validando Idades (Meu Pesadelo)
Idades são onde mais encontro problemas. Já vi "gente" com -5 anos e outros com 300 anos:
print("Problemas com idades:")
# Idades negativas
idade_negativa = df[df['idade'] < 0]
if not idade_negativa.empty:
print(f"Idades negativas: {len(idade_negativa)}")
print(idade_negativa[['nome', 'idade']])
# Idades impossíveis (acima de 120)
idade_alta = df[df['idade'] > 120]
if not idade_alta.empty:
print(f"Idades acima de 120: {len(idade_alta)}")
print(idade_alta[['nome', 'idade']])
Simples e efetivo.
E-mails: O Básico que Funciona
Para emails, uso uma verificação bem simples. Não precisa ser perfeita:
def validar_emails_simples(df):
# Checa se tem @ e um ponto depois do @
emails_validos = df['email'].str.contains('@.*\.', na=False)
emails_problema = df[~emails_validos]
print(f"Emails com problema: {len(emails_problema)}")
if not emails_problema.empty:
print(emails_problema[['nome', 'email']])
validar_emails_simples(df)
Não é perfeito, mas pega 90% dos problemas.
Datas: Meu Maior Desafio
Datas são complicadas. Já recebi datasets com pessoas nascidas em 2030:
def verificar_datas(df):
# Primeiro, vamos tentar converter para datetime
try:
datas = pd.to_datetime(df['data_nascimento'], errors='coerce')
# Datas que não conseguiram ser convertidas
datas_invalidas = datas.isna()
print(f"Datas inválidas: {datas_invalidas.sum()}")
# Datas no futuro
hoje = pd.Timestamp.now()
datas_futuro = datas > hoje
print(f"Datas no futuro: {datas_futuro.sum()}")
# Datas muito antigas (digamos, antes de 1900)
datas_antigas = datas < pd.Timestamp('1900-01-01')
print(f"Datas antes de 1900: {datas_antigas.sum()}")
except Exception as e:
print(f"Erro ao processar datas: {e}")
verificar_datas(df)
Telefones: Verificação Básica
Para telefones, mantenho simples:
def verificar_telefones(df):
# Telefones devem ter só números e ter pelo menos 10 dígitos
telefones_validos = df['telefone'].str.match(r'^\d{10,11}$', na=False)
telefones_problema = df[~telefones_validos]
print(f"Telefones com problema: {len(telefones_problema)}")
if not telefones_problema.empty:
print(telefones_problema[['nome', 'telefone']])
verificar_telefones(df)
Limpeza Automática: Minha Salvação
Depois de detectar, preciso limpar. Criei uma função que uso em todo projeto:
def limpar_dados_basico(df):
df_limpo = df.copy()
# Substitui strings vazias por NaN
df_limpo = df_limpo.replace('', np.nan)
# Remove linhas completamente vazias
df_limpo = df_limpo.dropna(how='all')
# Limpa espaços em branco das strings
for coluna in df_limpo.select_dtypes(include=['object']).columns:
df_limpo[coluna] = df_limpo[coluna].astype(str).str.strip()
# Volta para NaN se ficou vazio
df_limpo[coluna] = df_limpo[coluna].replace('nan', np.nan)
# Corrige idades impossíveis
df_limpo.loc[df_limpo['idade'] < 0, 'idade'] = np.nan
df_limpo.loc[df_limpo['idade'] > 120, 'idade'] = np.nan
# Corrige salários negativos
df_limpo.loc[df_limpo['salario'] < 0, 'salario'] = np.nan
return df_limpo
df_limpo = limpar_dados_basico(df)
print("Dados após limpeza:")
print(df_limpo)
Meu Relatório de Qualidade
Sempre faço um relatório para documentar o que encontrei:
def meu_relatorio_qualidade(df_original, df_limpo):
print("=" * 50)
print("RELATÓRIO DE QUALIDADE DOS DADOS")
print("=" * 50)
print(f"Registros originais: {len(df_original)}")
print(f"Registros após limpeza: {len(df_limpo)}")
print(f"Registros removidos: {len(df_original) - len(df_limpo)}")
print("\nCompletude por coluna (após limpeza):")
for coluna in df_limpo.columns:
nulos = df_limpo[coluna].isnull().sum()
percentual = (1 - nulos/len(df_limpo)) * 100
print(f" {coluna}: {percentual:.1f}% completo")
# Duplicatas
duplicatas = df_limpo.duplicated().sum()
print(f"\nRegistros duplicados: {duplicatas}")
print("=" * 50)
meu_relatorio_qualidade(df, df_limpo)
Validação Rápida que Uso Todo Dia
Para o dia a dia, criei uma função que roda tudo de uma vez:
def validacao_rapida(df):
print("🔍 VALIDAÇÃO RÁPIDA DOS DADOS\n")
# 1. Valores nulos
nulos = df.isnull().sum()
print("📊 Valores nulos:")
for coluna, qtd in nulos.items():
if qtd > 0:
print(f" - {coluna}: {qtd} ({qtd/len(df)*100:.1f}%)")
# 2. Strings vazias
print("\n📝 Strings vazias:")
for coluna in df.select_dtypes(include=['object']).columns:
vazias = (df[coluna].astype(str).str.strip() == '').sum()
if vazias > 0:
print(f" - {coluna}: {vazias}")
# 3. Duplicatas
duplicatas = df.duplicated().sum()
if duplicatas > 0:
print(f"\n📋 Registros duplicados: {duplicatas}")
# 4. Valores suspeitos em colunas numéricas
print("\n🔢 Valores suspeitos:")
for coluna in df.select_dtypes(include=['number']).columns:
negativos = (df[coluna] < 0).sum()
if negativos > 0:
print(f" - {coluna}: {negativos} valores negativos")
zeros = (df[coluna] == 0).sum()
if zeros > 0:
print(f" - {coluna}: {zeros} valores zero")
# Testando com nossos dados
validacao_rapida(df)
Dicas que Aprendi na Prática
- Sempre salve os dados originais:
df_original = df.copy() # Faça isso SEMPRE
- Documente o que você encontrou:
# Mantém um log dos problemas
problemas_encontrados = []
problemas_encontrados.append("5 idades negativas corrigidas")
- Para datasets grandes, use amostragem:
# Para datasets enormes, valide uma amostra primeiro
amostra = df.sample(1000)
validacao_rapida(amostra)
- Crie suas próprias regras de negócio:
# Exemplo: salário não pode ser maior que 100k
salarios_altos = df[df['salario'] > 100000]
if not salarios_altos.empty:
print(f"Salários suspeitos: {len(salarios_altos)}")
O Código Completo para Copiar
Aqui está tudo junto, pronto para usar:
import pandas as pd
import numpy as np
def validacao_completa_simples(df):
"""
Função que uso em todos os meus projetos
"""
print("🔍 VALIDANDO DADOS...\n")
problemas = []
# 1. Valores nulos
nulos = df.isnull().sum()
for coluna, qtd in nulos.items():
if qtd > 0:
problemas.append(f"Coluna '{coluna}': {qtd} valores nulos")
# 2. Strings vazias
for coluna in df.select_dtypes(include=['object']).columns:
vazias = (df[coluna].astype(str).str.strip() == '').sum()
if vazias > 0:
problemas.append(f"Coluna '{coluna}': {vazias} strings vazias")
# 3. Valores suspeitos
for coluna in df.select_dtypes(include=['number']).columns:
negativos = (df[coluna] < 0).sum()
if negativos > 0:
problemas.append(f"Coluna '{coluna}': {negativos} valores negativos")
# 4. Duplicatas
duplicatas = df.duplicated().sum()
if duplicatas > 0:
problemas.append(f"Registros duplicados: {duplicatas}")
# 5. Limpeza básica
df_limpo = df.copy()
df_limpo = df_limpo.replace('', np.nan)
df_limpo = df_limpo.dropna(how='all')
# Limpa espaços
for coluna in df_limpo.select_dtypes(include=['object']).columns:
df_limpo[coluna] = df_limpo[coluna].astype(str).str.strip()
df_limpo[coluna] = df_limpo[coluna].replace('nan', np.nan)
print(f"✅ RESULTADO:")
print(f" - Registros originais: {len(df)}")
print(f" - Registros limpos: {len(df_limpo)}")
print(f" - Problemas encontrados: {len(problemas)}")
if problemas:
print("\n⚠️ PROBLEMAS ENCONTRADOS:")
for problema in problemas:
print(f" - {problema}")
return df_limpo, problemas
# Exemplo de uso
df_limpo, lista_problemas = validacao_completa_simples(df)
Próximos Passos
Quando você dominar isso, pode partir para:
- Validação de CPF/CNPJ
- Consistência entre colunas (ex: data de nascimento vs idade)
- Detectar outliers automaticamente
- Validação de endereços
Conclusão
Validação de dados pode parecer chato, mas vai te salvar de muita dor de cabeça. Comece simples, com as verificações básicas que mostrei aqui.
Lembre-se: é melhor passar 10 minutos validando do que 2 horas explicando por que sua análise está errada.
Espero que tenha ajudado! Se tiver alguma dúvida ou quiser compartilhar suas próprias experiências com dados bagunçados, deixa um comentário aí embaixo.
Comentários (0)
Seja o primeiro a comentar!
Deixe um comentário