# Importar dados BRUTOS (com valores ausentes e problemas)
penguins_raw = pd.read_csv('https://raw.githubusercontent.com/allisonhorst/palmerpenguins/master/inst/extdata/penguins.csv')
# Diagnóstico inicial
glimpse(penguins_raw)Transformação e Limpeza de Dados
No módulo anterior, conhecemos os Pinguins de Palmer e aprendemos os princípios do Tidy Data. Agora chegou o momento de enfrentar a realidade: dados do mundo real raramente chegam limpos e prontos para análise.
Mesmo nosso dataset científico dos pinguins, coletado com rigor na Antártica, apresenta desafios reais que encontramos constantemente: valores ausentes, medidas biologicamente impossíveis, e inconsistências que podem comprometer nossas análises.
Neste módulo, você dominará o processo de transformação e limpeza usando nossos pinguins como case prático, desenvolvendo intuição para identificar e resolver problemas de qualidade que se aplicam a qualquer dataset.
Preparando o Ambiente e Dataset
Importando os Dados Brutos dos Pinguins
Rows: 344
Columns: 8
> species object 0 (0%) NAs : Adelie, Adelie, Adelie, Adelie, Adelie
> island object 0 (0%) NAs : Torgersen, Torgersen, Torgersen, Torgersen, Torgersen
> bill_length_mm float64 2 (1%) NAs : 39.1, 39.5, 40.3, nan, 36.7
> bill_depth_mm float64 2 (1%) NAs : 18.7, 17.4, 18.0, nan, 19.3
> flipper_length_mm float64 2 (1%) NAs : 181.0, 186.0, 195.0, nan, 193.0
> body_mass_g float64 2 (1%) NAs : 3750.0, 3800.0, 3250.0, nan, 3450.0
> sex object 16 (5%) NAs : male, female, female, nan, female
> year int64 0 (0%) NAs : 2007, 2007, 2007, 2007, 2007
Nossos dados dos pinguins já são organizados (tidy), com poucos valores ausentes. Para aprender técnicas de limpeza, vamos simular problemas típicos encontrados em datasets reais:
Criando e Resolvendo Problemas Reais
Simulando Problemas Típicos
# Criar dataset com problemas típicos para demonstrar soluções
penguins_problematic = penguins_raw.copy()
# Problema 1: Pinguim gigante (erro de digitação: 20kg em vez de 2kg)
penguins_problematic.loc[0, 'body_mass_g'] = 20000
# Problema 2: Bico impossível (alguém digitou em metros em vez de mm)
penguins_problematic.loc[1, 'bill_length_mm'] = 0.043 # 43mm virou 0.043
# Problema 3: Espécie com grafia inconsistente
penguins_problematic.loc[2, 'species'] = 'adelie' # minúscula
penguins_problematic.loc[3, 'species'] = 'ADELIE' # maiúscula
# Problema 4: Ilha com abreviação
penguins_problematic.loc[4, 'island'] = 'Torg' # Torgersen abreviado
# Problema 5: Sexo inconsistente
penguins_problematic.loc[5, 'sex'] = 'M' # M em vez de male
penguins_problematic.loc[6, 'sex'] = 'F' # F em vez de female
glimpse(penguins_problematic)Rows: 344
Columns: 8
> species object 0 (0%) NAs : Adelie, Adelie, adelie, ADELIE, Adelie
> island object 0 (0%) NAs : Torgersen, Torgersen, Torgersen, Torgersen, Torg
> bill_length_mm float64 2 (1%) NAs : 39.1, 0.043, 40.3, nan, 36.7
> bill_depth_mm float64 2 (1%) NAs : 18.7, 17.4, 18.0, nan, 19.3
> flipper_length_mm float64 2 (1%) NAs : 181.0, 186.0, 195.0, nan, 193.0
> body_mass_g float64 2 (1%) NAs : 20000.0, 3800.0, 3250.0, nan, 3450.0
> sex object 16 (5%) NAs : male, female, female, nan, female
> year int64 0 (0%) NAs : 2007, 2007, 2007, 2007, 2007
Detectando Valores Estranhos
# Detectar valores biologicamente impossíveis
print(f"Massa > 8kg: {(penguins_problematic['body_mass_g'] > 8000).sum()} pinguins")
print(f"Bico < 1mm: {(penguins_problematic['bill_length_mm'] < 1).sum()} pinguins")
print(f"Espécies únicas: {penguins_problematic['species'].nunique()} (esperamos 3)")
penguins_problematic['species'].value_counts()Massa > 8kg: 1 pinguins
Bico < 1mm: 1 pinguins
Espécies únicas: 5 (esperamos 3)
species
Adelie 150
Gentoo 124
Chinstrap 68
ADELIE 1
adelie 1
Name: count, dtype: int64
Padronizando Texto
penguins_clean = penguins_problematic.copy()
# Espécies sempre com primeira letra maiúscula
penguins_clean['species'] = penguins_clean['species'].str.title()
# Sexo sempre minúsculo e expandir abreviações
penguins_clean['sex'] = penguins_clean['sex'].str.lower()
penguins_clean['sex'] = penguins_clean['sex'].replace({'m': 'male', 'f': 'female'})
# Expandir abreviações das ilhas
penguins_clean['island'] = penguins_clean['island'].replace({'Torg': 'Torgersen'})
penguins_clean['species'].value_counts()species
Adelie 152
Gentoo 124
Chinstrap 68
Name: count, dtype: int64
Corrigindo Valores Estranhos
# Corrigir o pinguim de 20kg (provável erro de digitação)
penguins_clean.loc[penguins_clean['body_mass_g'] > 8000, 'body_mass_g'] = 2000
# Corrigir bico em metros (multiplicar por 1000)
penguins_clean.loc[penguins_clean['bill_length_mm'] < 1, 'bill_length_mm'] *= 1000
# Verificar correções
print(f"Massa > 8kg: {(penguins_clean['body_mass_g'] > 8000).sum()} pinguins")
print(f"Bico < 1mm: {(penguins_clean['bill_length_mm'] < 1).sum()} pinguins")Massa > 8kg: 0 pinguins
Bico < 1mm: 0 pinguins
Lidando com Valores Ausentes
Removendo Linhas com Dados Ausentes
total_original = len(penguins_clean)
penguins_no_na = penguins_clean.dropna()
sem_na = len(penguins_no_na)
perda = (total_original - sem_na) / total_original * 100
print(f"Registros originais: {total_original}")
print(f"Registros sem NA: {sem_na}")
print(f"Perda: {perda:.1f}%")Registros originais: 344
Registros sem NA: 328
Perda: 4.7%
Preenchendo Valores Ausentes
Cuidado com imputação! Quando você preenche valores ausentes, está alterando a distribuição real dos dados. Compare sempre resultados antes e depois.
# Exemplo: imputação por média da espécie
penguins_imputed = penguins_clean.copy()
numeric_cols = ['bill_length_mm', 'bill_depth_mm', 'flipper_length_mm', 'body_mass_g']
for col in numeric_cols:
penguins_imputed[col] = penguins_imputed.groupby('species')[col].transform(
lambda x: x.fillna(x.mean())
)
original_nas = penguins_clean.isnull().sum().sum()
final_nas = penguins_imputed.isnull().sum().sum()
print(f"NAs antes: {original_nas}")
print(f"NAs depois: {final_nas}")
print(f"Valores imputados: {original_nas - final_nas}")NAs antes: 24
NAs depois: 16
Valores imputados: 8
Entendendo “Outliers” vs Valores Raros
Outliers vs Valores Raros
Definição: Um outlier é um valor que não deveria existir no processo que gerou os dados - diferente de um valor apenas raro mas legítimo.
Cuidado! Um valor anômalo só é outlier se você tem conhecimento do domínio para classificá-lo como erro.
A Metáfora do Cisne Negro
Por séculos, europeus acreditavam que todos os cisnes eram brancos - até descobrirem cisnes negros na Austrália em 1697.
Lição: O que parece “impossível” pode ser apenas nossa ignorância sobre a distribuição real dos dados.
Exemplo: Em uma empresa, 99% ganham R$ 4.000-8.000, mas o diretor ganha R$ 30.000: - Visto como outlier: se tratamos como uma distribuição única - Visto como normal: se reconhecemos duas distribuições (operacional vs executiva)
Remover “cisnes negros” torna modelos mais limpos, mas menos capazes de modelar a realidade completa.
Como Distinguir?
Outliers verdadeiros (erros):
- Pinguim de 200kg (erro de digitação)
- Bico de -5mm (sensor defeituoso)
- Espécie “Adelie38” (erro tipográfico)
Cisnes negros (valores raros mas reais):
- Pinguim Gentoo de 6,5kg (grande, mas biologicamente possível)
- Adelie em ilha inusual (migração rara)
- Bico no extremo superior da distribuição (variação natural)
Critérios: Conhecimento do domínio + verificação da fonte + contexto
Detecção Prática
# Simular outliers verdadeiros
penguins_with_outliers = penguins_no_na.copy()
penguins_with_outliers.loc[0, 'body_mass_g'] = 20000 # Erro de digitação
penguins_with_outliers.loc[1, 'bill_length_mm'] = -5 # Sensor defeituoso
penguins_with_outliers.loc[2, 'species'] = 'Adelie38' # Erro tipográfico
# Detectar valores impossíveis
print(f"Massa > 10kg: {(penguins_with_outliers['body_mass_g'] > 10000).sum()} casos")
print(f"Bico negativo: {(penguins_with_outliers['bill_length_mm'] < 0).sum()} casos")
especies_com_numeros = penguins_with_outliers['species'].str.contains(r'\d', na=False).sum()
print(f"Espécie com números: {especies_com_numeros} casos")Massa > 10kg: 1 casos
Bico negativo: 1 casos
Espécie com números: 1 casos
# Detectar valores raros mas legítimos (cisnes negros)
penguins_final = penguins_no_na.copy()
muito_pesados = penguins_final[penguins_final['body_mass_g'] > penguins_final['body_mass_g'].quantile(0.95)]
print(f"Pinguins muito pesados (top 5%): {len(muito_pesados)}")
for _, p in muito_pesados.head(3).iterrows():
print(f" {p['species']}: {p['body_mass_g']:.0f}g (biologicamente possível)")
print(f"\nDiferença crucial:")
print(f"6.000g = Cisne negro (raro mas real)")
print(f"20.000g = Outlier (erro de digitação)")Pinguins muito pesados (top 5%): 17
Gentoo: 5700g (biologicamente possível)
Gentoo: 5700g (biologicamente possível)
Gentoo: 5850g (biologicamente possível)
Diferença crucial:
6.000g = Cisne negro (raro mas real)
20.000g = Outlier (erro de digitação)
Lição: Técnicas estatísticas não distinguem erros de valores raros - é necessário conhecimento do domínio.
Dataset Final Limpo
penguins = penguins_final.copy()
print(f"{len(penguins)} pinguins, {penguins.shape[1]} variáveis")
print(f"{penguins.isnull().sum().sum()} valores ausentes")
print(f"{penguins['species'].nunique()} espécies")
glimpse(penguins)328 pinguins, 8 variáveis
0 valores ausentes
3 espécies
Rows: 328
Columns: 8
> species object 0 (0%) NAs : Adelie, Adelie, Adelie, Adelie, Adelie
> island object 0 (0%) NAs : Torgersen, Torgersen, Torgersen, Torgersen, Torgersen
> bill_length_mm float64 0 (0%) NAs : 39.1, 43.0, 40.3, 36.7, 39.3
> bill_depth_mm float64 0 (0%) NAs : 18.7, 17.4, 18.0, 19.3, 20.6
> flipper_length_mm float64 0 (0%) NAs : 181.0, 186.0, 195.0, 193.0, 190.0
> body_mass_g float64 0 (0%) NAs : 2000.0, 3800.0, 3250.0, 3450.0, 3650.0
> sex object 0 (0%) NAs : male, female, female, female, male
> year int64 0 (0%) NAs : 2007, 2007, 2007, 2007, 2007
Conclusão
A transformação e limpeza de dados é onde a ciência de dados encontra a ciência do domínio. Com nossos pinguins, você aprendeu que limpeza eficaz exige conhecimento biológico - saber que ranges são plausíveis, como interpretar valores ausentes, e quando outliers representam variação natural legítima.
Recursos Adicionais
- Palmer Penguins Dataset - Documentação oficial do dataset
- Pandas Data Cleaning - Guia oficial para dados ausentes
- Biological Data Analysis - Métodos estatísticos para biologia
- Data Validation Best Practices - Framework para validação