In [4]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import re
from datetime import datetime
import warnings
warnings.filterwarnings('ignore')
plt.style.use('seaborn-v0_8')
plt.rcParams['figure.figsize'] = (12, 8)
print("Bibliotecas importadas com sucesso!")
Bibliotecas importadas com sucesso!
1. Carregando e Explorando a Base de Dados¶
In [5]:
# Carregando a base de dados
df = pd.read_csv('base_dados_sintetica_suja.csv')
print(f"Shape da base: {df.shape}")
print(f"Colunas: {list(df.columns)}")
print("\nPrimeiras 5 linhas:")
df.head()
Shape da base: (10800, 12) Colunas: ['id', 'nome', 'email', 'cpf', 'telefone', 'data_cadastro', 'idade', 'renda', 'estado', 'cidade', 'status', 'valor_compra'] Primeiras 5 linhas:
Out[5]:
| id | nome | cpf | telefone | data_cadastro | idade | renda | estado | cidade | status | valor_compra | ||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | Brenda Alves | samuel32@example.net.invalid | 36295062838123 | (91) 9116-68732 | 15-05-2025 | 56.0 | 1434.0 | CE | Recife | Ativo | 376.0 |
| 1 | 2 | Yago Borges | bandradeexample.org | 20974745121 | (61) 9461-64955 | 00/00/0000 | NaN | 7067228.0 | RS | São Paulo | Pendente | 1801.0 |
| 2 | 3 | Igor Montenegro JR | novaismiguel@example.net | 995.619.255-56 | NaN | NaN | 64.0 | 5010.0 | BA | Porto Alegre | Pendente | -10000.0 |
| 3 | 4 | NaN | manuelapastor@example.com | 43788280537 | (71) 9862-82117 | 2023-13-45 | 65.0 | 8019.0 | BA | Belo Horizonte | Ativo | -50000.0 |
| 4 | 5 | Emanuel Sampaio | rfernandes@example.org | 66813220242 | (11) 9913-06093 | 2021-09-22 | 67.0 | -999999.0 | PR | Belo Horizonte | Ativo | 661.0 |
2. Diagnóstico Inicial - Identificando Problemas¶
In [6]:
# Função para análise de valores únicos
def analisar_coluna(coluna, df):
print(f"\n=== ANÁLISE DA COLUNA: {coluna.upper()} ===")
print(f"Tipo de dados: {df[coluna].dtype}")
print(f"Valores únicos: {df[coluna].nunique()}")
print(f"Valores nulos: {df[coluna].isnull().sum()}")
if df[coluna].dtype == 'object':
print("\nTop 10 valores mais frequentes:")
print(df[coluna].value_counts().head(10))
else:
print(f"\nEstatísticas:")
print(f"Mínimo: {df[coluna].min()}")
print(f"Máximo: {df[coluna].max()}")
print(f"Média: {df[coluna].mean():.2f}")
print(f"Mediana: {df[coluna].median():.2f}")
# Analisando cada coluna
for coluna in df.columns:
analisar_coluna(coluna, df)
=== ANÁLISE DA COLUNA: ID === Tipo de dados: int64 Valores únicos: 10799 Valores nulos: 0 Estatísticas: Mínimo: 1 Máximo: 10799 Média: 5400.43 Mediana: 5400.50 === ANÁLISE DA COLUNA: NOME === Tipo de dados: object Valores únicos: 8339 Valores nulos: 1093 Top 10 valores mais frequentes: nome Dr.... 14 Ana... 12 Sr.... 11 Mar... 9 Sra... 9 Dra... 8 Srt... 7 Car... 5 Cec... 5 Joã... 5 Name: count, dtype: int64 === ANÁLISE DA COLUNA: EMAIL === Tipo de dados: object Valores únicos: 8069 Valores nulos: 761 Top 10 valores mais frequentes: email teste@teste.com 233 invalid-email 218 user@domain 208 exemplo@exemplo.com 196 @example.com 87 @example.net 68 @example.org 60 esampaio@example.org 4 hda-rosa@example.com 4 lmendes@example.org 4 Name: count, dtype: int64 === ANÁLISE DA COLUNA: CPF === Tipo de dados: object Valores únicos: 8520 Valores nulos: 627 Top 10 valores mais frequentes: cpf 11111111111 224 99999999999 213 123.456.789-AB 187 00000000000 182 ABC12345678 175 75727950065 2 28044796875 2 62468578070 2 23252640664 2 10560486873 2 Name: count, dtype: int64 === ANÁLISE DA COLUNA: TELEFONE === Tipo de dados: object Valores únicos: 8620 Valores nulos: 979 Top 10 valores mais frequentes: telefone ABC123456 294 000000000 254 (91) 9116-68732 2 (71) 9452-30811 2 (41) 929153182 2 (51) 9729-90827 2 (81) 9729-36603 2 (31) 9323-10103 2 (71) 9778-72047 2 (61) 9105-42619 2 Name: count, dtype: int64 === ANÁLISE DA COLUNA: DATA_CADASTRO === Tipo de dados: object Valores únicos: 2229 Valores nulos: 315 Top 10 valores mais frequentes: data_cadastro 01/01/1900 166 00/00/0000 148 2023-13-45 144 32/13/2023 141 31/12/2030 126 2024-08-04 16 2020-11-11 15 2025-03-29 14 2023-09-18 13 2021-05-19 12 Name: count, dtype: int64 === ANÁLISE DA COLUNA: IDADE === Tipo de dados: float64 Valores únicos: 195 Valores nulos: 443 Estatísticas: Mínimo: -50.0 Máximo: 999.0 Média: 64.24 Mediana: 49.00 === ANÁLISE DA COLUNA: RENDA === Tipo de dados: float64 Valores únicos: 5964 Valores nulos: 515 Estatísticas: Mínimo: -999999.0 Máximo: 999999999.0 Média: 7199909.09 Mediana: 5199.00 === ANÁLISE DA COLUNA: ESTADO === Tipo de dados: object Valores únicos: 35 Valores nulos: 921 Top 10 valores mais frequentes: estado PE 806 RS 804 CE 799 MG 786 SC 782 GO 782 PR 768 RJ 764 SP 759 BA 737 Name: count, dtype: int64 === ANÁLISE DA COLUNA: CIDADE === Tipo de dados: object Valores únicos: 166 Valores nulos: 1254 Top 10 valores mais frequentes: cidade Brasília 891 Manaus 885 Fortaleza 871 São Paulo 865 Recife 855 Curitiba 838 Rio de Janeiro 825 Porto Alegre 812 Belo Horizonte 794 Salvador 792 Name: count, dtype: int64 === ANÁLISE DA COLUNA: STATUS === Tipo de dados: object Valores únicos: 33 Valores nulos: 1389 Top 10 valores mais frequentes: status Pendente 2101 Bloqueado 2090 Ativo 2077 Inativo 1981 STATUS 311 ATIVO 79 bloqueado 76 pendente 75 BLOQUEADO 74 INATIVO 73 Name: count, dtype: int64 === ANÁLISE DA COLUNA: VALOR_COMPRA === Tipo de dados: float64 Valores únicos: 2339 Valores nulos: 693 Estatísticas: Mínimo: -999999.0 Máximo: 999999999.0 Média: 5139469.17 Mediana: 1007.00
3. Identificando Problemas Específicos¶
In [7]:
# Problemas identificados
print("=== PROBLEMAS IDENTIFICADOS ===")
# 1. Dados duplicados
duplicatas = df.duplicated().sum()
print(f"1. Registros duplicados: {duplicatas}")
# 2. Emails inválidos
emails_invalidos = df[~df['email'].str.contains('@', na=False)].shape[0]
print(f"2. Emails inválidos: {emails_invalidos}")
# 3. CPFs com problemas
cpfs_problemas = df[df['cpf'].str.len() != 11].shape[0]
print(f"3. CPFs com problemas: {cpfs_problemas}")
# 4. Telefones sem DDD
telefones_sem_ddd = df[~df['telefone'].str.contains(r'\(', na=False)].shape[0]
print(f"4. Telefones sem DDD: {telefones_sem_ddd}")
# 5. Idades impossíveis
idades_impossiveis = df[(df['idade'] < 0) | (df['idade'] > 120)].shape[0]
print(f"5. Idades impossíveis: {idades_impossiveis}")
# 6. Rendas negativas
rendas_negativas = df[df['renda'] < 0].shape[0]
print(f"6. Rendas negativas: {rendas_negativas}")
# 7. Valores de compra negativos
valores_negativos = df[df['valor_compra'] < 0].shape[0]
print(f"7. Valores de compra negativos: {valores_negativos}")
=== PROBLEMAS IDENTIFICADOS === 1. Registros duplicados: 0 2. Emails inválidos: 1413 3. CPFs com problemas: 1372 4. Telefones sem DDD: 3021 5. Idades impossíveis: 785 6. Rendas negativas: 598 7. Valores de compra negativos: 389
4. Visualizando os Problemas¶
In [8]:
# Criando visualizações dos problemas
fig, axes = plt.subplots(2, 3, figsize=(18, 12))
fig.suptitle('Análise de Problemas na Base de Dados', fontsize=16, fontweight='bold')
# 1. Distribuição de idades
axes[0,0].hist(df['idade'].dropna(), bins=30, alpha=0.7)
axes[0,0].set_title('Distribuição de Idades')
axes[0,0].set_xlabel('Idade')
axes[0,0].set_ylabel('Frequência')
# 2. Distribuição de rendas
axes[0,1].hist(df['renda'].dropna(), bins=30, alpha=0.7)
axes[0,1].set_title('Distribuição de Rendas')
axes[0,1].set_xlabel('Renda')
axes[0,1].set_ylabel('Frequência')
# 3. Distribuição de valores de compra
axes[0,2].hist(df['valor_compra'].dropna(), bins=30, alpha=0.7)
axes[0,2].set_title('Distribuição de Valores de Compra')
axes[0,2].set_xlabel('Valor')
axes[0,2].set_ylabel('Frequência')
# 4. Estados mais frequentes
estados_counts = df['estado'].value_counts().head(10)
axes[1,0].bar(range(len(estados_counts)), estados_counts.values)
axes[1,0].set_title('Top 10 Estados')
axes[1,0].set_ylabel('Quantidade')
axes[1,0].tick_params(axis='x', rotation=45)
# 5. Status dos clientes
status_counts = df['status'].value_counts()
axes[1,1].pie(status_counts.values, labels=status_counts.index, autopct='%1.1f%%')
axes[1,1].set_title('Distribuição de Status')
# 6. Problemas identificados
problemas = ['Duplicatas', 'Emails Inválidos', 'CPFs Problemas', 'Telefones Sem DDD', 'Idades Impossíveis', 'Rendas Negativas']
valores = [duplicatas, emails_invalidos, cpfs_problemas, telefones_sem_ddd, idades_impossiveis, rendas_negativas]
axes[1,2].bar(problemas, valores, color='red', alpha=0.7)
axes[1,2].set_title('Quantidade de Problemas')
axes[1,2].tick_params(axis='x', rotation=45)
plt.tight_layout()
plt.show()
5. Tratamento de Dados - Passo a Passo¶
In [9]:
# Criando uma cópia para tratamento
df_limpo = df.copy()
print("Iniciando tratamento de dados...")
print(f"Registros iniciais: {len(df_limpo)}")
Iniciando tratamento de dados... Registros iniciais: 10800
5.1 Análise Detalhada de Dados Nulos¶
In [10]:
# Análise detalhada de dados nulos
print("=== ANÁLISE DE DADOS NULOS ===")
# 1. Total de nulos por coluna
nulos_por_coluna = df_limpo.isnull().sum()
print("\n1. Total de nulos por coluna:")
for coluna, nulos in nulos_por_coluna.items():
if nulos > 0:
pct = (nulos / len(df_limpo)) * 100
print(f" {coluna}: {nulos} ({pct:.1f}%)")
# 2. Registros com pelo menos um nulo
registros_com_nulos = df_limpo.isnull().any(axis=1).sum()
print(f"\n2. Registros com pelo menos um nulo: {registros_com_nulos}")
# 3. Padrões de nulos (quais colunas têm nulos juntos)
print("\n3. Análise de padrões de nulos:")
colunas_com_nulos = df_limpo.columns[df_limpo.isnull().any()].tolist()
for i, col1 in enumerate(colunas_com_nulos):
for col2 in colunas_com_nulos[i+1:]:
ambos_nulos = df_limpo[df_limpo[col1].isnull() & df_limpo[col2].isnull()].shape[0]
if ambos_nulos > 0:
print(f" {col1} e {col2}: {ambos_nulos} registros com ambos nulos")
=== ANÁLISE DE DADOS NULOS === 1. Total de nulos por coluna: nome: 1093 (10.1%) email: 761 (7.0%) cpf: 627 (5.8%) telefone: 979 (9.1%) data_cadastro: 315 (2.9%) idade: 443 (4.1%) renda: 515 (4.8%) estado: 921 (8.5%) cidade: 1254 (11.6%) status: 1389 (12.9%) valor_compra: 693 (6.4%) 2. Registros com pelo menos um nulo: 6288 3. Análise de padrões de nulos: nome e email: 83 registros com ambos nulos nome e cpf: 48 registros com ambos nulos nome e telefone: 116 registros com ambos nulos nome e data_cadastro: 34 registros com ambos nulos nome e idade: 32 registros com ambos nulos nome e renda: 55 registros com ambos nulos nome e estado: 94 registros com ambos nulos nome e cidade: 124 registros com ambos nulos nome e status: 146 registros com ambos nulos nome e valor_compra: 71 registros com ambos nulos email e cpf: 48 registros com ambos nulos email e telefone: 58 registros com ambos nulos email e data_cadastro: 21 registros com ambos nulos email e idade: 29 registros com ambos nulos email e renda: 40 registros com ambos nulos email e estado: 59 registros com ambos nulos email e cidade: 94 registros com ambos nulos email e status: 101 registros com ambos nulos email e valor_compra: 55 registros com ambos nulos cpf e telefone: 61 registros com ambos nulos cpf e data_cadastro: 16 registros com ambos nulos cpf e idade: 24 registros com ambos nulos cpf e renda: 38 registros com ambos nulos cpf e estado: 51 registros com ambos nulos cpf e cidade: 83 registros com ambos nulos cpf e status: 93 registros com ambos nulos cpf e valor_compra: 48 registros com ambos nulos telefone e data_cadastro: 33 registros com ambos nulos telefone e idade: 37 registros com ambos nulos telefone e renda: 38 registros com ambos nulos telefone e estado: 76 registros com ambos nulos telefone e cidade: 107 registros com ambos nulos telefone e status: 127 registros com ambos nulos telefone e valor_compra: 64 registros com ambos nulos data_cadastro e idade: 27 registros com ambos nulos data_cadastro e renda: 19 registros com ambos nulos data_cadastro e estado: 33 registros com ambos nulos data_cadastro e cidade: 38 registros com ambos nulos data_cadastro e status: 43 registros com ambos nulos data_cadastro e valor_compra: 26 registros com ambos nulos idade e renda: 12 registros com ambos nulos idade e estado: 41 registros com ambos nulos idade e cidade: 52 registros com ambos nulos idade e status: 48 registros com ambos nulos idade e valor_compra: 23 registros com ambos nulos renda e estado: 49 registros com ambos nulos renda e cidade: 53 registros com ambos nulos renda e status: 55 registros com ambos nulos renda e valor_compra: 31 registros com ambos nulos estado e cidade: 112 registros com ambos nulos estado e status: 113 registros com ambos nulos estado e valor_compra: 61 registros com ambos nulos cidade e status: 168 registros com ambos nulos cidade e valor_compra: 85 registros com ambos nulos status e valor_compra: 64 registros com ambos nulos
5.2 Tratamento de Dados Nulos¶
In [11]:
# Tratamento de dados nulos
print("=== TRATAMENTO DE DADOS NULOS ===")
# 1. Remover registros com muitos nulos (>50% das colunas)
print("\n1. Removendo registros com muitos nulos...")
limite_nulos = len(df_limpo.columns) * 0.5
registros_muitos_nulos = df_limpo[df_limpo.isnull().sum(axis=1) > limite_nulos].shape[0]
print(f" Registros com >50% de nulos: {registros_muitos_nulos}")
df_limpo = df_limpo[df_limpo.isnull().sum(axis=1) <= limite_nulos]
print(f" Registros após remoção: {len(df_limpo)}")
# 2. Preencher nulos com estratégias específicas
print("\n2. Preenchendo nulos com estratégias específicas:")
# Nomes: preencher com 'Nome não informado'
if df_limpo['nome'].isnull().sum() > 0:
df_limpo['nome'] = df_limpo['nome'].fillna('Nome não informado')
print(f" Nomes nulos preenchidos: {df_limpo['nome'].isnull().sum()}")
# Emails: remover registros (não é possível inferir)
if df_limpo['email'].isnull().sum() > 0:
df_limpo = df_limpo[df_limpo['email'].notna()]
print(f" Registros com email nulo removidos: {df_limpo['email'].isnull().sum()}")
# CPFs: remover registros (não é possível inferir)
if df_limpo['cpf'].isnull().sum() > 0:
df_limpo = df_limpo[df_limpo['cpf'].notna()]
print(f" Registros com CPF nulo removidos: {df_limpo['cpf'].isnull().sum()}")
# Telefones: preencher com 'Não informado'
if df_limpo['telefone'].isnull().sum() > 0:
df_limpo['telefone'] = df_limpo['telefone'].fillna('Não informado')
print(f" Telefones nulos preenchidos: {df_limpo['telefone'].isnull().sum()}")
# Idades: preencher com mediana
if df_limpo['idade'].isnull().sum() > 0:
mediana_idade = df_limpo['idade'].median()
df_limpo['idade'] = df_limpo['idade'].fillna(mediana_idade)
print(f" Idades nulas preenchidas com mediana: {df_limpo['idade'].isnull().sum()}")
# Rendas: preencher com mediana
if df_limpo['renda'].isnull().sum() > 0:
mediana_renda = df_limpo['renda'].median()
df_limpo['renda'] = df_limpo['renda'].fillna(mediana_renda)
print(f" Rendas nulas preenchidas com mediana: {df_limpo['renda'].isnull().sum()}")
# Estados: preencher com moda
if df_limpo['estado'].isnull().sum() > 0:
moda_estado = df_limpo['estado'].mode()[0]
df_limpo['estado'] = df_limpo['estado'].fillna(moda_estado)
print(f" Estados nulos preenchidos com moda: {df_limpo['estado'].isnull().sum()}")
# Cidades: preencher com moda
if df_limpo['cidade'].isnull().sum() > 0:
moda_cidade = df_limpo['cidade'].mode()[0]
df_limpo['cidade'] = df_limpo['cidade'].fillna(moda_cidade)
print(f" Cidades nulas preenchidas com moda: {df_limpo['cidade'].isnull().sum()}")
# Status: preencher com 'Pendente'
if df_limpo['status'].isnull().sum() > 0:
df_limpo['status'] = df_limpo['status'].fillna('Pendente')
print(f" Status nulos preenchidos: {df_limpo['status'].isnull().sum()}")
# Valores de compra: preencher com mediana
if df_limpo['valor_compra'].isnull().sum() > 0:
mediana_valor = df_limpo['valor_compra'].median()
df_limpo['valor_compra'] = df_limpo['valor_compra'].fillna(mediana_valor)
print(f" Valores de compra nulos preenchidos com mediana: {df_limpo['valor_compra'].isnull().sum()}")
print(f"\n✅ Registros após tratamento de nulos: {len(df_limpo)}")
=== TRATAMENTO DE DADOS NULOS === 1. Removendo registros com muitos nulos... Registros com >50% de nulos: 0 Registros após remoção: 10800 2. Preenchendo nulos com estratégias específicas: Nomes nulos preenchidos: 0 Registros com email nulo removidos: 0 Registros com CPF nulo removidos: 0 Telefones nulos preenchidos: 0 Idades nulas preenchidas com mediana: 0 Rendas nulas preenchidas com mediana: 0 Estados nulos preenchidos com moda: 0 Cidades nulas preenchidas com moda: 0 Status nulos preenchidos: 0 Valores de compra nulos preenchidos com mediana: 0 ✅ Registros após tratamento de nulos: 9460
5.3 Análise de Outliers¶
In [12]:
# Análise de outliers
print("=== ANÁLISE DE OUTLIERS ===")
# Variáveis numéricas para análise
colunas_numericas = ['idade', 'renda', 'valor_compra']
for coluna in colunas_numericas:
if coluna in df_limpo.columns:
print(f"\n📊 Análise de outliers em {coluna}:")
# Estatísticas básicas
Q1 = df_limpo[coluna].quantile(0.25)
Q3 = df_limpo[coluna].quantile(0.75)
IQR = Q3 - Q1
limite_inferior = Q1 - 1.5 * IQR
limite_superior = Q3 + 1.5 * IQR
outliers = df_limpo[(df_limpo[coluna] < limite_inferior) | (df_limpo[coluna] > limite_superior)]
print(f" Mínimo: {df_limpo[coluna].min():.2f}")
print(f" Q1: {Q1:.2f}")
print(f" Mediana: {df_limpo[coluna].median():.2f}")
print(f" Q3: {Q3:.2f}")
print(f" Máximo: {df_limpo[coluna].max():.2f}")
print(f" IQR: {IQR:.2f}")
print(f" Limite inferior: {limite_inferior:.2f}")
print(f" Limite superior: {limite_superior:.2f}")
print(f" Outliers encontrados: {len(outliers)} ({len(outliers)/len(df_limpo)*100:.1f}%)")
# Mostrar alguns exemplos de outliers
if len(outliers) > 0:
print(f" Exemplos de outliers: {outliers[coluna].head(5).tolist()}")
=== ANÁLISE DE OUTLIERS === 📊 Análise de outliers em idade: Mínimo: -50.00 Q1: 32.00 Mediana: 49.00 Q3: 66.00 Máximo: 999.00 IQR: 34.00 Limite inferior: -19.00 Limite superior: 117.00 Outliers encontrados: 594 (6.3%) Exemplos de outliers: [-25.0, 300.0, 145.0, -50.0, 200.0] 📊 Análise de outliers em renda: Mínimo: -999999.00 Q1: 2632.00 Mediana: 5185.00 Q3: 7768.25 Máximo: 999999999.00 IQR: 5136.25 Limite inferior: -5072.38 Limite superior: 15472.62 Outliers encontrados: 842 (8.9%) Exemplos de outliers: [7067228.0, -999999.0, -999999.0, -100000.0, 2242892.0] 📊 Análise de outliers em valor_compra: Mínimo: -999999.00 Q1: 492.75 Mediana: 1009.00 Q3: 1513.00 Máximo: 999999999.00 IQR: 1020.25 Limite inferior: -1037.62 Limite superior: 3043.38 Outliers encontrados: 704 (7.4%) Exemplos de outliers: [-10000.0, -50000.0, -1095.0, 999999999.0, 379795.0]
5.4 Tratamento de Outliers¶
In [13]:
# Tratamento de outliers
print("=== TRATAMENTO DE OUTLIERS ===")
# Criar cópia antes do tratamento
df_sem_outliers = df_limpo.copy()
for coluna in colunas_numericas:
if coluna in df_sem_outliers.columns:
print(f"\n🔧 Tratando outliers em {coluna}:")
# Calcular limites
Q1 = df_sem_outliers[coluna].quantile(0.25)
Q3 = df_sem_outliers[coluna].quantile(0.75)
IQR = Q3 - Q1
limite_inferior = Q1 - 1.5 * IQR
limite_superior = Q3 + 1.5 * IQR
# Contar outliers antes
outliers_antes = df_sem_outliers[(df_sem_outliers[coluna] < limite_inferior) |
(df_sem_outliers[coluna] > limite_superior)].shape[0]
# Estratégia 1: Winsorização (capar os valores)
df_sem_outliers[coluna] = df_sem_outliers[coluna].clip(limite_inferior, limite_superior)
# Contar outliers depois
outliers_depois = df_sem_outliers[(df_sem_outliers[coluna] < limite_inferior) |
(df_sem_outliers[coluna] > limite_superior)].shape[0]
print(f" Outliers antes: {outliers_antes}")
print(f" Outliers depois: {outliers_depois}")
print(f" Valores ajustados: {outliers_antes - outliers_depois}")
print(f"\n✅ Registros após tratamento de outliers: {len(df_sem_outliers)}")
=== TRATAMENTO DE OUTLIERS === 🔧 Tratando outliers em idade: Outliers antes: 594 Outliers depois: 0 Valores ajustados: 594 🔧 Tratando outliers em renda: Outliers antes: 842 Outliers depois: 0 Valores ajustados: 842 🔧 Tratando outliers em valor_compra: Outliers antes: 704 Outliers depois: 0 Valores ajustados: 704 ✅ Registros após tratamento de outliers: 9460
5.5 Removendo Duplicatas¶
In [14]:
# Removendo duplicatas
print("=== REMOVENDO DUPLICATAS ===")
print(f"Duplicatas encontradas: {df_limpo.duplicated().sum()}")
df_limpo = df_limpo.drop_duplicates()
print(f"Registros após remoção: {len(df_limpo)}")
print(f"Registros removidos: {len(df) - len(df_limpo)}")
=== REMOVENDO DUPLICATAS === Duplicatas encontradas: 0 Registros após remoção: 9460 Registros removidos: 1340
5.2 Padronizando Nomes¶
In [15]:
# Padronizando nomes
print("=== PADRONIZANDO NOMES ===")
def padronizar_nome(nome):
if pd.isna(nome):
return nome
# Remover espaços extras
nome = str(nome).strip()
# Converter para título (primeira letra maiúscula)
nome = nome.title()
# Corrigir caracteres especiais
nome = nome.replace('ã', 'ã').replace('ç', 'ç')
return nome
# Aplicando padronização
df_limpo['nome'] = df_limpo['nome'].apply(padronizar_nome)
print("Exemplos de nomes padronizados:")
print(df_limpo['nome'].head(10))
=== PADRONIZANDO NOMES === Exemplos de nomes padronizados: 0 Brenda Alves 1 Yago Borges 2 Igor Montenegro Jr 3 Nome Não Informado 4 Emanuel Sampaio 5 Lucca Caldeira 6 Dr. Augusto Siqueira Silva 7 Nome Não Informado 8 João Vitor Barros 9 Maria Clara Castro Name: nome, dtype: object
5.3 Limpando Emails¶
In [16]:
# Limpando emails
print("=== LIMPANDO EMAILS ===")
def limpar_email(email):
if pd.isna(email):
return email
email = str(email).strip().lower()
# Verificar se é válido
if '@' not in email or email.count('@') != 1:
return np.nan
# Remover emails temporários
if any(temp in email for temp in ['temp', 'teste', 'exemplo']):
return np.nan
return email
# Aplicando limpeza
df_limpo['email'] = df_limpo['email'].apply(limpar_email)
print(f"Emails válidos após limpeza: {df_limpo['email'].notna().sum()}")
print(f"Emails removidos: {df_limpo['email'].isna().sum()}")
=== LIMPANDO EMAILS === Emails válidos após limpeza: 8436 Emails removidos: 1024
5.4 Validando CPFs¶
In [17]:
# Validando CPFs
print("=== VALIDANDO CPFS ===")
def validar_cpf(cpf):
if pd.isna(cpf):
return np.nan
cpf = str(cpf).replace('.', '').replace('-', '')
# Verificar se tem 11 dígitos
if len(cpf) != 11:
return np.nan
# Verificar se são todos iguais
if cpf == cpf[0] * 11:
return np.nan
# Validar CPF (algoritmo simplificado)
try:
# Calcular primeiro dígito verificador
soma = sum(int(cpf[i]) * (10 - i) for i in range(9))
digito1 = (soma * 10) % 11
if digito1 == 10:
digito1 = 0
# Calcular segundo dígito verificador
soma = sum(int(cpf[i]) * (11 - i) for i in range(10))
digito2 = (soma * 10) % 11
if digito2 == 10:
digito2 = 0
# Verificar se os dígitos verificadores estão corretos
if int(cpf[9]) == digito1 and int(cpf[10]) == digito2:
return f"{cpf[:3]}.{cpf[3:6]}.{cpf[6:9]}-{cpf[9:]}"
else:
return np.nan
except:
return np.nan
# Aplicando validação
df_limpo['cpf'] = df_limpo['cpf'].apply(validar_cpf)
print(f"CPFs válidos após validação: {df_limpo['cpf'].notna().sum()}")
print(f"CPFs removidos: {df_limpo['cpf'].isna().sum()}")
=== VALIDANDO CPFS === CPFs válidos após validação: 81 CPFs removidos: 9379
5.5 Formatando Telefones¶
In [18]:
# Formatando telefones
print("=== FORMATANDO TELEFONES ===")
def formatar_telefone(telefone):
if pd.isna(telefone):
return telefone
telefone = str(telefone).replace('(', '').replace(')', '').replace('-', '').replace(' ', '')
# Verificar se tem pelo menos 10 dígitos
if len(telefone) < 10:
return np.nan
# Se não tem DDD, adicionar um padrão
if len(telefone) == 9:
telefone = '11' + telefone
# Formatar
if len(telefone) == 11:
return f"({telefone[:2]}) {telefone[2:6]}-{telefone[6:]}"
else:
return np.nan
# Aplicando formatação
df_limpo['telefone'] = df_limpo['telefone'].apply(formatar_telefone)
print(f"Telefones válidos após formatação: {df_limpo['telefone'].notna().sum()}")
print(f"Telefones removidos: {df_limpo['telefone'].isna().sum()}")
=== FORMATANDO TELEFONES === Telefones válidos após formatação: 7293 Telefones removidos: 2167
5.6 Padronizando Datas¶
In [19]:
# Padronizando datas
print("=== PADRONIZANDO DATAS ===")
def padronizar_data(data):
if pd.isna(data) or data == '':
return np.nan
data = str(data).strip()
# Tentar diferentes formatos
formatos = ['%d/%m/%Y', '%Y-%m-%d', '%d-%m-%Y', '%Y/%m/%d']
for formato in formatos:
try:
data_obj = datetime.strptime(data, formato)
# Verificar se a data é razoável
if 1900 <= data_obj.year <= 2024:
return data_obj.strftime('%d/%m/%Y')
except:
continue
return np.nan
# Aplicando padronização
df_limpo['data_cadastro'] = df_limpo['data_cadastro'].apply(padronizar_data)
print(f"Datas válidas após padronização: {df_limpo['data_cadastro'].notna().sum()}")
print(f"Datas removidas: {df_limpo['data_cadastro'].isna().sum()}")
=== PADRONIZANDO DATAS === Datas válidas após padronização: 7684 Datas removidas: 1776
5.7 Limpando Valores Numéricos¶
In [20]:
# Limpando valores numéricos
print("=== LIMPANDO VALORES NUMÉRICOS ===")
# Idade
df_limpo['idade'] = pd.to_numeric(df_limpo['idade'], errors='coerce')
df_limpo.loc[(df_limpo['idade'] < 0) | (df_limpo['idade'] > 120), 'idade'] = np.nan
# Renda
df_limpo['renda'] = pd.to_numeric(df_limpo['renda'], errors='coerce')
df_limpo.loc[df_limpo['renda'] < 0, 'renda'] = np.nan
# Valor de compra
df_limpo['valor_compra'] = pd.to_numeric(df_limpo['valor_compra'], errors='coerce')
df_limpo.loc[df_limpo['valor_compra'] < 0, 'valor_compra'] = np.nan
print(f"Idades válidas: {df_limpo['idade'].notna().sum()}")
print(f"Rendas válidas: {df_limpo['renda'].notna().sum()}")
print(f"Valores de compra válidos: {df_limpo['valor_compra'].notna().sum()}")
=== LIMPANDO VALORES NUMÉRICOS === Idades válidas: 8762 Rendas válidas: 8925 Valores de compra válidos: 9115
5.8 Padronizando Estados e Cidades¶
In [21]:
# Padronizando estados e cidades
print("=== PADRONIZANDO ESTADOS E CIDADES ===")
def padronizar_texto(texto):
if pd.isna(texto) or texto == '':
return np.nan
texto = str(texto).strip().title()
# Mapeamento de variações
mapeamento = {
'SP': 'São Paulo',
'RJ': 'Rio de Janeiro',
'MG': 'Minas Gerais',
'BA': 'Bahia',
'PR': 'Paraná'
}
return mapeamento.get(texto, texto)
# Aplicando padronização
df_limpo['estado'] = df_limpo['estado'].apply(padronizar_texto)
df_limpo['cidade'] = df_limpo['cidade'].apply(padronizar_texto)
df_limpo['status'] = df_limpo['status'].apply(padronizar_texto)
print("Padronização concluída!")
=== PADRONIZANDO ESTADOS E CIDADES === Padronização concluída!
6. Comparação Antes e Depois¶
In [22]:
# Comparando antes e depois
print("=== COMPARAÇÃO ANTES E DEPOIS ===")
comparacao = {
'Métrica': [
'Total de registros',
'Registros duplicados',
'Emails válidos',
'CPFs válidos',
'Telefones válidos',
'Datas válidas',
'Idades válidas',
'Rendas válidas',
'Valores de compra válidos'
],
'Antes': [
len(df),
df.duplicated().sum(),
df['email'].str.contains('@', na=False).sum(),
df['cpf'].str.len().eq(11).sum(),
df['telefone'].str.contains(r'\(', na=False).sum(),
df['data_cadastro'].notna().sum(),
df[(df['idade'] >= 0) & (df['idade'] <= 120)]['idade'].notna().sum(),
df[df['renda'] >= 0]['renda'].notna().sum(),
df[df['valor_compra'] >= 0]['valor_compra'].notna().sum()
],
'Depois': [
len(df_limpo),
df_limpo.duplicated().sum(),
df_limpo['email'].notna().sum(),
df_limpo['cpf'].notna().sum(),
df_limpo['telefone'].notna().sum(),
df_limpo['data_cadastro'].notna().sum(),
df_limpo['idade'].notna().sum(),
df_limpo['renda'].notna().sum(),
df_limpo['valor_compra'].notna().sum()
]
}
df_comparacao = pd.DataFrame(comparacao)
df_comparacao['Melhoria'] = df_comparacao['Depois'] - df_comparacao['Antes']
df_comparacao['Melhoria %'] = (df_comparacao['Melhoria'] / df_comparacao['Antes'] * 100).round(2)
print(df_comparacao)
=== COMPARAÇÃO ANTES E DEPOIS ===
Métrica Antes Depois Melhoria Melhoria %
0 Total de registros 10800 9460 -1340 -12.41
1 Registros duplicados 0 0 0 NaN
2 Emails válidos 9387 8436 -951 -10.13
3 CPFs válidos 9428 81 -9347 -99.14
4 Telefones válidos 7779 7293 -486 -6.25
5 Datas válidas 10485 7684 -2801 -26.71
6 Idades válidas 9572 8762 -810 -8.46
7 Rendas válidas 9687 8925 -762 -7.87
8 Valores de compra válidos 9718 9115 -603 -6.20
7. Visualizando as Melhorias¶
In [23]:
# Visualizando as melhorias
fig, axes = plt.subplots(2, 2, figsize=(15, 12))
fig.suptitle('Comparação Antes e Depois do Tratamento', fontsize=16, fontweight='bold')
# 1. Distribuição de idades
axes[0,0].hist(df['idade'].dropna(), bins=30, alpha=0.7, label='Antes', color='red')
axes[0,0].hist(df_limpo['idade'].dropna(), bins=30, alpha=0.7, label='Depois', color='green')
axes[0,0].set_title('Distribuição de Idades')
axes[0,0].set_xlabel('Idade')
axes[0,0].legend()
# 2. Distribuição de rendas
axes[0,1].hist(df['renda'].dropna(), bins=30, alpha=0.7, label='Antes', color='red')
axes[0,1].hist(df_limpo['renda'].dropna(), bins=30, alpha=0.7, label='Depois', color='green')
axes[0,1].set_title('Distribuição de Rendas')
axes[0,1].set_xlabel('Renda')
axes[0,1].legend()
# 3. Estados padronizados
estados_antes = df['estado'].value_counts().head(5)
estados_depois = df_limpo['estado'].value_counts().head(5)
x = np.arange(len(estados_antes))
width = 0.35
axes[1,0].bar(x - width/2, estados_antes.values, width, label='Antes', color='red', alpha=0.7)
axes[1,0].bar(x + width/2, estados_depois.values, width, label='Depois', color='green', alpha=0.7)
axes[1,0].set_title('Top 5 Estados')
axes[1,0].set_xticks(x)
axes[1,0].set_xticklabels(estados_antes.index, rotation=45)
axes[1,0].legend()
# 4. Melhorias percentuais
metricas = ['Emails', 'CPFs', 'Telefones', 'Datas', 'Idades', 'Rendas']
melhorias = [
df_comparacao[df_comparacao['Métrica'] == 'Emails válidos']['Melhoria %'].iloc[0],
df_comparacao[df_comparacao['Métrica'] == 'CPFs válidos']['Melhoria %'].iloc[0],
df_comparacao[df_comparacao['Métrica'] == 'Telefones válidos']['Melhoria %'].iloc[0],
df_comparacao[df_comparacao['Métrica'] == 'Datas válidas']['Melhoria %'].iloc[0],
df_comparacao[df_comparacao['Métrica'] == 'Idades válidas']['Melhoria %'].iloc[0],
df_comparacao[df_comparacao['Métrica'] == 'Rendas válidas']['Melhoria %'].iloc[0]
]
axes[1,1].bar(metricas, melhorias, color='blue', alpha=0.7)
axes[1,1].set_title('Melhorias Percentuais')
axes[1,1].set_ylabel('Melhoria (%)')
axes[1,1].tick_params(axis='x', rotation=45)
plt.tight_layout()
plt.show()
8. Salvando a Base Limpa¶
In [25]:
# Salvando a base limpa
df_limpo.to_csv('base_dados_sintetica_limpa.csv', index=False)
print("=== RESUMO FINAL ===")
print(f"Registros originais: {len(df)}")
print(f"Registros após limpeza: {len(df_limpo)}")
print(f"Registros removidos: {len(df) - len(df_limpo)}")
print(f"Taxa de retenção: {(len(df_limpo) / len(df) * 100):.1f}%")
print("\nBase limpa salva como 'base_dados_sintetica_limpa.csv'")
print("\nTratamento de dados concluído com sucesso!")
# Mostrar algumas linhas da base limpa
print("\nExemplos da base limpa:")
print(df_limpo.head())
=== RESUMO FINAL === Registros originais: 10800 Registros após limpeza: 9460 Registros removidos: 1340 Taxa de retenção: 87.6% Base limpa salva como 'base_dados_sintetica_limpa.csv' Tratamento de dados concluído com sucesso! Exemplos da base limpa: id nome email cpf telefone \ 0 1 Brenda Alves samuel32@example.net.invalid NaN (91) 9116-68732 1 2 Yago Borges NaN NaN (61) 9461-64955 2 3 Igor Montenegro Jr novaismiguel@example.net NaN NaN 3 4 Nome Não Informado manuelapastor@example.com NaN (71) 9862-82117 4 5 Emanuel Sampaio rfernandes@example.org NaN (11) 9913-06093 data_cadastro idade renda estado cidade status \ 0 NaN 56.0 1434.0 Ce Recife Ativo 1 NaN 49.0 7067228.0 Rs São Paulo Pendente 2 NaN 64.0 5010.0 Ba Porto Alegre Pendente 3 NaN 65.0 8019.0 Ba Belo Horizonte Ativo 4 22/09/2021 67.0 NaN Pr Belo Horizonte Ativo valor_compra 0 376.0 1 1801.0 2 NaN 3 NaN 4 661.0