Tutoriel du jour 1

Expliquer et prédire le prix des propriétés d’Ames

Objectif

Ce tutoriel construit un modèle linéaire multiple du prix de vente de propriétés résidentielles. Il suit la même progression que la présentation et les trois missions:

  1. comprendre l’unité statistique et la réponse;
  2. choisir un petit ensemble de prédicteurs;
  3. comprendre comment les coefficients sont estimés;
  4. interpréter les coefficients et leur incertitude;
  5. produire une prédiction honnête;
  6. vérifier les hypothèses, l’influence et la sensibilité;
  7. comparer le prix brut et le prix logarithmique.

Les données décrivent 2 930 ventes à Ames, en Iowa (D. De Cock, 2011; M. Kuhn, 2020).

Carte théorique de la matinée

Une régression linéaire multiple ne se résume pas à la commande lm(). Elle relie cinq décisions qui doivent rester cohérentes:

Décision Question à résoudre
objectif veut-on décrire, expliquer une association ou prédire?
représentation quelle est l’unité statistique et comment coder les variables?
structure quelle forme de moyenne conditionnelle paraît plausible?
estimation comment choisir les coefficients et quantifier leur incertitude?
évaluation quelles hypothèses et quelles données permettent de faire confiance au résultat?

Le parcours commence par une association conditionnelle, puis examine une prédiction individuelle et les diagnostics. Une analyse causale demanderait une stratégie supplémentaire concernant le plan d’étude, les variables de confusion et les hypothèses d’identification.

Préparer l’environnement

library(tidyverse)
library(AmesHousing)
library(broom)
library(performance)
library(scales)
library(patchwork)

theme_set(theme_minimal(base_size = 14))

Chaque paquet possède ici un rôle précis. AmesHousing fournit les données, tidyverse prépare et visualise, broom transforme les sorties du modèle en tableaux, performance aide au diagnostic et patchwork combine les graphiques.

Charger et simplifier les données

make_ames() fournit le prix de vente et 80 caractéristiques. Nous conservons quelques variables faciles à expliquer, tout en gardant deux extensions possibles.

ames_complet <- make_ames()

niveaux_qualite_attendus <- c(
  "Very_Poor", "Poor", "Fair", "Below_Average", "Average",
  "Above_Average", "Good", "Very_Good", "Excellent",
  "Very_Excellent"
)

stopifnot(identical(
  levels(ames_complet$Overall_Qual),
  niveaux_qualite_attendus
))

ames <- ames_complet |>
  transmute(
    sale_price = Sale_Price,
    gr_liv_area = Gr_Liv_Area,
    overall_qual = as.integer(Overall_Qual),
    year_built = Year_Built,
    garage_cars = Garage_Cars,
    full_bath = Full_Bath,
    neighborhood = Neighborhood
  )

dim(ames_complet)
[1] 2930   81
glimpse(ames)
Rows: 2,930
Columns: 7
$ sale_price   <int> 215000, 105000, 172000, 244000, 189900, 195500, 213500, 1…
$ gr_liv_area  <int> 1656, 896, 1329, 2110, 1629, 1604, 1338, 1280, 1616, 1804…
$ overall_qual <int> 6, 5, 6, 7, 5, 6, 8, 8, 8, 7, 6, 6, 6, 7, 8, 8, 8, 9, 4, …
$ year_built   <int> 1960, 1961, 1958, 1968, 1997, 1998, 2001, 1992, 1995, 199…
$ garage_cars  <dbl> 2, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 3, 2, …
$ full_bath    <int> 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 3, 2, 1, 1, …
$ neighborhood <fct> North_Ames, North_Ames, North_Ames, North_Ames, Gilbert, …

Une ligne est une vente résidentielle. sale_price est en dollars américains et gr_liv_area est la surface habitable au-dessus du sol, en pieds carrés. overall_qual est un score ordinal de 1 à 10.

Le passage de Overall_Qual à un entier impose une hypothèse: une hausse d’un point possède le même sens moyen partout sur l’échelle. Cette simplification rend le coefficient lisible, mais elle doit être nommée.

Comprendre la réponse

ames |>
  summarise(
    ventes = n(),
    prix_median = median(sale_price),
    prix_moyen = mean(sale_price),
    prix_min = min(sale_price),
    prix_max = max(sale_price)
  )
# A tibble: 1 × 5
  ventes prix_median prix_moyen prix_min prix_max
   <int>       <dbl>      <dbl>    <int>    <int>
1   2930      160000    180796.    12789   755000
ames |>
  ggplot(aes(sale_price)) +
  geom_histogram(bins = 40, fill = "#0f7c80", color = "white") +
  scale_x_continuous(labels = label_dollar()) +
  labs(x = "Prix de vente", y = "Nombre de ventes")

La distribution est asymétrique vers la droite. Cela n’interdit pas d’ajuster un modèle en dollars. La forme de la réponse n’est pas, à elle seule, une hypothèse de la régression. Elle invite cependant à examiner attentivement les résidus et à comparer une transformation logarithmique.

Explorer avant de modéliser

p_surface <- ames |>
  ggplot(aes(gr_liv_area, sale_price)) +
  geom_point(alpha = 0.25, color = "#277da1") +
  geom_smooth(method = "lm", se = TRUE, color = "#b5222e") +
  scale_y_continuous(labels = label_dollar()) +
  labs(x = "Surface habitable, pieds carrés", y = "Prix de vente")

p_qualite <- ames |>
  ggplot(aes(overall_qual, sale_price)) +
  geom_jitter(width = 0.15, alpha = 0.2, color = "#0f7c80") +
  geom_smooth(method = "lm", se = TRUE, color = "#b5222e") +
  scale_y_continuous(labels = label_dollar()) +
  labs(x = "Qualité globale, de 1 à 10", y = "Prix de vente")

p_surface + p_qualite

Les graphiques suggèrent des associations positives, mais montrent aussi quelques propriétés très grandes ou très coûteuses. Un graphique bivarié ne permet pas d’isoler l’association propre à chaque caractéristique. C’est précisément le rôle de la comparaison conditionnelle dans le modèle multiple.

Du modèle de population au modèle ajusté

Le modèle théorique décrit la moyenne du prix pour des propriétés possédant certaines caractéristiques:

\[ E(Y_i \mid X_i) = \beta_0 + \beta_1 X_{i1} + \cdots + \beta_p X_{ip}. \]

Les paramètres \(\beta_j\) appartiennent à une population conceptuelle et sont inconnus. Le modèle ajusté les remplace par des estimations \(\widehat{\beta}_j\) calculées à partir des ventes observées.

Pour chaque vente, le modèle produit une valeur ajustée \(\widehat{y}_i\) et un résidu:

\[ e_i = y_i - \widehat{y}_i. \]

Le résidu n’est pas nécessairement une erreur de saisie. Il représente la partie du prix que la moyenne conditionnelle du modèle n’explique pas pour cette vente.

Le principe des moindres carrés

La fonction lm() choisit les coefficients qui minimisent la somme des résidus au carré:

\[ \sum_{i=1}^{n}(y_i - \widehat{y}_i)^2. \]

Élever les résidus au carré empêche les erreurs positives et négatives de s’annuler et accorde davantage de poids aux grandes erreurs. Cette propriété rend aussi l’ajustement sensible aux observations extrêmes, ce qui motive l’analyse d’influence plus loin.

Note

« Linéaire » signifie que la moyenne est linéaire dans les coefficients. Un modèle linéaire peut contenir un logarithme, un terme quadratique ou une interaction. Ces choix changent toutefois la forme et l’interprétation du modèle.

Ajuster le modèle commun

modele_prix <- lm(
  sale_price ~ gr_liv_area + overall_qual + year_built + garage_cars,
  data = ames
)

tidy(modele_prix, conf.int = TRUE)
# A tibble: 5 × 7
  term          estimate std.error statistic   p.value  conf.low conf.high
  <chr>            <dbl>     <dbl>     <dbl>     <dbl>     <dbl>     <dbl>
1 (Intercept)  -820149.   60058.       -13.7 3.32e- 41 -937908.  -702389. 
2 gr_liv_area       57.3      1.82      31.5 4.59e-188      53.7      60.8
3 overall_qual   23960.     777.        30.8 3.85e-181   22437.    25483. 
4 year_built       377.      31.6       11.9 4.17e- 32     315.      439. 
5 garage_cars    14723.    1275.        11.5 3.43e- 30   12223.    17223. 
glance(modele_prix)
# A tibble: 1 × 12
  r.squared adj.r.squared  sigma statistic p.value    df  logLik    AIC    BIC
      <dbl>         <dbl>  <dbl>     <dbl>   <dbl> <dbl>   <dbl>  <dbl>  <dbl>
1     0.765         0.765 38761.     2379.       0     4 -35111. 70234. 70270.
# ℹ 3 more variables: deviance <dbl>, df.residual <int>, nobs <int>

Le modèle s’écrit:

\[ Y_i = \beta_0 + \beta_1 S_i + \beta_2 Q_i + \beta_3 A_i + \beta_4 G_i + \varepsilon_i, \]

\(Y_i\) est le prix, \(S_i\) la surface, \(Q_i\) la qualité globale, \(A_i\) l’année de construction et \(G_i\) la capacité du garage.

tidy() décrit les coefficients individuels. glance() résume l’ajustement entier. Le \(R^2\) mesure la proportion de variation observée expliquée dans ces données. Le \(R^2\) ajusté ajoute une pénalité liée au nombre de termes. Aucun des deux ne mesure directement l’erreur sur de nouvelles ventes.

Interpréter les coefficients

Les valeurs numériques sont calculées directement à partir du modèle.

coef_surface <- coef(modele_prix)[["gr_liv_area"]]
coef_qualite <- coef(modele_prix)[["overall_qual"]]
coef_annee <- coef(modele_prix)[["year_built"]]
coef_garage <- coef(modele_prix)[["garage_cars"]]

À qualité, année de construction et capacité du garage identiques, 100 pieds carrés supplémentaires sont associés en moyenne à environ $5,727 de plus sur le prix de vente.

À surface, année de construction et capacité du garage identiques, un point supplémentaire de qualité globale est associé en moyenne à environ $23,960 de plus.

Ces phrases décrivent des associations conditionnelles. Elles ne démontrent pas qu’une intervention sur une caractéristique causerait exactement la variation estimée.

Note

L’ordonnée à l’origine correspondrait notamment à une surface nulle et à une année de construction égale à zéro. Elle est nécessaire au calcul, mais n’a pas ici d’interprétation immobilière utile. Un centrage des variables pourrait lui donner une référence plus plausible.

Coefficient, intervalle et valeur p

Ces trois éléments répondent à des questions différentes:

  1. le coefficient donne la direction et la taille estimée dans les unités du modèle;
  2. l’intervalle de confiance décrit une plage de valeurs compatibles avec les données et les hypothèses;
  3. la valeur p évalue la compatibilité des données avec une hypothèse nulle, généralement \(\beta_j = 0\).

Une petite valeur p ne mesure ni la taille de l’association, ni son importance pratique, ni la qualité des prédictions. Avec 2 930 ventes, de petites associations peuvent être estimées avec précision. Il faut donc lire ensemble l’estimation, l’intervalle, les unités, le contexte et les diagnostics.

Le test d’un coefficient est conditionnel aux autres termes du modèle. Modifier la formule peut modifier l’estimation, son erreur-type et sa valeur p, même lorsque les données ne changent pas.

Vérifier la colinéarité

check_collinearity(modele_prix)
# Check for Multicollinearity

Low Correlation

         Term  VIF   VIF 95% CI adj. VIF Tolerance Tolerance 95% CI
  gr_liv_area 1.64 [1.57, 1.73]     1.28      0.61     [0.58, 0.64]
 overall_qual 2.34 [2.22, 2.48]     1.53      0.43     [0.40, 0.45]
   year_built 1.78 [1.69, 1.88]     1.33      0.56     [0.53, 0.59]
  garage_cars 1.84 [1.75, 1.94]     1.36      0.54     [0.52, 0.57]

Les facteurs d’inflation de la variance sont tous inférieurs à 2.3 dans ce modèle. Ils ne signalent pas une colinéarité sévère. Cette vérification ne garantit toutefois ni la bonne forme fonctionnelle ni l’absence de variables omises.

La colinéarité concerne surtout la capacité à isoler des coefficients. Deux prédicteurs très redondants peuvent produire une bonne prédiction globale tout en donnant des coefficients individuels instables et des intervalles larges.

Hypothèses et portée des conclusions

Les hypothèses ne sont pas une liste à cocher mécaniquement. Chacune protège certaines conclusions:

Hypothèse ou condition Pourquoi elle compte Comment l’examiner
moyenne correctement structurée évite des résidus systématiques dans certaines zones résidus contre valeurs ajustées et connaissance du domaine
variance conditionnelle à peu près constante soutient les erreurs-types et intervalles usuels étalement vertical des résidus
erreurs indépendantes évite de compter plusieurs fois la même information plan de collecte, ordre temporel, proximité spatiale ou groupes
erreurs compatibles avec l’inférence utilisée soutient les queues des intervalles diagramme quantile-quantile et résidus extrêmes
absence de colinéarité parfaite permet d’isoler les coefficients structure des variables, matrice du modèle et VIF
absence de domination individuelle soutient la stabilité de la conclusion levier, distance de Cook et sensibilité

La normalité ne concerne pas la distribution brute de sale_price. Elle concerne les erreurs conditionnelles autour de la moyenne du modèle. Les estimations par moindres carrés peuvent être calculées sans normalité, mais l’inférence usuelle est plus fragile lorsque les queues s’éloignent fortement du modèle supposé.

L’indépendance ne se lit pas uniquement dans un graphique. Des ventes provenant d’un même quartier ou d’une même période peuvent partager des caractéristiques non représentées. Le plan de collecte et la population visée font donc partie du diagnostic.

Confiance et prédiction

Considérons une propriété de 1 500 pieds carrés, de qualité 6 sur 10, construite en 2000 et dotée de deux places de garage.

propriete_type <- tibble(
  gr_liv_area = 1500,
  overall_qual = 6L,
  year_built = 2000L,
  garage_cars = 2
)

intervalle_moyenne <- predict(
  modele_prix,
  propriete_type,
  interval = "confidence"
)

intervalle_individuel <- predict(
  modele_prix,
  propriete_type,
  interval = "prediction"
)

intervalle_moyenne
       fit      lwr      upr
1 192778.7 190525.4 195031.9
intervalle_individuel
       fit    lwr      upr
1 192778.7 116744 268813.3

Les deux intervalles sont centrés sur environ $192,779. L’intervalle de confiance vise le prix moyen des propriétés possédant ces caractéristiques. L’intervalle de prédiction vise une nouvelle vente individuelle et est beaucoup plus large, car il inclut aussi la variabilité entre propriétés.

Ce que ces intervalles ne valident pas

Les deux intervalles supposent que la formule, la structure de variance et la population cible sont suffisamment appropriées. Ils ne remplacent pas une évaluation prédictive sur de nouvelles données.

Le modèle est ici ajusté et résumé avec les mêmes 2 930 ventes. Son erreur dans ces données est donc optimiste si l’on veut prédire de futures ventes. Pour évaluer la performance prédictive, il faudrait réserver un jeu test ou utiliser un rééchantillonnage qui répète la séparation entre ajustement et évaluation. Cette étape sera approfondie au jour 3.

Diagnostiquer les résidus

diagnostic_prix <- augment(modele_prix, data = ames)
p_residus <- diagnostic_prix |>
  ggplot(aes(.fitted, .resid)) +
  geom_point(alpha = 0.25, color = "#277da1") +
  geom_hline(yintercept = 0, linetype = 2) +
  geom_smooth(se = FALSE, color = "#b5222e") +
  scale_x_continuous(labels = label_dollar()) +
  scale_y_continuous(labels = label_dollar()) +
  labs(x = "Prix ajusté", y = "Résidu")

p_qq <- diagnostic_prix |>
  ggplot(aes(sample = .std.resid)) +
  stat_qq(alpha = 0.3, color = "#0f7c80") +
  stat_qq_line(color = "#b5222e") +
  labs(x = "Quantiles théoriques", y = "Résidus standardisés")

p_residus + p_qq

Le graphique des résidus doit idéalement ressembler à une bande horizontale centrée sur zéro, sans courbure claire et d’épaisseur à peu près constante. Ici, l’étalement augmente pour les prix ajustés élevés et plusieurs observations sont extrêmes.

Le diagramme quantile-quantile compare les résidus standardisés à une distribution de référence. L’écart dans les queues ne signifie pas que toute association disparaît. Il signale surtout que les erreurs-types, les tests et les intervalles usuels méritent davantage de prudence.

Examiner l’influence

ventes_influentes <- diagnostic_prix |>
  mutate(numero = row_number()) |>
  arrange(desc(.cooksd)) |>
  select(
    numero, sale_price, gr_liv_area, overall_qual,
    year_built, garage_cars, .resid, .cooksd
  )

ventes_influentes |>
  slice_head(n = 8)
# A tibble: 8 × 8
  numero sale_price gr_liv_area overall_qual year_built garage_cars   .resid
   <int>      <int>       <int>        <int>      <int>       <dbl>    <dbl>
1   1499     160000        5642           10       2008           2 -368847.
2   2181     183850        5095           10       2008           3 -328393.
3   2182     184750        4676           10       2007           3 -303120.
4   1768     755000        4316           10       1994           3  292647.
5   1761     745000        4476           10       1996           3  272730.
6   2446     625000        3627           10       1995           3  201730.
7   2451     584500        3500            9       1993           3  193217.
8   1064     615000        2470           10       2003           3  254977.
# ℹ 1 more variable: .cooksd <dbl>

Une observation peut être atypique par sa réponse, posséder un fort levier par ses prédicteurs, ou être influente parce qu’elle modifie sensiblement l’ajustement. La distance de Cook combine ces deux dernières idées.

La vente la plus influente est la ligne 1499. Elle combine une surface de 5,642 pieds carrés, une qualité de 10 sur 10 et un prix de $160,000. Elle est inhabituelle, mais son caractère extrême ne prouve pas une erreur de saisie.

Avant toute exclusion, il faut vérifier la source, comprendre le contexte, comparer les résultats avec et sans l’observation et documenter la décision.

Faire une analyse de sensibilité

numero_max <- which.max(cooks.distance(modele_prix))

modele_sans_max <- lm(
  sale_price ~ gr_liv_area + overall_qual + year_built + garage_cars,
  data = ames[-numero_max, ]
)

comparaison_coefficients <- bind_rows(
  tidy(modele_prix) |>
    mutate(analyse = "toutes les ventes"),
  tidy(modele_sans_max) |>
    mutate(analyse = "sans la vente la plus influente")
) |>
  filter(term != "(Intercept)") |>
  select(analyse, term, estimate)

comparaison_coefficients
# A tibble: 8 × 3
  analyse                         term         estimate
  <chr>                           <chr>           <dbl>
1 toutes les ventes               gr_liv_area      57.3
2 toutes les ventes               overall_qual  23960. 
3 toutes les ventes               year_built      377. 
4 toutes les ventes               garage_cars   14723. 
5 sans la vente la plus influente gr_liv_area      60.2
6 sans la vente la plus influente overall_qual  23775. 
7 sans la vente la plus influente year_built      388. 
8 sans la vente la plus influente garage_cars   13798. 

Les coefficients changent, surtout celui de la surface, mais leur signe et la conclusion générale restent stables. Une variation numérique n’est pas nécessairement une différence substantielle. L’analyse de sensibilité informe le verdict; elle ne justifie pas à elle seule une suppression.

Comparer une échelle logarithmique

modele_log <- lm(
  log(sale_price) ~ log(gr_liv_area) + overall_qual +
    year_built + garage_cars,
  data = ames
)

tidy(modele_log, conf.int = TRUE)
# A tibble: 5 × 7
  term             estimate std.error statistic   p.value conf.low conf.high
  <chr>               <dbl>     <dbl>     <dbl>     <dbl>    <dbl>     <dbl>
1 (Intercept)       2.77     0.301         9.21 5.83e- 20  2.18      3.36   
2 log(gr_liv_area)  0.440    0.0131       33.5  1.43e-208  0.414     0.466  
3 overall_qual      0.120    0.00356      33.6  4.22e-210  0.113     0.127  
4 year_built        0.00263  0.000143     18.4  1.60e- 71  0.00235   0.00291
5 garage_cars       0.0766   0.00582      13.2  1.82e- 38  0.0652    0.0880 
glance(modele_log)
# A tibble: 1 × 12
  r.squared adj.r.squared sigma statistic p.value    df logLik    AIC    BIC
      <dbl>         <dbl> <dbl>     <dbl>   <dbl> <dbl>  <dbl>  <dbl>  <dbl>
1     0.813         0.813 0.176     3175.       0     4   927. -1843. -1807.
# ℹ 3 more variables: deviance <dbl>, df.residual <int>, nobs <int>

Dans ce modèle, le coefficient de log(gr_liv_area) est une élasticité conditionnelle. Une hausse de 1 % de la surface est associée en moyenne à une variation d’environ 0.44% du prix, à caractéristiques identiques.

diagnostic_log <- augment(modele_log, data = ames)

p_prix <- diagnostic_prix |>
  ggplot(aes(.fitted, .resid)) +
  geom_point(alpha = 0.22, color = "#277da1") +
  geom_hline(yintercept = 0, linetype = 2) +
  geom_smooth(se = FALSE, color = "#b5222e") +
  labs(title = "Prix en dollars", x = "Valeurs ajustées", y = "Résidus")

p_log <- diagnostic_log |>
  ggplot(aes(.fitted, .resid)) +
  geom_point(alpha = 0.22, color = "#0f7c80") +
  geom_hline(yintercept = 0, linetype = 2) +
  geom_smooth(se = FALSE, color = "#b5222e") +
  labs(title = "Prix logarithmique", x = "Valeurs ajustées", y = "Résidus")

p_prix + p_log

La version logarithmique produit un nuage généralement plus régulier et explique davantage de variation sur son échelle. Le choix ne doit cependant pas reposer uniquement sur le \(R^2\): l’échelle modifie la question, les unités et la manière de revenir aux dollars.

Catégories, interactions et non-linéarité

Le modèle commun reste volontairement simple. Trois extensions sont importantes à connaître:

  1. une variable nominale comme neighborhood est représentée par des indicatrices comparées à une catégorie de référence;
  2. une interaction permet à l’association entre la surface et le prix de varier selon une autre caractéristique;
  3. un terme quadratique ou une transformation permet de représenter une courbure.

Ces extensions doivent être choisies pour répondre à une question ou corriger une structure visible, pas seulement pour augmenter le \(R^2\). Elles compliquent l’interprétation et doivent faire partie de toute validation prédictive.

Ce que le modèle permet de dire

Une conclusion proportionnée est:

Parmi les 2 930 ventes observées à Ames, la surface, la qualité globale, l’année de construction et la capacité du garage sont associées au prix. Le modèle en dollars est facile à interpréter, mais ses résidus deviennent plus variables pour les propriétés coûteuses et certaines ventes sont influentes. Une transformation logarithmique améliore la régularité des diagnostics, au prix d’une interprétation différente. Le modèle peut soutenir une première estimation et une comparaison conditionnelle, mais il ne démontre pas d’effet causal et ne remplace pas une validation sur de nouvelles données.

Prolongements

  1. ajouter neighborhood comme variable catégorielle;
  2. tester une interaction entre surface et qualité;
  3. séparer apprentissage et test pour mesurer l’erreur hors échantillon;
  4. comparer le modèle linéaire à un modèle plus flexible au jour 3.

Lectures complémentaires

Pour consolider la théorie

  1. James et ses collègues, An Introduction to Statistical Learning, chapitre 3. Une présentation accessible de la régression simple et multiple, de l’inférence, des variables qualitatives et des extensions du modèle linéaire (G. James et al., 2021).
  2. OpenIntro, chapitre 7 sur la régression linéaire multiple. Une lecture plus détaillée des paramètres, des moindres carrés, des tests, des intervalles et des prédicteurs catégoriels.
  3. Documentation officielle de lm(). La référence technique pour les formules, les contrastes, les valeurs manquantes et les composantes retournées par R.

Pour prolonger le travail avec Ames et la modélisation

  1. Kuhn et Silge, présentation des données Ames. Une exploration structurée du jeu et de sa préparation.
  2. Kuhn et Silge, fondamentaux des modèles dans R. Une explication des formules, objets ajustés, prédictions et conventions de base R.
  3. Kuhn et Silge, flux de modélisation. Une introduction à la séparation entre préparation, ajustement et évaluation, ainsi qu’à la validation sur des données réservées (M. Kuhn et J. Silge, 2022).
  4. De Cock, article original sur les données Ames. La source à consulter pour comprendre la construction du jeu et son objectif pédagogique (D. De Cock, 2011).

Les références

D. De Cock. (2011). Ames, Iowa: Alternative to the Boston Housing Data as an End of Semester Regression Project. Journal of Statistics Education. https://doi.org/10.1080/10691898.2011.11889627.
G. James, D. Witten, T. Hastie, et R. Tibshirani. (2021). An Introduction to Statistical Learning: With Applications in R. https://www.statlearning.com/.
M. Kuhn. (2020). AmesHousing: The Ames Iowa Housing Data. https://CRAN.R-project.org/package=AmesHousing.
M. Kuhn et J. Silge. (2022). Tidy Modeling with R. https://www.tmwr.org/.