Correlações e Seleção de Variáveis
Introdução à Ciência de Dados com R - INFNET
16.Jun.2026
Objetivos de Aprendizagem
Ao final desta aula, o estudante será capaz de:
- Calcular e interpretar correlações de Pearson e Spearman
- Construir e ler uma matriz de correlação visual com
corrplot - Entender o valor-p no contexto de testes de correlação
- Aplicar teste t e ANOVA para comparar grupos de preços
- Usar o critério de Bendel & Afifi para triagem de preditores
- Sair da aula com uma lista de variáveis candidatas para o modelo
Cronograma
1 Setup — Retomando o Dataset
Começamos exatamente de onde a Aula 9 terminou: dataset limpo, variável-alvo transformada.
library(tidyverse)
library(modeldata)
library(corrplot) # install.packages("corrplot")
library(broom) # arruma saídas de testes estatísticos
library(patchwork)
# Recarregar e limpar — igual ao final da Aula 9
data(ames)
ames_clean <- ames |>
filter(Gr_Liv_Area <= 4000) |>
mutate(log_preco = log10(Sale_Price)) # variável-alvo transformada
dim(ames_clean) # 2928 × 75#> [1] 2925 75
2 Correlação — Quantificando Relações
Na Aula 9 vimos relações em gráficos. Agora vamos medi-las. Correlação é um número entre −1 e +1 que resume a força e a direção da relação entre duas variáveis.
−1−0.7−0.30+0.3+0.7+1
Forte negativa < −0.7 Moderada −0.3 a −0.7 Fraca −0.3 a +0.3 Moderada +0.3 a +0.7 Forte positiva > +0.7
2.1 Pearson vs. Spearman
Como Sale_Price é assimétrico, a correlação de Spearman com log_preco tende a ser mais informativa. Use Pearson para confirmar relações lineares após a transformação log.
2.2 Calculando correlações com log_preco
# Correlação de todas as variáveis numéricas com log_preco
correlacoes <- ames_clean |>
select(where(is.numeric)) |>
cor(use = "complete.obs", method = "pearson") |>
as.data.frame() |>
rownames_to_column("variavel") |>
select(variavel, log_preco) |>
filter(variavel != "log_preco", variavel != "Sale_Price") |>
arrange(desc(abs(log_preco))) |>
rename(correlacao = log_preco)
# Ver as 15 mais correlacionadas
correlacoes |> head(15)#> variavel correlacao
#> 1 Gr_Liv_Area 0.7113418
#> 2 Garage_Cars 0.6749027
#> 3 Garage_Area 0.6536989
#> 4 Total_Bsmt_SF 0.6490548
#> 5 First_Flr_SF 0.6209804
#> 6 Year_Built 0.6168231
#> 7 Year_Remod_Add 0.5878485
#> 8 Full_Bath 0.5753146
#> 9 TotRms_AbvGrd 0.4937001
#> 10 Fireplaces 0.4879956
#> 11 Mas_Vnr_Area 0.4454380
#> 12 Wood_Deck_SF 0.3319996
#> 13 Open_Porch_SF 0.3245708
#> 14 Half_Bath 0.3044516
#> 15 Longitude -0.2921394
correlacoes |>
head(15) |>
ggplot(aes(x = reorder(variavel, abs(correlacao)),
y = correlacao,
fill = correlacao > 0)) +
geom_col(show.legend = FALSE) +
geom_hline(yintercept = c(-0.3, 0.3), linetype = "dashed",
color = "gray50", linewidth = 0.5) +
scale_fill_manual(values = c("TRUE" = "#4fd1a5", "FALSE" = "#e05c5c")) +
coord_flip() +
labs(
title = "Correlação das Variáveis Numéricas com log10(Sale_Price)",
subtitle = "Linhas tracejadas em ±0.3 — limiar de correlação moderada",
x = NULL,
y = "Correlação de Pearson"
) +
theme_minimal(base_size = 13) +
theme(plot.title = element_text(face = "bold"),
plot.subtitle = element_text(color = "gray60"))2.3 Testando a significância da correlação
Uma correlação pode ser alta por acaso se a amostra for pequena. O teste de correlação verifica se o valor observado é distinguível de zero.
# Teste de correlação — Gr_Liv_Area vs log_preco
cor.test(ames_clean$Gr_Liv_Area, ames_clean$log_preco) |>
tidy()#> # A tibble: 1 × 8
#> estimate statistic p.value parameter conf.low conf.high method alternative
#> <dbl> <dbl> <dbl> <int> <dbl> <dbl> <chr> <chr>
#> 1 0.711 54.7 0 2923 0.693 0.729 Pearson's… two.sided
Com n = 2.928, quase qualquer correlação — mesmo minúscula — terá p < 0.05. O que importa é a magnitude da correlação, não só a significância. Um r = 0.05 com p < 0.001 é estatisticamente significativo mas praticamente irrelevante.
3 Matriz de Correlação Visual
Com muitas variáveis, um único gráfico que mostre todas as correlações de uma vez é muito mais eficiente do que tabelas.
3.1 Selecionando as variáveis mais relevantes
# Variáveis com |r| > 0.3 em relação a log_preco
top_vars <- correlacoes |>
filter(abs(correlacao) > 0.3) |>
pull(variavel)
# Montar a submatriz
mat_cor <- ames_clean |>
select(all_of(top_vars), log_preco) |>
cor(use = "complete.obs")
dim(mat_cor)#> [1] 15 15
3.2 Construindo o corrplot
corrplot(mat_cor,
method = "color",
type = "upper",
order = "hclust",
addCoef.col = "black",
number.cex = 0.65,
tl.col = "black",
tl.srt = 45,
tl.cex = 0.8,
col = colorRampPalette(c("#e05c5c", "white", "#4fd1a5"))(200),
title = "Matriz de Correlação — Ames Housing",
mar = c(0, 0, 2, 0)
)Além de correlações altas com log_preco (última linha/coluna), observe pares de preditores muito correlacionados entre si — isso é multicolinearidade, um problema para regressão que trataremos na aula de modelagem com VIF.
3.3 Versão alternativa com ggplot2
mat_cor |>
as.data.frame() |>
rownames_to_column("var1") |>
pivot_longer(-var1, names_to = "var2", values_to = "cor") |>
ggplot(aes(x = var1, y = var2, fill = cor)) +
geom_tile(color = "white", linewidth = 0.3) +
geom_text(aes(label = round(cor, 2)), size = 2.6) +
scale_fill_gradient2(
low = "#e05c5c",
mid = "white",
high = "#4fd1a5",
midpoint = 0,
limits = c(-1, 1)
) +
theme_minimal(base_size = 11) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold"),
panel.grid = element_blank()
) +
labs(title = "Matriz de Correlação — Ames Housing",
x = NULL, y = NULL, fill = "r")4 Testes de Hipótese
Correlação responde perguntas sobre variáveis numéricas. Para perguntas do tipo “grupos diferentes têm preços médios diferentes?”, usamos testes de hipótese.
“All models are wrong, but some are useful — and all tests give p-values, but not all p-values are useful.” — adaptado de George Box
4.1 O que é o valor-p — de forma direta
| Conceito | Significado prático |
|---|---|
| H₀ (hipótese nula) | Não há diferença entre os grupos |
| H₁ (hipótese alternativa) | Há diferença real entre os grupos |
| valor-p | Prob. de observar resultado tão extremo se H₀ fosse verdadeira |
| p < 0.05 | Improvável sob H₀ → rejeitamos H₀ |
| p ≥ 0.05 | Compatível com H₀ → não rejeitamos H₀ |
A probabilidade de a hipótese ser verdadeira. Ele também não mede o tamanho do efeito. Um p minúsculo pode corresponder a uma diferença irrelevante na prática — sempre olhe a magnitude.
4.2 Teste t — Dois grupos
Pergunta: “Casas com lareira custam mais do que casas sem lareira?”
# Criar variável binária
ames_clean <- ames_clean |>
mutate(tem_lareira = ifelse(Fireplaces > 0, "Sim", "Não"))
# Visualizar
ggplot(ames_clean, aes(x = tem_lareira, y = log_preco, fill = tem_lareira)) +
geom_boxplot(alpha = 0.7, outlier.alpha = 0.3) +
scale_fill_manual(values = c("Sim" = "#4fd1a5", "Não" = "#7c6ff7")) +
labs(
title = "Preço por presença de lareira",
x = "Tem lareira?",
y = "log10(Sale_Price)"
) +
theme_minimal(base_size = 13) +
theme(legend.position = "none",
plot.title = element_text(face = "bold"))# Teste t de Welch (não assume variâncias iguais — mais robusto)
t.test(log_preco ~ tem_lareira, data = ames_clean) |>
tidy()#> # A tibble: 1 × 10
#> estimate estimate1 estimate2 statistic p.value parameter conf.low conf.high
#> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 -0.180 5.13 5.31 -32.1 5.68e-194 2911. -0.191 -0.169
#> # ℹ 2 more variables: method <chr>, alternative <chr>
# estimate1 e estimate2 = médias dos grupos
# statistic = valor t | p.value | conf.low, conf.high = IC 95%4.3 ANOVA — Três ou mais grupos
Pergunta: “O preço médio difere entre os tipos de zoneamento?”
# Visualizar primeiro
ggplot(ames_clean, aes(x = reorder(MS_Zoning, log_preco, median),
y = log_preco, fill = MS_Zoning)) +
geom_boxplot(alpha = 0.7, outlier.alpha = 0.3) +
scale_fill_viridis_d() +
labs(
title = "Preço por Tipo de Zoneamento",
x = NULL,
y = "log10(Sale_Price)"
) +
theme_minimal(base_size = 13) +
theme(legend.position = "none",
plot.title = element_text(face = "bold"))# ANOVA de uma via
modelo_anova <- aov(log_preco ~ MS_Zoning, data = ames_clean)
tidy(modelo_anova)#> # A tibble: 2 × 6
#> term df sumsq meansq statistic p.value
#> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 MS_Zoning 6 17.4 2.90 115. 7.93e-131
#> 2 Residuals 2918 73.5 0.0252 NA NA
# Qual par de grupos difere?
TukeyHSD(modelo_anova) |>
tidy() |>
filter(adj.p.value < 0.05) |>
arrange(adj.p.value) |>
head(10)#> # A tibble: 10 × 7
#> term contrast null.value estimate conf.low conf.high adj.p.value
#> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 MS_Zoning Residential_Med… 0 -0.251 -0.296 -0.205 0
#> 2 MS_Zoning A_agr-Floating_… 0 -0.814 -1.15 -0.481 0
#> 3 MS_Zoning C_all-Floating_… 0 -0.460 -0.562 -0.359 0
#> 4 MS_Zoning Residential_Med… 0 -0.171 -0.195 -0.147 0
#> 5 MS_Zoning A_agr-Residenti… 0 -0.734 -1.07 -0.403 0
#> 6 MS_Zoning C_all-Residenti… 0 -0.380 -0.474 -0.286 0
#> 7 MS_Zoning C_all-Residenti… 0 -0.210 -0.306 -0.113 1.44e-9
#> 8 MS_Zoning Residential_Hig… 0 -0.210 -0.309 -0.112 5.28e-9
#> 9 MS_Zoning Residential_Low… 0 -0.0801 -0.121 -0.0392 1.75e-7
#> 10 MS_Zoning C_all-Residenti… 0 -0.250 -0.380 -0.120 3.23e-7
Transforma a saída bagunçada de t.test(), aov() e outros testes em tibbles organizados — fáceis de filtrar, ordenar e exportar.
5 Seleção de Variáveis Candidatas
Com tantas variáveis disponíveis, precisamos de um critério sistemático para decidir quais levar para o modelo.
5.1 Critério de Bendel & Afifi (1977)
| Tipo de variável | Teste | Critério de inclusão |
|---|---|---|
| Numérica | Regressão simples com log_preco |
p < 0.25 |
| Categórica | ANOVA com log_preco |
p < 0.25 |
No estágio de triagem, preferimos ser inclusivos — melhor incluir uma variável fraca e descartá-la depois do que excluir um preditor que só se revela importante em presença de outras variáveis. O refinamento vem na modelagem com VIF e AIC.
5.2 Triagem das variáveis numéricas
vars_numericas <- ames_clean |>
select(where(is.numeric)) |>
select(-Sale_Price, -log_preco) |>
names()
# Função auxiliar: p-valor da regressão univariada
pvalor_regressao <- function(var) {
formula <- as.formula(paste("log_preco ~", var))
modelo <- lm(formula, data = ames_clean)
tidy(modelo) |>
filter(term != "(Intercept)") |>
pull(p.value) |>
min()
}
triagem_num <- tibble(
variavel = vars_numericas,
p_valor = map_dbl(vars_numericas, pvalor_regressao)
) |>
arrange(p_valor) |>
mutate(incluir = p_valor < 0.25)
triagem_num |> head(20)#> # A tibble: 20 × 3
#> variavel p_valor incluir
#> <chr> <dbl> <lgl>
#> 1 Total_Bsmt_SF 0 TRUE
#> 2 Gr_Liv_Area 0 TRUE
#> 3 Garage_Cars 0 TRUE
#> 4 Garage_Area 0 TRUE
#> 5 First_Flr_SF 1.51e-311 TRUE
#> 6 Year_Built 3.00e-306 TRUE
#> 7 Year_Remod_Add 1.94e-271 TRUE
#> 8 Full_Bath 1.91e-257 TRUE
#> 9 TotRms_AbvGrd 1.42e-179 TRUE
#> 10 Fireplaces 6.93e-175 TRUE
#> 11 Mas_Vnr_Area 1.39e-142 TRUE
#> 12 Wood_Deck_SF 3.32e- 76 TRUE
#> 13 Open_Porch_SF 1.00e- 72 TRUE
#> 14 Half_Bath 8.78e- 64 TRUE
#> 15 Longitude 1.20e- 58 TRUE
#> 16 Latitude 6.09e- 56 TRUE
#> 17 Bsmt_Full_Bath 1.62e- 52 TRUE
#> 18 Second_Flr_SF 2.69e- 49 TRUE
#> 19 Lot_Area 3.71e- 45 TRUE
#> 20 Bsmt_Unf_SF 2.13e- 26 TRUE
cat("Variáveis numéricas que passaram na triagem:", sum(triagem_num$incluir))#> Variáveis numéricas que passaram na triagem: 31
5.3 Triagem das variáveis categóricas
vars_cat <- ames_clean |>
select(where(is.factor)) |>
names()
pvalor_anova <- function(var) {
formula <- as.formula(paste("log_preco ~", var))
modelo <- aov(formula, data = ames_clean)
tidy(modelo) |>
filter(term != "Residuals") |>
pull(p.value)
}
triagem_cat <- tibble(
variavel = vars_cat,
p_valor = map_dbl(vars_cat, pvalor_anova)
) |>
arrange(p_valor) |>
mutate(incluir = p_valor < 0.25)
triagem_cat |> head(20)#> # A tibble: 20 × 3
#> variavel p_valor incluir
#> <chr> <dbl> <lgl>
#> 1 Neighborhood 0 TRUE
#> 2 Garage_Finish 3.97e-289 TRUE
#> 3 Garage_Type 9.44e-245 TRUE
#> 4 Foundation 1.50e-242 TRUE
#> 5 MS_SubClass 5.94e-237 TRUE
#> 6 Heating_QC 2.50e-183 TRUE
#> 7 BsmtFin_Type_1 6.09e-173 TRUE
#> 8 Mas_Vnr_Type 5.34e-134 TRUE
#> 9 Overall_Cond 7.21e-132 TRUE
#> 10 MS_Zoning 7.93e-131 TRUE
#> 11 Exterior_1st 2.33e-130 TRUE
#> 12 Exterior_2nd 1.92e-117 TRUE
#> 13 Bsmt_Exposure 1.25e-115 TRUE
#> 14 Garage_Cond 3.83e-101 TRUE
#> 15 Central_Air 3.08e- 96 TRUE
#> 16 Paved_Drive 3.94e- 95 TRUE
#> 17 Sale_Condition 2.77e- 93 TRUE
#> 18 Sale_Type 9.48e- 83 TRUE
#> 19 Lot_Shape 2.75e- 71 TRUE
#> 20 Electrical 6.32e- 60 TRUE
# Lista consolidada de variáveis candidatas
candidatas <- bind_rows(triagem_num, triagem_cat) |>
filter(incluir) |>
arrange(p_valor)
cat("Total de variáveis candidatas para o modelo:", nrow(candidatas))#> Total de variáveis candidatas para o modelo: 71
5.4 Principais variáveis selecionadas
Variáveis numéricas:
Variáveis categóricas:
5.5 O pipeline de seleção
Esta lista é apenas a triagem inicial. Na aula de regressão múltipla, refinaremos com VIF (detectar multicolinearidade) e AIC (comparar modelos). Algumas dessas variáveis podem ser redundantes entre si.
6 Prática Independente
6.1 Exercício 1 — Correlação de Spearman
Recalcule o ranking de correlações usando method = "spearman". Compare os dois rankings: alguma variável muda muito de posição? O que isso indica?
# Seu código aqui
correlacoes_spearman <- ames_clean |>
select(where(is.numeric)) |>
cor(use = "complete.obs", method = "spearman") |>
# ... complete o restante6.2 Exercício 2 — Teste t: garagem vs. sem garagem
Crie uma variável binária tem_garagem a partir de Garage_Area (área = 0 → sem garagem). Faça um boxplot e um teste t.
ames_clean <- ames_clean |>
mutate(tem_garagem = ifelse(Garage_Area > 0, "Sim", "Não"))
# Seu boxplot e t.test() aqui6.3 Exercício 3 — ANOVA: qualidade da cozinha
Aplique ANOVA para Kitchen_Qual vs log_preco. Se significativa, use TukeyHSD() para identificar quais pares de grupos diferem.
# Seu código aqui6.4 Desafio — Corrplot interativo com ggplotly
Transforme o heatmap de correlação em interativo com plotly::ggplotly().
library(plotly)
p <- # seu heatmap ggplot2 aqui
ggplotly(p, tooltip = c("x", "y", "fill"))7 Leituras Recomendadas
| Obra | Capítulo | Foco |
|---|---|---|
| R for Data Science, 2ª ed. — Wickham | Cap. 15–16 | Testes e correlação no R |
| Practical Statistics for Data Scientists — Bruce & Bruce | Cap. 3 | Testes de significância |
| Tidy Modeling with R — Kuhn & Silge | Cap. 4 | Análise do Ames Housing |
| Bendel & Afifi (1977) — JASA | — | Origem do critério p < 0.25 |
Para a Próxima Aula
- Finalizar os 4 exercícios com os dados do Ames
- Ler Cap. 4 do Tidy Modeling with R
-
Garantir que
tidymodelsestá instalado:install.packages("tidymodels") -
Próxima aula: Regressão Linear Simples com
tidymodels— primeiro modelo preditivo do curso!