4  Limpeza e preparação de dados

4.1 Por que a limpeza de dados importa

Em qualquer projeto de análise de dados, a etapa de limpeza e preparação consome, em média, mais tempo do que a análise em si. Dados do mundo real chegam com inconsistências de tipos, valores ausentes, duplicatas, categorias grafadas de formas diferentes e erros de digitação.

Iniciar uma análise estatística sem verificar a qualidade dos dados produz resultados incorretos - e o pior: resultados que parecem plausíveis.

4.2 Mercado e pesquisa

Analistas experientes dedicam de 50% a 80% do tempo de um projeto à preparação dos dados. Habilidade em limpeza de dados não é detalhe: é o que separa uma análise confiável de uma análise perigosa.

4.3 Verificação inicial do dataset

O fluxo de verificação inicial deve ser sempre o mesmo, independentemente da origem dos dados:

data("CO2")

# 1. Dimensões
cat("Linhas:", nrow(CO2), "| Colunas:", ncol(CO2), "\n")
Linhas: 84 | Colunas: 5 
# 2. Tipos de variáveis
glimpse(CO2)
Rows: 84
Columns: 5
$ Plant     <ord> Qn1, Qn1, Qn1, Qn1, Qn1, Qn1, Qn1, Qn2, Qn2, Qn2, Qn2, Qn2, …
$ Type      <fct> Quebec, Quebec, Quebec, Quebec, Quebec, Quebec, Quebec, Queb…
$ Treatment <fct> nonchilled, nonchilled, nonchilled, nonchilled, nonchilled, …
$ conc      <dbl> 95, 175, 250, 350, 500, 675, 1000, 95, 175, 250, 350, 500, 6…
$ uptake    <dbl> 16.0, 30.4, 34.8, 37.2, 35.3, 39.2, 39.7, 13.6, 27.3, 37.1, …
# 3. Nomes das colunas
names(CO2)
[1] "Plant"     "Type"      "Treatment" "conc"      "uptake"   
# 4. Verificar valores ausentes por coluna
colSums(is.na(CO2))
    Plant      Type Treatment      conc    uptake 
        0         0         0         0         0 
# 5. Resumo geral
summary(CO2)
     Plant             Type         Treatment       conc          uptake     
 Qn1    : 7   Quebec     :42   nonchilled:42   Min.   :  95   Min.   : 7.70  
 Qn2    : 7   Mississippi:42   chilled   :42   1st Qu.: 175   1st Qu.:17.90  
 Qn3    : 7                                    Median : 350   Median :28.30  
 Qc1    : 7                                    Mean   : 435   Mean   :27.21  
 Qc3    : 7                                    3rd Qu.: 675   3rd Qu.:37.12  
 Qc2    : 7                                    Max.   :1000   Max.   :45.50  
 (Other):42                                                                  

4.4 Valores ausentes

4.4.1 O que são e por que ocorrem

Valores ausentes (NA no R) são observações para as quais não existe registro. Podem ocorrer por falha na coleta, erro de digitação, dados não aplicáveis ou perda durante o processamento.

4.4.2 Tipos de ausência

Tipo Descrição Implicação
MCAR (Missing Completely at Random) Ausência não relacionada a nenhuma variável Remoção simples possível
MAR (Missing at Random) Ausência relacionada a outras variáveis observadas Imputação adequada
MNAR (Missing Not at Random) Ausência relacionada ao próprio valor faltante Problema sério, exige cuidado

4.4.3 Identificação e tratamento

# Função para resumo completo de ausentes
resumo_ausentes <- function(df) {
  ausentes <- colSums(is.na(df))
  perc     <- round(ausentes / nrow(df) * 100, 2)
  
  data.frame(
    Variavel  = names(ausentes),
    N_ausente = ausentes,
    Perc_ausente = perc) |>
    filter(N_ausente > 0) |>
    arrange(desc(Perc_ausente))
}

# Aplicando ao CO2 (sem ausentes, mas a função serve para qualquer dataset)
resultado <- resumo_ausentes(CO2)

if (nrow(resultado) == 0) {
  cat("Nenhum valor ausente encontrado.\n")
} else {
  kable(resultado, caption = "Valores ausentes por variável")
}
Nenhum valor ausente encontrado.
# Simulando ausentes para demonstração didática
set.seed(42)
CO2_sujo <- CO2
indices_na <- sample(nrow(CO2_sujo), size = 8)
CO2_sujo$uptake[indices_na] <- NA

cat("Valores ausentes inseridos:", sum(is.na(CO2_sujo$uptake)), "\n")
Valores ausentes inseridos: 8 
# Estratégia 1: remover linhas com NA (só justificada com poucos casos e MCAR)
CO2_sem_na <- CO2_sujo |> drop_na(uptake)
cat("Linhas após remoção:", nrow(CO2_sem_na), "\n")
Linhas após remoção: 76 
# Estratégia 2: imputar pela mediana do grupo (mais conservadora)
CO2_imputado <- CO2_sujo |>
  group_by(Type, Treatment) |>
  mutate(
    uptake = ifelse(is.na(uptake), median(uptake, na.rm = TRUE), uptake)) |>
  ungroup()

cat("Valores ausentes após imputação:", sum(is.na(CO2_imputado$uptake)), "\n")
Valores ausentes após imputação: 0 
Atenção

Nunca remova valores ausentes automaticamente sem investigar o padrão. Remoção inadequada pode introduzir viés nos resultados. Sempre documente a decisão tomada e o critério utilizado.

4.5 Verificação e correção de tipos

Variáveis com o tipo errado causam erros silenciosos - operações que parecem funcionar mas produzem resultados incorretos.

# Verificar tipos atuais
sapply(CO2, class)
$Plant
[1] "ordered" "factor" 

$Type
[1] "factor"

$Treatment
[1] "factor"

$conc
[1] "numeric"

$uptake
[1] "numeric"
# No CO2, Plant e Type e Treatment já são fatores
# Demonstração de conversão de tipos

CO2_tipado <- CO2 |>
  mutate(
    # Garantir que variáveis categóricas são fatores com labels claros
    Type = factor(Type,
                  levels = c("Quebec", "Mississippi"),
                  labels = c("Quebec", "Mississippi")),
    Treatment = factor(Treatment,
                       levels = c("nonchilled", "chilled"),
                       labels = c("Nao_resfriada", "Resfriada")),
    # Concentração como numérica (já está, mas explicitando)
    conc   = as.numeric(conc),
    uptake = as.numeric(uptake))

# Verificando resultado
str(CO2_tipado)
Classes 'nfnGroupedData', 'nfGroupedData', 'groupedData' and 'data.frame':  84 obs. of  5 variables:
 $ Plant    : Ord.factor w/ 12 levels "Qn1"<"Qn2"<"Qn3"<..: 1 1 1 1 1 1 1 2 2 2 ...
 $ Type     : Factor w/ 2 levels "Quebec","Mississippi": 1 1 1 1 1 1 1 1 1 1 ...
 $ Treatment: Factor w/ 2 levels "Nao_resfriada",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ conc     : num  95 175 250 350 500 675 1000 95 175 250 ...
 $ uptake   : num  16 30.4 34.8 37.2 35.3 39.2 39.7 13.6 27.3 37.1 ...

4.6 Verificação de duplicatas

# Verificar linhas duplicadas
n_duplicatas <- sum(duplicated(CO2))
cat("Linhas duplicadas:", n_duplicatas, "\n")
Linhas duplicadas: 0 
# Remover duplicatas se existirem
CO2_limpo <- CO2 |> distinct()
cat("Linhas após remoção de duplicatas:", nrow(CO2_limpo), "\n")
Linhas após remoção de duplicatas: 84 

4.7 Verificação de valores inconsistentes

Valores fora dos limites esperados podem indicar erros de medição ou digitação.

# Verificar valores de uptake: biologicamente, deve ser positivo
cat("Mínimo de uptake:", min(CO2$uptake), "\n")
Mínimo de uptake: 7.7 
cat("Máximo de uptake:", max(CO2$uptake), "\n")
Máximo de uptake: 45.5 
# Verificar se há valores negativos (biologicamente impossíveis aqui)
negativos <- CO2 |> filter(uptake < 0)
cat("Valores negativos:", nrow(negativos), "\n")
Valores negativos: 0 
# Verificar concentrações únicas (devem ser os 7 níveis esperados)
sort(unique(CO2$conc))
[1]   95  175  250  350  500  675 1000
# Resumo completo de qualidade dos dados
cat("=== Relatório de qualidade dos dados ===\n")
=== Relatório de qualidade dos dados ===
cat("Dimensões:              ", nrow(CO2), "x", ncol(CO2), "\n")
Dimensões:               84 x 5 
cat("Valores ausentes:       ", sum(is.na(CO2)), "\n")
Valores ausentes:        0 
cat("Linhas duplicadas:      ", sum(duplicated(CO2)), "\n")
Linhas duplicadas:       0 
cat("Variáveis categóricas:  ", sum(sapply(CO2, is.factor)), "\n")
Variáveis categóricas:   3 
cat("Variáveis numéricas:    ", sum(sapply(CO2, is.numeric)), "\n")
Variáveis numéricas:     2 

4.8 Transformação de variáveis

Transformações podem ser necessárias quando os dados violam pressupostos de normalidade ou quando a relação entre variáveis não é linear.

# Transformação logarítmica - reduz assimetria positiva
CO2 <- CO2 |>
  mutate(
    log_uptake  = log(uptake),
    sqrt_uptake = sqrt(uptake)
  )

# Comparando distribuições: original - log - raiz quadrada
par(mfrow = c(1, 3))

hist(CO2$uptake,
     main = "Original",
     xlab = "uptake",
     col  = "#4A6FA5",
     border = "white")

hist(CO2$log_uptake,
     main = "Log(uptake)",
     xlab = "log(uptake)",
     col  = "#6B4F4F",
     border = "white")

hist(CO2$sqrt_uptake,
     main = "Sqrt(uptake)",
     xlab = "sqrt(uptake)",
     col  = "#224573",
     border = "white")

Conceito

Transformações de dados não são manipulação dos resultados - são ajustes legítimos que permitem atender pressupostos de testes estatísticos. A transformação logarítmica é a mais comum em dados biológicos, pois estabiliza variâncias e reduz assimetria positiva. Sempre reporte que a variável foi transformada e interprete os resultados na escala original quando possível.

4.9 Checklist de limpeza

Antes de prosseguir para qualquer análise, percorra este checklist:

Boas práticas

Nunca modifique o arquivo de dados brutos. Mantenha o dataset original intacto e aplique todas as transformações em um novo objeto. Isso garante rastreabilidade e facilita a revisão da análise.