Mission 2 — Tester & interpréter

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)

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
Note

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
Tip

À 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 mod et mod_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
Note

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 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)

Warning

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)")
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
Note

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)")
Comparaison rapide de performance (test)
Model RMSE MAE
Complet 34713.31 22622.28
Simple 35150.98 23088.00
Tip

Décision modèle :

  • Utilisez ANOVA + AIC/BIC + calibration/couverture (pas seulement RMSE/MAE) pour trancher entre mod et mod_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_Bath et Garage_Cars pris 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 mod mais BIC préfère mod_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 mod vs mod_small.