source("../../utils/requirements.R")
library(AmesHousing); set.seed(42); library(dplyr); library(broom)
ames <- make_ames()
idx <- sample(seq_len(nrow(ames)), size = floor(.8*nrow(ames)))
train <- ames[idx,]; test <- ames[-idx,]
mod <- lm(Sale_Price ~ Gr_Liv_Area + Overall_Qual + Year_Built + Full_Bath + Garage_Cars, data=train)
mod_small <- lm(Sale_Price ~ Gr_Liv_Area + Overall_Qual + Year_Built, data=train)Mission 2 — Tester & interpréter
Objectifs
Tester des hypothèses (t-tests sur coefficients, test F global et modèles emboîtés).
Intervalles : construire et interpréter des intervalles de confiance (IC) et de prédiction (IP).
Évaluer la calibration et la couverture des IP sur l’échantillon test.
Analyser l’erreur par sous-groupes (robustesse et équité de base).
1) Tests d’hypothèses sur les coefficients
sum_mod <- summary(mod)
sum_mod$coefficients Estimate Std. Error t value Pr(>|t|)
(Intercept) -881183.00199 66913.101296 -13.169065 2.967990e-38
Gr_Liv_Area 56.74416 2.104504 26.963206 1.341430e-139
Overall_QualPoor 23909.61553 20290.439629 1.178369 2.387700e-01
Overall_QualFair 26465.61196 18198.854053 1.454246 1.460128e-01
Overall_QualBelow_Average 38511.36847 17334.313212 2.221684 2.640040e-02
Overall_QualAverage 51976.79659 17211.078719 3.019962 2.555518e-03
Overall_QualAbove_Average 58489.05809 17263.901408 3.387940 7.159023e-04
Overall_QualGood 78020.81605 17366.424861 4.492624 7.378771e-06
Overall_QualVery_Good 120614.34000 17487.957022 6.896994 6.816907e-12
Overall_QualExcellent 199510.31713 17861.122971 11.170088 2.941930e-28
Overall_QualVery_Excellent 242212.29392 18922.086251 12.800507 2.641965e-36
Year_Built 448.30307 33.296147 13.464112 7.550363e-40
Full_Bath -2792.26431 1878.872594 -1.486138 1.373779e-01
Garage_Cars 13870.75392 1257.341923 11.031807 1.278968e-27
Réflexion ciblée :
Formulez \(H_0\) et \(H_1\) pour \(\beta_{Year\_Built}\). Interprétez le t et la p-value au seuil 5%.
Quelle(s) variable(s) reste(nt) non significative(s)? Les conservez-vous quand même? Justifiez (domaine, colinéarité, coût d’erreur, etc.).
2) Test global et modèles emboîtés
# F global (déjà dans summary(mod) via statistic)
anova(mod_small, mod)Analysis of Variance Table
Model 1: Sale_Price ~ Gr_Liv_Area + Overall_Qual + Year_Built
Model 2: Sale_Price ~ Gr_Liv_Area + Overall_Qual + Year_Built + Full_Bath +
Garage_Cars
Res.Df RSS Df Sum of Sq F Pr(>F)
1 2332 2.8789e+12
2 2330 2.7348e+12 2 1.4413e+11 61.401 < 2.2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
À faire (équipe) :
Interprétez le test ANOVA ci-dessus : le modèle complet apporte-t-il un gain significatif par rapport au modèle réduit?
Calculez et comparez AIC/BIC pour
modetmod_small. Quel critère privilégieriez-vous ici et pourquoi?
AIC(mod_small, mod) df AIC
mod_small 13 55735.13
mod 15 55618.74
BIC(mod_small, mod) df BIC
mod_small 13 55810.00
mod 15 55705.13
3) Intervalles de confiance et de prédiction
# Moyennes pour les numériques
num_means <- train %>% summarise(
Gr_Liv_Area = mean(Gr_Liv_Area, na.rm = TRUE),
Year_Built = mean(Year_Built, na.rm = TRUE),
Full_Bath = mean(Full_Bath, na.rm = TRUE),
Garage_Cars = mean(Garage_Cars, na.rm = TRUE)
)
# Modalité la plus fréquente pour le facteur Overall_Qual
mode_qual <- train %>% summarise(
Overall_Qual = names(which.max(table(Overall_Qual)))
) %>% mutate(Overall_Qual = factor(Overall_Qual, levels = levels(train$Overall_Qual)))
# Maison type
new0 <- dplyr::bind_cols(num_means, mode_qual)
# IC
predict(mod, newdata = new0, interval = "confidence") fit lwr upr
1 160220.6 157199.6 163241.7
# IP sur l’échantillon test
pred_pi <- predict(mod, newdata = test, interval = "prediction")
head(pred_pi) fit lwr upr
1 180648.2 113338.9 247957.5
2 170506.0 103198.6 237813.3
3 271895.4 204579.2 339211.5
4 405288.4 337646.4 472930.3
5 300636.2 233315.5 367956.8
6 231996.0 164646.9 299345.1
Réflexion interprétation :
Différence IC vs IP : que quantifient-ils, et pourquoi l’IP est-il plus large?
À quoi sert un IP pour un décideur (ex. courtier, évaluateur)? Donnez un exemple concret.
4) Couverture des IP et calibration
# Taux de couverture 95% des IP sur test
inside <- (test$Sale_Price >= pred_pi[ ,"lwr"]) & (test$Sale_Price <= pred_pi[ ,"upr"])
coverage <- mean(inside)
width <- mean(pred_pi[ ,"upr"] - pred_pi[ ,"lwr"])
knitr::kable(data.frame(Couverture_IP95 = coverage, Largeur_moy_IP = width),
caption = "Couverture et largeur moyenne des intervalles de prédiction (test)")| Couverture_IP95 | Largeur_moy_IP |
|---|---|
| 0.9624573 | 134744.5 |
# Plot calibration : observé vs prédit
plot(pred_pi[ ,"fit"], test$Sale_Price,
xlab = "Prix prédit", ylab = "Prix observé", main = "Calibration: observé vs prédit")
abline(0, 1)Analyse critique :
La couverture est-elle proche de 95%? Si non, quelles raisons possibles (mauvaise spécification, hétéroscédasticité, non-normalité, outliers…)?
La pente de calibration semble-t-elle proche de 1? Y a-t-il biais systématique (sous/sur-prédiction)?
5) Erreurs par sous-groupes (robustesse)
err <- test$Sale_Price - pred_pi[ ,"fit"]
# Stratification simple : quartiles de surface et niveaux de qualité
q_area <- cut(test$Gr_Liv_Area, breaks = quantile(test$Gr_Liv_Area, probs = seq(0,1,0.25), na.rm=TRUE), include.lowest=TRUE)
by_grp <- test %>% mutate(err = err, q_area = q_area) %>%
group_by(q_area, Overall_Qual) %>% summarise(MAE = mean(abs(err)), RMSE = sqrt(mean(err^2)), .groups='drop')
knitr::kable(by_grp, caption = "Erreurs par sous-groupes (aire habitable en quartiles × qualité globale)")| q_area | Overall_Qual | MAE | RMSE |
|---|---|---|---|
| [520,1.13e+03] | Poor | 30364.570 | 38651.540 |
| [520,1.13e+03] | Fair | 13414.979 | 17555.484 |
| [520,1.13e+03] | Below_Average | 13564.462 | 18418.238 |
| [520,1.13e+03] | Average | 13883.661 | 17991.222 |
| [520,1.13e+03] | Above_Average | 13903.862 | 17361.991 |
| [520,1.13e+03] | Good | 20736.199 | 21570.278 |
| (1.13e+03,1.42e+03] | Fair | 16939.169 | 19902.571 |
| (1.13e+03,1.42e+03] | Below_Average | 21017.884 | 24903.919 |
| (1.13e+03,1.42e+03] | Average | 13765.626 | 17648.594 |
| (1.13e+03,1.42e+03] | Above_Average | 14439.528 | 17670.036 |
| (1.13e+03,1.42e+03] | Good | 14201.040 | 17231.101 |
| (1.13e+03,1.42e+03] | Very_Good | 31862.465 | 37032.937 |
| (1.42e+03,1.72e+03] | Below_Average | 33394.954 | 39938.912 |
| (1.42e+03,1.72e+03] | Average | 21226.006 | 27886.582 |
| (1.42e+03,1.72e+03] | Above_Average | 16698.841 | 24631.618 |
| (1.42e+03,1.72e+03] | Good | 23702.029 | 29274.231 |
| (1.42e+03,1.72e+03] | Very_Good | 29526.155 | 41360.485 |
| (1.42e+03,1.72e+03] | Excellent | 32939.308 | 33972.903 |
| (1.72e+03,4.68e+03] | Fair | 2609.718 | 2609.718 |
| (1.72e+03,4.68e+03] | Below_Average | 58601.132 | 58601.456 |
| (1.72e+03,4.68e+03] | Average | 30841.210 | 39331.843 |
| (1.72e+03,4.68e+03] | Above_Average | 24321.604 | 30690.056 |
| (1.72e+03,4.68e+03] | Good | 28348.812 | 35079.171 |
| (1.72e+03,4.68e+03] | Very_Good | 37801.170 | 50260.929 |
| (1.72e+03,4.68e+03] | Excellent | 52026.385 | 63023.133 |
| (1.72e+03,4.68e+03] | Very_Excellent | 132702.685 | 178022.285 |
Question d’enquête :
Identifiez un sous-groupe avec une erreur nettement plus élevée. Donnez une hypothèse explicative (non-linéarité, interaction manquante, variable omise…).
Proposez une action concrète (transformation, terme quadratique, interaction, variable additionnelle) pour la Mission 3.
6) Comparaison de modèles
# Performance globale simple (pour mémoire) :
pred <- pred_pi[ ,"fit"]
rmse <- sqrt(mean((pred - test$Sale_Price)^2))
mae <- mean(abs(pred - test$Sale_Price))
knitr::kable(data.frame(Model=c("Complet","Simple"),
RMSE=c(rmse, sqrt(mean((predict(mod_small, test) - test$Sale_Price)^2))),
MAE =c(mae, mean(abs(predict(mod_small, test) - test$Sale_Price)))),
caption = "Comparaison rapide de performance (test)")| Model | RMSE | MAE |
|---|---|---|
| Complet | 34713.31 | 22622.28 |
| Simple | 35150.98 | 23088.00 |
Décision modèle :
- Utilisez ANOVA + AIC/BIC + calibration/couverture (pas seulement RMSE/MAE) pour trancher entre
modetmod_small. Justifiez la traçabilité de votre choix.
Questions
Q1. Testez \(H_0\!:\;\beta_{Year\_Built}=0\) (5%). Concluez et interprétez dans le contexte.
Q2. Le test ANOVA conclut-il à un apport significatif de
Full_BathetGarage_Carspris ensemble? Décision?Q3. La couverture empirique des IP95 est-elle satisfaisante? Si non, quelle modification du modèle proposeriez-vous?
Q4. Identifiez un sous-groupe à erreur élevée et proposez une amélioration.
Pour aller plus loin
Calculez un IC pour la moyenne conditionnelle d’une maison type (définissez explicitement la maison) et expliquez la différence avec un IP pour cette même maison.
Montrez un exemple où AIC préfère
modmais BIC préfèremod_small. Laquelle des deux décisions retiendriez-vous ici et pourquoi (taille d’échantillon, parcimonie, objectif)?Implémentez une validation croisée K-fold maison (sans nouveaux packages) et comparez RMSE moyen de
modvsmod_small.