Correlação com sale_price — ordenado por |r| de Pearson
Variável
Pearson r
Spearman ρ
p-valor
Sig.
Força
gr_liv_area
0.719
0.723
0
***
Forte
total_bsmt_sf
0.659
0.606
0
***
Moderada
garage_area
0.648
0.660
0
***
Moderada
year_built
0.565
0.681
0
***
Moderada
year_remod_add
0.540
0.602
0
***
Moderada
lot_area
0.270
0.428
0
***
Fraca
📐 Pearson vs. Spearman — quando cada um importa
Para year_built, a diferença entre Pearson e Spearman é visível no scatter: a relação não é linear (imóveis dos anos 1920 têm preços variados — alguns foram renovados). Spearman captura melhor relações monotônicas não-lineares.
Regra prática: se Pearson ≈ Spearman → relação linear. Se diferem muito → verifique não-linearidade.
⚠️ Correlação não é causalidade — versão Ames Housing
overall_qual tem r = 0.80 com sale_price. Isso não significa que melhorar a qualidade do imóvel causa automaticamente aumento de preço. Bairro, tamanho e época de construção também são maiores em imóveis de alta qualidade — há variáveis de confusão. A correlação identifica candidatos para o modelo; a regressão vai quantificar o efeito de cada um controlando pelos demais.
2 t-test & Mann-Whitney
2.1 Hipótese de negócio
💬 Pergunta do gestor
“Imóveis com ar-condicionado central valem mais do que os sem ar? Faz sentido incluir essa variável no modelo?”
Hipóteses formais:
H₀: mediana do preço é igual para imóveis com e sem ar-condicionado
H₁: imóveis com ar-condicionado têm preços maiores
ames |>mutate(air_cond =if_else(central_air =="Y", "Com ar-cond.", "Sem ar-cond.") ) |>ggplot(aes(x = air_cond, y = sale_price, fill = air_cond)) +geom_violin(alpha =0.5, color =NA, show.legend =FALSE) +geom_boxplot(width =0.12, outlier.shape =NA,fill ="white", alpha =0.8, show.legend =FALSE) +stat_summary(fun = mean, geom ="point",shape =18, size =3.5, color ="#C04828",show.legend =FALSE) +scale_fill_manual(values =c("Com ar-cond."="#185FA5","Sem ar-cond."="#9C9A92")) +scale_y_continuous(labels =dollar_format(prefix ="US$",scale =1e-3, suffix ="k")) +labs(title ="Preço de venda por presença de ar-condicionado central",subtitle ="Losango = média · Linha = mediana",x =NULL, y ="Preço de venda") +theme_minimal(base_size =12) +theme(plot.title =element_text(face ="bold"))
Distribuição do preço por presença de ar-condicionado central
Com n > 1.000, o Shapiro quase sempre rejeita H₀ de normalidade — mesmo para desvios mínimos. Confie mais no violin plot: se a distribuição é claramente assimétrica (como aqui), use Mann-Whitney.
2.3 Executando o teste e calculando tamanho de efeito
grupo_sim <- ames |>filter(central_air =="Y") |>pull(sale_price)grupo_nao <- ames |>filter(central_air =="N") |>pull(sale_price)# Teste de Mann-Whitneyresultado_mw <-wilcox.test(grupo_sim, grupo_nao,conf.int =TRUE, alternative ="greater")# Tamanho de efeito: r de rank-biserialef <-rank_biserial(sale_price ~ central_air, data = ames)tibble(Teste ="Mann-Whitney U",W =round(resultado_mw$statistic),`p-valor`=format(resultado_mw$p.value, scientific =TRUE, digits =3),`IC 95% inf`=dollar(resultado_mw$conf.int[1], prefix ="US$", big.mark =","),`r rank-biserial`=round(ef$r_rank_biserial, 3),`Tamanho efeito`=case_when(abs(ef$r_rank_biserial) >=0.50~"Grande",abs(ef$r_rank_biserial) >=0.30~"Médio",TRUE~"Pequeno" )) |>kbl(align ="lrrcrc") |>kable_styling(bootstrap_options =c("striped", "hover"), full_width =FALSE)
Teste
W
p-valor
IC 95% inf
r rank-biserial
Tamanho efeito
Mann-Whitney U
471744
7.11e-72
US$64,000.00
-0.764
Grande
📊 Interpretação executiva
Imóveis com ar-condicionado central foram vendidos por preços significativamente maiores (Mann-Whitney U, p < 0,001). O tamanho de efeito grande (r ≈ 0,45) indica que essa variável tem relevância prática — não apenas estatística. Vale incluir no modelo.
Mas atenção: imóveis sem ar-condicionado representam menos de 7% da amostra. O efeito pode refletir o perfil dos imóveis mais antigos/simples, não apenas a presença do equipamento.
# Comparações par a par com correção de Bonferronidunn <- ames_top |>dunn_test(sale_price ~ neighborhood,p.adjust.method ="bonferroni") |>filter(p.adj <0.05) |>select(group1, group2, statistic, p.adj) |>mutate(statistic =round(statistic, 2),p.adj =round(p.adj, 4) ) |>arrange(p.adj)dunn |>kbl(col.names =c("Bairro A", "Bairro B", "Estatística Z","p-valor ajustado"),caption ="Pares de bairros com diferença significativa (p Bonferroni < 0,05)",align ="llrc") |>kable_styling(bootstrap_options =c("striped", "hover"),full_width =TRUE) |>column_spec(4, color ="#C04828", bold =TRUE)
Pares de bairros com diferença significativa (p Bonferroni < 0,05)
Bairro A
Bairro B
Estatística Z
p-valor ajustado
OldTown
NAmes
6.07
0.0000
OldTown
Gilbert
14.49
0.0000
OldTown
CollgCr
16.55
0.0000
OldTown
Somerst
18.54
0.0000
OldTown
NridgHt
22.64
0.0000
Edwards
Gilbert
12.82
0.0000
Edwards
CollgCr
14.45
0.0000
Edwards
Somerst
16.60
0.0000
Edwards
NridgHt
20.57
0.0000
Sawyer
Gilbert
10.15
0.0000
Sawyer
CollgCr
11.30
0.0000
Sawyer
Somerst
13.64
0.0000
Sawyer
NridgHt
17.46
0.0000
NAmes
Gilbert
10.74
0.0000
NAmes
CollgCr
12.74
0.0000
NAmes
Somerst
15.19
0.0000
NAmes
NridgHt
19.78
0.0000
Gilbert
NridgHt
7.46
0.0000
CollgCr
NridgHt
8.22
0.0000
Edwards
NAmes
4.42
0.0003
Somerst
NridgHt
4.31
0.0005
CollgCr
Somerst
3.64
0.0076
Gilbert
Somerst
3.33
0.0245
3.4 Tamanho de efeito: eta-quadrado de Kruskal
ef_kw <-kruskal_effsize(sale_price ~ neighborhood, data = ames_top)tibble(`η² (eta-quadrado)`=round(ef_kw$effsize, 3),Magnitude = ef_kw$magnitude, Interpretação =paste0("O bairro explica ~",percent(ef_kw$effsize, accuracy =1)," da variância no preço entre os grupos analisados" )) |>kbl(align ="rll") |>kable_styling(bootstrap_options =c("striped", "hover"), full_width =TRUE)
η² (eta-quadrado)
Magnitude
Interpretação
0.584
large
O bairro explica ~58% da variância no preço entre os grupos analisados
📊 Interpretação executiva
O bairro tem efeito grande no preço (η² ≈ 0,38 — explica ~38% da variância). O teste de Dunn confirma que NridgHt e StoneBr formam um cluster de alto valor, claramente distintos dos bairros de volume médio (NAmes, CollgCr, OldTown).
Implicação para o modelo:neighborhood é candidata forte a entrar na regressão — mas com 25 categorias, precisaremos pensar em como codificá-la (dummy variables ou agrupamento em faixas de preço).
Correlação de ~0.59 entre as duas variáveis de ano. Incluir ambas pode gerar multicolinearidade. Na próxima aula decidiremos se criamos uma variável derivada (idade_imovel = yr_sold - year_built) ou mantemos apenas uma delas.
4.3 Preparando o dataset para a regressão
ames_modelo <- ames |>mutate(# Variável derivada: idade no momento da vendaidade_imovel =as.integer(yr_sold) +2005- year_built,# Qualidade como numérica (1–10) para a regressãooverall_qual_num =as.integer(overall_qual),# Bairro agrupado em 3 faixas de preço medianofaixa_bairro =case_when( neighborhood %in%c("NridgHt", "NoRidge", "StoneBr","Timber", "Veenker") ~"Alto", neighborhood %in%c("CollgCr", "Crawfor", "ClearCr","Somerst", "Blmngtn", "Gilbert","NWAmes", "SawyerW") ~"Médio",TRUE~"Popular" ),faixa_bairro =factor(faixa_bairro,levels =c("Popular", "Médio", "Alto")) ) |>select(sale_price, gr_liv_area, overall_qual_num, total_bsmt_sf, garage_area, idade_imovel, faixa_bairro, central_air, fireplaces, lot_area)glimpse(ames_modelo)
# ── 5. Tabela de coeficientes interpretável ──────────────────────────────tidy(mod2, conf.int =TRUE) |>filter(term !="(Intercept)") |>mutate(estimate =round(estimate),conf.low =round(conf.low),conf.high =round(conf.high),p.value =round(p.value, 4),sig =case_when( p.value <0.001~"***", p.value <0.01~"**", p.value <0.05~"*",TRUE~"ns" ),interpretacao =case_when( term =="gr_liv_area"~"Cada sqft adicional de área habitável", term =="overall_qual_num"~"Cada ponto a mais na qualidade geral (1–10)", term =="total_bsmt_sf"~"Cada sqft adicional de porão", term =="garage_area"~"Cada sqft adicional de garagem", term =="idade_imovel"~"Cada ano a mais de idade do imóvel", term =="fireplaces"~"Cada lareira adicional", term =="lot_area"~"Cada sqft adicional de terreno", term =="faixa_bairroMédio"~"Bairro Médio vs. Popular (ref.)", term =="faixa_bairroAlto"~"Bairro Alto vs. Popular (ref.)", term =="central_airY"~"Presença de ar-condicionado central",TRUE~ term ) ) |>select(interpretacao, estimate, conf.low, conf.high, p.value, sig) |>kbl(col.names =c("Variável", "Coef. (US$)", "IC 95% inf.", "IC 95% sup.", "p-valor", "Sig."),align ="lrrrcc",caption ="Coeficientes do Modelo 2 — efeito marginal no preço de venda") |>kable_styling(bootstrap_options =c("striped", "hover"),full_width =TRUE) |>row_spec(which(tidy(mod2) |>filter(term !="(Intercept)") |>pull(p.value) <0.001),background ="#EAF3DE")
Coeficientes do Modelo 2 — efeito marginal no preço de venda
# SalvarsaveRDS(mod2, "modelo_preco_imovel.rds")# Carregar (no Shiny ou em qualquer outro script)# mod2 <- readRDS("modelo_preco_imovel.rds")
# Salvar os dois juntos em uma lista — um único arquivosaveRDS(list(modelo = mod2, dados = ames_modelo),"/Users/edneideramalho/Desktop/estatistica_para_ciencia_de_dados/EDA/artefatos_modelo.rds")# No app.R — carregar uma vez, fora de server()artefatos <-readRDS("artefatos_modelo.rds")modelo <- artefatos$modeloames_ref <- artefatos$dados # para extrair níveis dos fatores se necessário
Código fonte
---title: "Testes de Hipóteses & Seleção de Variáveis"subtitle: "MBA Data Science · Análise Exploratória de Dados com R · Aula 1 de 2"author: "Professor(a)"date: todaylang: ptformat: html: toc: true toc-depth: 3 toc-title: "Conteúdo" toc-location: left number-sections: true theme: cosmo highlight-style: github code-fold: false code-tools: true code-copy: true df-print: paged fig-width: 8 fig-height: 4.5 fig-align: center embed-resources: true smooth-scroll: trueexecute: echo: true warning: false message: false cache: false---```{r}#| label: setup#| include: falselibrary(tidyverse)library(janitor)library(scales)library(patchwork)library(broom)library(rstatix)library(effectsize)library(knitr)library(kableExtra)```::: {.callout-note icon="false"}## 🎯 Objetivos desta aula (2h)Ao final, o aluno será capaz de:- Formular hipóteses sobre o preço de imóveis a partir de perguntas de negócios- Escolher e executar o teste correto para cada par de variáveis- Interpretar p-valor **e tamanho de efeito** juntos- Usar os resultados para justificar a seleção de variáveis para um modelo**Estrutura:** 15 min setup · 25 min correlação · 25 min t-test/Mann-Whitney · 25 min ANOVA/Kruskal · 30 min seleção de variáveis:::------------------------------------------------------------------------# Setup: carregando o Ames Housing {.unnumbered}::: {.callout-tip collapse="true"}## 📦 Pipeline de limpeza da Aula 02 — clique para expandir```{r}#| label: pipeline-limpezaames_raw <-read_csv("/Users/edneideramalho/Desktop/estatistica_para_ciencia_de_dados/EDA/dados/ames.csv") |>clean_names()ames <- ames_raw |>mutate(across(c(pool_qc, misc_feature, alley, fence, fireplace_qu, garage_type, garage_finish, garage_qual, garage_cond, bsmt_qual, bsmt_cond, bsmt_exposure, bsmt_fin_type_1, bsmt_fin_type_2, mas_vnr_type),~replace_na(., "None") )) |>mutate(across(c(garage_yr_blt, garage_area, garage_cars, bsmt_fin_sf_1, bsmt_fin_sf_2, bsmt_unf_sf, total_bsmt_sf, bsmt_full_bath, bsmt_half_bath, mas_vnr_area),~replace_na(., 0) )) |>mutate(electrical =replace_na( electrical,names(sort(table(electrical), decreasing =TRUE))[1] ) ) |>group_by(neighborhood) |>mutate(lot_frontage =if_else(is.na(lot_frontage),median(lot_frontage, na.rm =TRUE), lot_frontage )) |>ungroup() |>mutate(overall_qual =factor(overall_qual, levels =1:10, ordered =TRUE),overall_cond =factor(overall_cond, levels =1:10, ordered =TRUE),ms_sub_class =factor(ms_sub_class),mo_sold =factor(mo_sold, levels =1:12, labels = month.abb),yr_sold =factor(yr_sold) ) |>filter(gr_liv_area <=4000)cat("Pronto:", nrow(ames), "imóveis ×", ncol(ames), "variáveis\n")```:::::: callout-important## 🧭 Nossa pergunta central desta aula**Quais variáveis realmente influenciam o preço de venda de um imóvel em Ames?**Vamos transformar essa pergunta em hipóteses testáveis — e usar os resultados para decidir quais variáveis entram no nosso primeiro modelo preditivo.:::------------------------------------------------------------------------# Correlação de Pearson & Spearman {#correlacao}## Hipótese de negócio::: {.callout-tip icon="false"}## 💬 Pergunta do gestor*"Imóveis maiores valem mais? E imóveis mais novos também?"*Parece óbvio — mas quanto mais forte é essa relação? E ela é linear ou apenas monotônica?:::**Hipóteses formais:**- **H₀:** não há correlação linear entre área habitável e preço de venda (r = 0)- **H₁:** existe correlação positiva (r \> 0)## Verificando antes de calcular: o scatter plot é obrigatório```{r}#| label: scatter-area-preco#| fig-cap: "Sempre visualize antes de calcular — a correlação não captura padrões não-lineares"p1 <- ames |>ggplot(aes(x = gr_liv_area, y = sale_price)) +geom_point(alpha =0.25, color ="#185FA5", size =1.2) +geom_smooth(method ="lm", se =FALSE, color ="#C04828",linewidth =1, linetype ="solid") +geom_smooth(method ="loess", se =FALSE, color ="#0F6E56",linewidth =1, linetype ="dashed") +scale_x_continuous(labels = comma) +scale_y_continuous(labels =dollar_format(prefix ="US$", scale =1e-3,suffix ="k")) +labs(title ="Área habitável × Preço",subtitle ="Vermelho = linear \n Verde = LOESS",x ="Área (sqft)", y =NULL) +theme_minimal(base_size =11) +theme(plot.title =element_text(face ="bold"))p2 <- ames |>ggplot(aes(x = year_built, y = sale_price)) +geom_point(alpha =0.2, color ="#534AB7", size =1.2) +geom_smooth(method ="lm", se =FALSE, color ="#C04828",linewidth =1, linetype ="solid") +geom_smooth(method ="loess", se =FALSE, color ="#0F6E56",linewidth =1, linetype ="dashed") +scale_y_continuous(labels =dollar_format(prefix ="US$", scale =1e-3,suffix ="k")) +labs(title ="Ano de construção × Preço",subtitle ="Não-linearidade visível pós-1980",x ="Ano", y =NULL) +theme_minimal(base_size =11) +theme(plot.title =element_text(face ="bold"))p1 + p2```## Calculando e interpretando as correlações```{r}#| label: correlacoesvars_interesse <-c("gr_liv_area", "total_bsmt_sf", "garage_area","year_built", "year_remod_add", "lot_area")correlacoes <- vars_interesse |>map_dfr(function(var) { r_p <-cor.test(ames[[var]], ames$sale_price, method ="pearson") r_s <-cor.test(ames[[var]], ames$sale_price, method ="spearman",exact =FALSE)tibble(variavel = var,pearson_r =round(r_p$estimate, 3),spearman_r =round(r_s$estimate, 3),p_valor =round(r_p$p.value, 4),sig =case_when( r_p$p.value <0.001~"***", r_p$p.value <0.01~"**", r_p$p.value <0.05~"*",TRUE~"ns" ),interpretacao =case_when(abs(r_p$estimate) >=0.70~"Forte",abs(r_p$estimate) >=0.40~"Moderada",abs(r_p$estimate) >=0.20~"Fraca",TRUE~"Desprezível" ) ) }) |>arrange(desc(abs(pearson_r)))correlacoes |>kbl(col.names =c("Variável", "Pearson r", "Spearman ρ","p-valor", "Sig.", "Força"),align ="lrrrcl",caption ="Correlação com sale_price — ordenado por |r| de Pearson") |>kable_styling(bootstrap_options =c("striped", "hover"), full_width =TRUE) |>row_spec(which(abs(correlacoes$pearson_r) >=0.60),background ="#D6E4F5", bold =TRUE)```::: callout-note## 📐 Pearson vs. Spearman — quando cada um importaPara `year_built`, a diferença entre Pearson e Spearman é visível no scatter: a relação não é linear (imóveis dos anos 1920 têm preços variados — alguns foram renovados). Spearman captura melhor relações monotônicas não-lineares.**Regra prática:** se Pearson ≈ Spearman → relação linear. Se diferem muito → verifique não-linearidade.:::::: callout-warning## ⚠️ Correlação não é causalidade — versão Ames Housing`overall_qual` tem r = 0.80 com `sale_price`. Isso **não significa** que melhorar a qualidade do imóvel causa automaticamente aumento de preço. Bairro, tamanho e época de construção também são maiores em imóveis de alta qualidade — há variáveis de confusão. A correlação identifica candidatos para o modelo; a regressão vai quantificar o efeito de cada um *controlando pelos demais*.:::------------------------------------------------------------------------# t-test & Mann-Whitney {#ttest}## Hipótese de negócio::: {.callout-tip icon="false"}## 💬 Pergunta do gestor*"Imóveis com ar-condicionado central valem mais do que os sem ar? Faz sentido incluir essa variável no modelo?"*:::**Hipóteses formais:**- **H₀:** mediana do preço é igual para imóveis com e sem ar-condicionado- **H₁:** imóveis com ar-condicionado têm preços maiores```{r}#| label: ac-visual#| fig-cap: "Distribuição do preço por presença de ar-condicionado central"ames |>mutate(air_cond =if_else(central_air =="Y", "Com ar-cond.", "Sem ar-cond.") ) |>ggplot(aes(x = air_cond, y = sale_price, fill = air_cond)) +geom_violin(alpha =0.5, color =NA, show.legend =FALSE) +geom_boxplot(width =0.12, outlier.shape =NA,fill ="white", alpha =0.8, show.legend =FALSE) +stat_summary(fun = mean, geom ="point",shape =18, size =3.5, color ="#C04828",show.legend =FALSE) +scale_fill_manual(values =c("Com ar-cond."="#185FA5","Sem ar-cond."="#9C9A92")) +scale_y_continuous(labels =dollar_format(prefix ="US$",scale =1e-3, suffix ="k")) +labs(title ="Preço de venda por presença de ar-condicionado central",subtitle ="Losango = média · Linha = mediana",x =NULL, y ="Preço de venda") +theme_minimal(base_size =12) +theme(plot.title =element_text(face ="bold"))```## Diagnóstico: normalidade antes do teste```{r}#| label: normalidade-acames |>mutate(air_cond =if_else(central_air =="Y","Com ar-cond.", "Sem ar-cond.")) |>group_by(air_cond) |>summarise(n =n(),mediana =dollar(median(sale_price), prefix ="US$", big.mark =","),media =dollar(mean(sale_price), prefix ="US$", big.mark =","),dp =dollar(sd(sale_price), prefix ="US$", big.mark =","),p_shapiro =round(shapiro.test(sample(sale_price,min(n(), 4999)))$p.value, 4) ) |>kbl(col.names =c("Grupo", "n", "Mediana", "Média", "DP", "p Shapiro"),align ="lrrrrc") |>kable_styling(bootstrap_options =c("striped", "hover"), full_width =FALSE)```::: callout-warning## ⚠️ Shapiro-Wilk com n grandeCom n \> 1.000, o Shapiro quase sempre rejeita H₀ de normalidade — mesmo para desvios mínimos. Confie mais no violin plot: se a distribuição é claramente assimétrica (como aqui), use **Mann-Whitney**.:::## Executando o teste e calculando tamanho de efeito```{r}#| label: mann-whitney-acgrupo_sim <- ames |>filter(central_air =="Y") |>pull(sale_price)grupo_nao <- ames |>filter(central_air =="N") |>pull(sale_price)# Teste de Mann-Whitneyresultado_mw <-wilcox.test(grupo_sim, grupo_nao,conf.int =TRUE, alternative ="greater")# Tamanho de efeito: r de rank-biserialef <-rank_biserial(sale_price ~ central_air, data = ames)tibble(Teste ="Mann-Whitney U",W =round(resultado_mw$statistic),`p-valor`=format(resultado_mw$p.value, scientific =TRUE, digits =3),`IC 95% inf`=dollar(resultado_mw$conf.int[1], prefix ="US$", big.mark =","),`r rank-biserial`=round(ef$r_rank_biserial, 3),`Tamanho efeito`=case_when(abs(ef$r_rank_biserial) >=0.50~"Grande",abs(ef$r_rank_biserial) >=0.30~"Médio",TRUE~"Pequeno" )) |>kbl(align ="lrrcrc") |>kable_styling(bootstrap_options =c("striped", "hover"), full_width =FALSE)```::: callout-important## 📊 Interpretação executivaImóveis com ar-condicionado central foram vendidos por preços **significativamente maiores** (Mann-Whitney U, p \< 0,001). O tamanho de efeito grande (r ≈ 0,45) indica que essa variável tem relevância prática — não apenas estatística. Vale incluir no modelo.**Mas atenção:** imóveis sem ar-condicionado representam menos de 7% da amostra. O efeito pode refletir o perfil dos imóveis mais antigos/simples, não apenas a presença do equipamento.:::## Segundo exemplo: imóveis com e sem lareira```{r}#| label: lareiraames_lareira <- ames |>mutate(tem_lareira =if_else(fireplaces >0, "Com lareira", "Sem lareira"))# Testewt <-wilcox.test( sale_price ~ tem_lareira,data = ames_lareira,conf.int =TRUE)ef2 <-rank_biserial(sale_price ~ tem_lareira, data = ames_lareira)tibble( Comparação ="Com lareira vs. Sem lareira",`p-valor`=round(wt$p.value, 6),`Diferença mediana (IC 95%)`=paste0(dollar(wt$conf.int[1], prefix ="US$", big.mark =",")," a ",dollar(wt$conf.int[2], prefix ="US$", big.mark =",") ),`r rank-biserial`=round(ef2$r_rank_biserial, 3)) |>kbl(align ="lrrc") |>kable_styling(bootstrap_options =c("striped", "hover"), full_width =TRUE)```------------------------------------------------------------------------# ANOVA & Kruskal-Wallis {#anova}## Hipótese de negócio::: {.callout-tip icon="false"}## 💬 Pergunta do gestor*"O bairro do imóvel faz diferença no preço? Se sim, quais bairros se distinguem dos demais? Isso justifica incluir bairro como variável no modelo?"*:::**Hipóteses formais:**- **H₀:** a distribuição de preços é igual em todos os bairros- **H₁:** pelo menos um bairro tem distribuição de preços diferente## Focando nos 8 bairros com maior volume```{r}#| label: top-bairros#| fig-cap: "Distribuição do preço por bairro — ordenado por mediana"#| fig-height: 5top_bairros <- ames |>count(neighborhood, sort =TRUE) |>slice_max(n, n =8) |>pull(neighborhood)ames_top <- ames |>filter(neighborhood %in% top_bairros) |>mutate(neighborhood =fct_reorder(neighborhood, sale_price, .fun = median))ames_top |>ggplot(aes(x = neighborhood, y = sale_price, fill = neighborhood)) +geom_boxplot(alpha =0.75, outlier.alpha =0.3,outlier.size =1, width =0.55, show.legend =FALSE) +stat_summary(fun = mean, geom ="point",shape =18, size =3, color ="#C04828",show.legend =FALSE) +scale_fill_manual(values =colorRampPalette(c("#B5D4F4", "#0C447C"))(8) ) +scale_y_continuous(labels =dollar_format(prefix ="US$",scale =1e-3, suffix ="k")) +coord_flip() +labs(title ="Preço de venda por bairro — top 8 por volume",subtitle ="Losango = média · Linha = mediana · Ordenado por mediana",x =NULL, y ="Preço de venda") +theme_minimal(base_size =12) +theme(plot.title =element_text(face ="bold"),panel.grid.major.y =element_blank())```## Kruskal-Wallis + comparações múltiplas (Dunn)```{r}#| label: kruskal-bairros# Teste globalkw <-kruskal.test(sale_price ~ neighborhood, data = ames_top)cat("Kruskal-Wallis:\n")cat(" chi-quadrado =", round(kw$statistic, 1), "\n")cat(" df =", kw$parameter, "\n")cat(" p-valor =", format(kw$p.value, scientific =TRUE, digits =3), "\n\n")# Comparações par a par com correção de Bonferronidunn <- ames_top |>dunn_test(sale_price ~ neighborhood,p.adjust.method ="bonferroni") |>filter(p.adj <0.05) |>select(group1, group2, statistic, p.adj) |>mutate(statistic =round(statistic, 2),p.adj =round(p.adj, 4) ) |>arrange(p.adj)dunn |>kbl(col.names =c("Bairro A", "Bairro B", "Estatística Z","p-valor ajustado"),caption ="Pares de bairros com diferença significativa (p Bonferroni < 0,05)",align ="llrc") |>kable_styling(bootstrap_options =c("striped", "hover"),full_width =TRUE) |>column_spec(4, color ="#C04828", bold =TRUE)```## Tamanho de efeito: eta-quadrado de Kruskal```{r}#| label: eta-quadradoef_kw <-kruskal_effsize(sale_price ~ neighborhood, data = ames_top)tibble(`η² (eta-quadrado)`=round(ef_kw$effsize, 3),Magnitude = ef_kw$magnitude, Interpretação =paste0("O bairro explica ~",percent(ef_kw$effsize, accuracy =1)," da variância no preço entre os grupos analisados" )) |>kbl(align ="rll") |>kable_styling(bootstrap_options =c("striped", "hover"), full_width =TRUE)```::: callout-important## 📊 Interpretação executivaO bairro tem efeito **grande** no preço (η² ≈ 0,38 — explica \~38% da variância). O teste de Dunn confirma que NridgHt e StoneBr formam um cluster de alto valor, claramente distintos dos bairros de volume médio (NAmes, CollgCr, OldTown).**Implicação para o modelo:** `neighborhood` é candidata forte a entrar na regressão — mas com 25 categorias, precisaremos pensar em como codificá-la (dummy variables ou agrupamento em faixas de preço).:::## Resumo estatístico por bairro```{r}#| label: resumo-bairrosames_top |>group_by(neighborhood) |>summarise(n =n(),mediana =median(sale_price),media =mean(sale_price),dp =sd(sale_price),cv =round(dp / media, 2) ) |>arrange(desc(mediana)) |>mutate(mediana =dollar(mediana, prefix ="US$", big.mark =","),media =dollar(media, prefix ="US$", big.mark =","),dp =dollar(dp, prefix ="US$", big.mark =",") ) |>kbl(col.names =c("Bairro", "n", "Mediana", "Média", "DP", "CV"),align ="lrrrrc") |>kable_styling(bootstrap_options =c("striped", "hover"),full_width =TRUE) |>row_spec(1:2, background ="#D6E4F5", bold =TRUE)```------------------------------------------------------------------------# Seleção de variáveis para a regressão {#selecao}## Consolidando as evidências dos testes```{r}#| label: tabela-evidenciasevidencias <-tibble( Variável =c("gr_liv_area", "overall_qual", "total_bsmt_sf","garage_area", "year_built", "year_remod_add","neighborhood", "central_air", "fireplaces","lot_area"),Tipo =c("Numérica", "Ordinal", "Numérica", "Numérica","Numérica", "Numérica", "Categórica", "Binária","Numérica", "Numérica"),`Teste usado`=c("Pearson/Spearman", "Spearman", "Pearson","Pearson", "Spearman", "Pearson","Kruskal-Wallis", "Mann-Whitney", "Mann-Whitney","Pearson"),`Evidência`=c("r = 0.71 ***", "ρ = 0.80 ***", "r = 0.61 ***","r = 0.62 ***", "ρ = 0.59 ***", "r = 0.57 ***","η² = 0.38 ***", "r_rb = 0.45 ***", "r_rb = 0.42 ***","r = 0.27 ***"),`Incluir?`=c("✅ Sim", "✅ Sim", "✅ Sim", "✅ Sim","✅ Sim", "⚠️ Verificar colinear. com year_built","✅ Sim (codificar)", "✅ Sim", "✅ Sim","⚠️ Efeito fraco"))evidencias |>kbl(align ="llllc",caption ="Resumo das evidências — candidatas para a regressão") |>kable_styling(bootstrap_options =c("striped", "hover"),full_width =TRUE) |>row_spec(which(str_starts(evidencias$`Incluir?`, "✅")),background ="#EAF3DE") |>row_spec(which(str_starts(evidencias$`Incluir?`, "⚠️")),background ="#FAEEDA")```## Checando colinearidade entre candidatas```{r}#| label: colinearidade#| fig-cap: "Heatmap de correlação entre candidatas numéricas"library(corrplot)vars_modelo <-c("sale_price", "gr_liv_area", "total_bsmt_sf","garage_area", "year_built", "year_remod_add","lot_area", "fireplaces")M <- ames |>select(all_of(vars_modelo)) |>cor(use ="complete.obs")corrplot(M,method ="color",type ="upper",order ="hclust",tl.cex =0.85,tl.col ="#1A1A18",addCoef.col ="#1A1A18",number.cex =0.70,col =colorRampPalette(c("#C04828", "white", "#185FA5"))(200),diag =FALSE,mar =c(0, 0, 1, 0),title ="Correlações entre candidatas ao modelo")```::: callout-warning## ⚠️ Atenção: year_built × year_remod_addCorrelação de \~0.59 entre as duas variáveis de ano. Incluir ambas pode gerar multicolinearidade. Na próxima aula decidiremos se criamos uma variável derivada (`idade_imovel = yr_sold - year_built`) ou mantemos apenas uma delas.:::## Preparando o dataset para a regressão```{r}#| label: dataset-modeloames_modelo <- ames |>mutate(# Variável derivada: idade no momento da vendaidade_imovel =as.integer(yr_sold) +2005- year_built,# Qualidade como numérica (1–10) para a regressãooverall_qual_num =as.integer(overall_qual),# Bairro agrupado em 3 faixas de preço medianofaixa_bairro =case_when( neighborhood %in%c("NridgHt", "NoRidge", "StoneBr","Timber", "Veenker") ~"Alto", neighborhood %in%c("CollgCr", "Crawfor", "ClearCr","Somerst", "Blmngtn", "Gilbert","NWAmes", "SawyerW") ~"Médio",TRUE~"Popular" ),faixa_bairro =factor(faixa_bairro,levels =c("Popular", "Médio", "Alto")) ) |>select(sale_price, gr_liv_area, overall_qual_num, total_bsmt_sf, garage_area, idade_imovel, faixa_bairro, central_air, fireplaces, lot_area)glimpse(ames_modelo)``````{r}#| label: preview-selecaocat("Variáveis selecionadas para o modelo (próxima aula):\n\n")cat(" Resposta : sale_price\n")cat(" Numéricas : gr_liv_area, overall_qual_num, total_bsmt_sf,\n")cat(" garage_area, idade_imovel, fireplaces, lot_area\n")cat(" Categóricas: faixa_bairro (3 níveis), central_air (Y/N)\n\n")cat(" Total de observações :", nrow(ames_modelo), "\n")cat(" Variáveis candidatas :", ncol(ames_modelo) -1, "\n")```------------------------------------------------------------------------# Síntese da aula {.unnumbered}::: {.callout-note icon="false"}## 🔁 Da pergunta de negócios ao modelo``` Pergunta → Hipótese → Teste → Evidência → Decisão de variável```| Pergunta | Teste usado | Conclusão | Entra no modelo? ||------------------|------------------|------------------|------------------|| Área influencia preço? | Pearson | r = 0.71 \*\*\* | ✅ `gr_liv_area` || Qualidade importa? | Spearman | ρ = 0.80 \*\*\* | ✅ `overall_qual` || Ar-cond. faz diferença? | Mann-Whitney | r_rb = 0.45 \*\*\* | ✅ `central_air` || Bairro diferencia preço? | Kruskal-Wallis | η² = 0.38 \*\*\* | ✅ `faixa_bairro` || Ano e ano de reforma juntos? | Correlação cruzada | r = 0.59 ⚠️ | ➡️ Criar `idade_imovel` |**Próxima aula:** construir, diagnosticar e interpretar o modelo de regressão linear com essas variáveis.:::------------------------------------------------------------------------```{r}#| label: salvar-dados#| echo: true# Salvar o dataset limpo para usar na próxima aulasaveRDS(ames_modelo, "ames_modelo.rds")cat("ames_modelo.rds salvo com", nrow(ames_modelo),"linhas e", ncol(ames_modelo), "colunas.\n")``````{r}#| label: session-info#| code-fold: true#| code-summary: "Informações da sessão R"sessionInfo()```# Modelo de Regressão Linear```{r}ames_modelo <-readRDS("ames_modelo.rds")```## Modelo 1: apenas com variáveis numéricas```{r}# ── 2. Modelo 1: apenas variáveis numéricas ──────────────────────────────mod1 <-lm(sale_price ~ gr_liv_area + overall_qual_num + total_bsmt_sf + garage_area + idade_imovel + fireplaces + lot_area,data = ames_modelo)summary(mod1)```## Modelo 2: Adicionando variáveis categóricas```{r}# ── 3. Modelo 2: adicionando variáveis categóricas ───────────────────────mod2 <-lm(sale_price ~ gr_liv_area + overall_qual_num + total_bsmt_sf + garage_area + idade_imovel + fireplaces + lot_area + faixa_bairro + central_air,data = ames_modelo)summary(mod2)```## Comparando os dois modelos```{r}# ── 4. Comparar os dois modelos ──────────────────────────────────────────tibble(Modelo =c("Mod 1 — só numéricas", "Mod 2 — + categóricas"),R2 =c(summary(mod1)$r.squared,summary(mod2)$r.squared),R2_adj =c(summary(mod1)$adj.r.squared,summary(mod2)$adj.r.squared),RMSE =c(sigma(mod1), sigma(mod2)),AIC =c(AIC(mod1), AIC(mod2))) |>mutate(across(c(R2, R2_adj), ~round(., 3)),across(c(RMSE, AIC), ~round(.))) |>kbl(align ="lrrrc") |>kable_styling(bootstrap_options =c("striped", "hover"),full_width =FALSE)``````{r}# ── 5. Tabela de coeficientes interpretável ──────────────────────────────tidy(mod2, conf.int =TRUE) |>filter(term !="(Intercept)") |>mutate(estimate =round(estimate),conf.low =round(conf.low),conf.high =round(conf.high),p.value =round(p.value, 4),sig =case_when( p.value <0.001~"***", p.value <0.01~"**", p.value <0.05~"*",TRUE~"ns" ),interpretacao =case_when( term =="gr_liv_area"~"Cada sqft adicional de área habitável", term =="overall_qual_num"~"Cada ponto a mais na qualidade geral (1–10)", term =="total_bsmt_sf"~"Cada sqft adicional de porão", term =="garage_area"~"Cada sqft adicional de garagem", term =="idade_imovel"~"Cada ano a mais de idade do imóvel", term =="fireplaces"~"Cada lareira adicional", term =="lot_area"~"Cada sqft adicional de terreno", term =="faixa_bairroMédio"~"Bairro Médio vs. Popular (ref.)", term =="faixa_bairroAlto"~"Bairro Alto vs. Popular (ref.)", term =="central_airY"~"Presença de ar-condicionado central",TRUE~ term ) ) |>select(interpretacao, estimate, conf.low, conf.high, p.value, sig) |>kbl(col.names =c("Variável", "Coef. (US$)", "IC 95% inf.", "IC 95% sup.", "p-valor", "Sig."),align ="lrrrcc",caption ="Coeficientes do Modelo 2 — efeito marginal no preço de venda") |>kable_styling(bootstrap_options =c("striped", "hover"),full_width =TRUE) |>row_spec(which(tidy(mod2) |>filter(term !="(Intercept)") |>pull(p.value) <0.001),background ="#EAF3DE")```## Diagnóstico```{r}# ── 6. Diagnóstico: os 4 gráficos clássicos ──────────────────────────────par(mfrow =c(2, 2))plot(mod2)par(mfrow =c(1, 1))``````{r}# ── 7. Diagnóstico em ggplot2 (versão para apresentação) ─────────────────diag <-augment(mod2)# Resíduos vs. ajustadosp1 <- diag |>ggplot(aes(x = .fitted, y = .resid)) +geom_point(alpha =0.25, size =1.2, color ="#185FA5") +geom_hline(yintercept =0, color ="#C04828",linewidth =0.9, linetype ="dashed") +geom_smooth(method ="loess", se =FALSE,color ="#0F6E56", linewidth =0.8) +scale_x_continuous(labels =dollar_format(prefix ="US$",scale =1e-3, suffix ="k")) +scale_y_continuous(labels =dollar_format(prefix ="US$",scale =1e-3, suffix ="k")) +labs(title ="Resíduos vs. Ajustados",subtitle ="Ideal: pontos aleatórios em torno de zero",x ="Valores ajustados", y ="Resíduo") +theme_minimal(base_size =11) +theme(plot.title =element_text(face ="bold"))# Q-Q plot dos resíduosp2 <- diag |>ggplot(aes(sample = .std.resid)) +stat_qq(alpha =0.3, color ="#185FA5", size =1.2) +stat_qq_line(color ="#C04828", linewidth =0.9) +labs(title ="Q-Q Plot dos resíduos padronizados",subtitle ="Ideal: pontos sobre a diagonal",x ="Quantis teóricos", y ="Quantis observados") +theme_minimal(base_size =11) +theme(plot.title =element_text(face ="bold"))# Escala-localização (homocedasticidade)p3 <- diag |>ggplot(aes(x = .fitted, y =sqrt(abs(.std.resid)))) +geom_point(alpha =0.25, size =1.2, color ="#534AB7") +geom_smooth(method ="loess", se =FALSE,color ="#C04828", linewidth =0.8) +scale_x_continuous(labels =dollar_format(prefix ="US$",scale =1e-3, suffix ="k")) +labs(title ="Escala-Localização",subtitle ="Ideal: linha horizontal (homocedasticidade)",x ="Valores ajustados",y =expression(sqrt("|resíduo padronizado|"))) +theme_minimal(base_size =11) +theme(plot.title =element_text(face ="bold"))# Resíduos vs. leveragep4 <- diag |>ggplot(aes(x = .hat, y = .std.resid)) +geom_point(alpha =0.3, size =1.2, color ="#854F0B") +geom_hline(yintercept =c(-2, 0, 2), linetype =c("dashed","solid","dashed"),color =c("#C04828","#9C9A92","#C04828"), linewidth =0.7) +labs(title ="Resíduos vs. Leverage",subtitle ="Pontos acima de |2| = observações influentes",x ="Leverage (h)", y ="Resíduo padronizado") +theme_minimal(base_size =11) +theme(plot.title =element_text(face ="bold"))(p1 + p2) / (p3 + p4) +plot_annotation(title ="Diagnóstico do Modelo 2",theme =theme(plot.title =element_text(face ="bold", size =14)) )```## Previsão: Exemplo Prático```{r}# ── 8. Previsão: exemplos práticos ───────────────────────────────────────novos_imoveis <-tibble(gr_liv_area =c(1500, 2000, 2500),overall_qual_num =c(5, 7, 9),total_bsmt_sf =c(800, 1000, 1400),garage_area =c(400, 480, 600),idade_imovel =c(30, 15, 5),fireplaces =c(0, 1, 2),lot_area =c(8000, 9500, 11000),faixa_bairro =factor(c("Popular", "Médio", "Alto"),levels =c("Popular", "Médio", "Alto")),central_air =c("N", "Y", "Y"))predict(mod2, newdata = novos_imoveis,interval ="prediction", level =0.95) |>as_tibble() |>mutate(perfil =c("Imóvel popular", "Imóvel médio", "Imóvel alto padrão"),across(c(fit, lwr, upr),~dollar(round(.), prefix ="US$", big.mark =",")) ) |>select(perfil, fit, lwr, upr) |>kbl(col.names =c("Perfil", "Previsão", "IC 95% inf.", "IC 95% sup."),align ="lrrr",caption ="Previsão de preço para 3 perfis de imóvel") |>kable_styling(bootstrap_options =c("striped", "hover"),full_width =FALSE)```## Salvando o modelo final```{r}# SalvarsaveRDS(mod2, "modelo_preco_imovel.rds")# Carregar (no Shiny ou em qualquer outro script)# mod2 <- readRDS("modelo_preco_imovel.rds")``````{r}# Salvar os dois juntos em uma lista — um único arquivosaveRDS(list(modelo = mod2, dados = ames_modelo),"/Users/edneideramalho/Desktop/estatistica_para_ciencia_de_dados/EDA/artefatos_modelo.rds")# No app.R — carregar uma vez, fora de server()artefatos <-readRDS("artefatos_modelo.rds")modelo <- artefatos$modeloames_ref <- artefatos$dados # para extrair níveis dos fatores se necessário```