Auteur·rice

HAMLIL Mohamed

Date de publication

30 avril 2026

Introduction

Dans ce TP, on étudie la relation entre la tension artérielle et l’âge des individus. Les données ont été simulées à partir de BOUYER et al. (1995) Épidémiologie. Principes et méthodes quantitatives, Les éditions INSERM.

Objectif : Déterminer si l’âge a une influence sur la tension artérielle et éventuellement prédire la tension en fonction de l’âge.


1. Régression Linéaire Simple

1.1 Statistiques descriptives

(i) Identification des variables :

  • Variable explicative (indépendante) : l’âge (\(x\))
  • Variable expliquée (dépendante) : la tension artérielle (\(y\))

(ii) Importation des données :

Code
data = read.csv("tension.csv", header = TRUE, sep = ";", dec = ",")
head(data)
x = data$age
y = data$tension
head(x)
head(y)
A data.frame: 6 × 2
age tension
<int> <dbl>
1 44 136.1
2 44 132.0
3 55 148.8
4 44 127.5
5 41 130.2
6 51 136.7
  1. 44
  2. 44
  3. 55
  4. 44
  5. 41
  6. 51
  1. 136.1
  2. 132
  3. 148.8
  4. 127.5
  5. 130.2
  6. 136.7

(iii) Nuage de points :

Code
plot(x, y,
     main = "Tension artérielle en fonction de l'âge",
     xlab = "Âge (années)", ylab = "Tension (mmHg)",
     pch = 19, col = "steelblue")

Commentaire : Le nuage est relativement rectiligne. La régression linéaire a du sens et donnera une tendance moyennement précise de \(y\) en fonction de \(x\).

(iv) Coefficient de corrélation linéaire :

Code
r = cor(x, y)
cat("Coefficient de corrélation r =", round(r, 4))
Coefficient de corrélation r = 0.8638

Sa valeur confirme la tendance linéaire observée graphiquement.

(v) Calcul des coefficients de la droite de régression :

On cherche \(y = \hat{\beta}_1 x + \hat{\beta}_0\) par la méthode des moindres carrés :

\[\hat{\beta}_1 = \frac{\text{Cov}(x, y)}{\text{Var}(x)}, \quad \hat{\beta}_0 = \bar{y} - \hat{\beta}_1 \bar{x}\]

Code
beta1 = cov(x, y) / var(x)
beta0 = mean(y) - beta1 * mean(x)
cat("beta1 =", round(beta1, 4), "\nbeta0 =", round(beta0, 4))
beta1 = 1.5994 
beta0 = 59.127

(vi) Vérification avec lm :

Code
lm(y ~ x)

Call:
lm(formula = y ~ x)

Coefficients:
(Intercept)            x  
     59.127        1.599  

(vii) Tracé de la droite de régression :

Code
plot(x, y,
     main = "Régression linéaire : Tension ~ Âge",
     xlab = "Âge", ylab = "Tension",
     pch = 19, col = "steelblue")
abline(beta0, beta1, col = "red", lwd = 2)
legend("topleft", legend = "Droite de régression", col = "red", lwd = 2)


1.2 Résidus

Les résidus (erreurs) mesurent l’écart entre les valeurs observées et les valeurs prédites par le modèle.

(i) Calcul à la main :

Code
yajust = beta1 * x + beta0
erreurs = y - yajust

(ii) Avec la fonction lm :

Code
model = lm(y ~ x)
beta0 = model$coefficients[1]
beta1 = model$coefficients[2]
yajustes = model$fitted.values
erreurs = model$residuals

(iii) Résidus en fonction des valeurs ajustées :

Code
plot(yajustes, erreurs,
     main = "Résidus vs Valeurs ajustées",
     xlab = "Valeurs ajustées", ylab = "Résidus",
     pch = 19, col = "darkgreen")
abline(h = 0, col = "red", lty = 2)

(iv) Structure des résidus ?

Il ne semble pas y avoir de structure particulière des erreurs. C’est ce qu’on attend : dans une régression linéaire, les erreurs doivent être distribuées selon \(\mathcal{N}(0, \sigma)\) et être indépendantes de la variable explicative.

(v) Densité des résidus :

Code
plot(density(erreurs),
     main = "Densité des résidus",
     col = "blue", lwd = 2)

# Comparaison avec la densité gaussienne
discr = -15:15
norm = dnorm(discr, 0, sqrt(var(erreurs)))
lines(discr, norm, col = "red", lwd = 2, lty = 2)
legend("topright", legend = c("Résidus", "Gaussienne"),
       col = c("blue", "red"), lwd = 2, lty = c(1, 2))

(vi) QQ-plot et test de Shapiro-Wilk :

Code
qqnorm(erreurs, main = "QQ-plot des résidus")
qqline(erreurs, col = "red")

Code
shapiro.test(erreurs)

    Shapiro-Wilk normality test

data:  erreurs
W = 0.99749, p-value = 0.9296

Conclusion : Avec une p-value supérieure à 5%, on ne rejette pas la normalité des résidus.


1.3 Somme des carrés

Rappel des trois sommes fondamentales :

  • SCE (Somme des Carrés des Erreurs) : \(\sum (y_i - \hat{y}_i)^2\)
  • SCT (Somme des Carrés Totale) : \(\sum (y_i - \bar{y})^2\)
  • SCM (Somme des Carrés du Modèle) : \(\sum (\hat{y}_i - \bar{y})^2\)

(i) Calcul :

Code
SCE = sum(erreurs^2)
SCT = sum((y - mean(y))^2)
SCM = sum((yajustes - mean(y))^2)
cat("SCE =", round(SCE, 2), "\nSCT =", round(SCT, 2), "\nSCM =", round(SCM, 2))
SCE = 5785.65 
SCT = 22798.4 
SCM = 17012.76

(ii) Vérification de la décomposition :

\[SCT = SCM + SCE\]

Code
cat("SCM + SCE =", round(SCM + SCE, 2), "\nSCT =", round(SCT, 2))
SCM + SCE = 22798.4 
SCT = 22798.4

(iii) Coefficient de détermination \(R^2\) :

Code
R2 = SCM / SCT
cat("R² =", round(R2, 4))
cat("\ncor(x,y)² =", round(cor(x, y)^2, 4))
R² = 0.7462
cor(x,y)² = 0.7462

On vérifie que \(R^2 = r^2\) (carré du coefficient de corrélation).


1.4 Distribution de \(\hat{\beta}_1\)

Pour comprendre la variabilité de l’estimateur \(\hat{\beta}_1\), on simule 100 échantillons du même modèle.

(i) Simulation de 100 échantillons :

Code
set.seed(42)
age_sim = sample(40:56, replace = TRUE, 30000)
simx = matrix(age_sim, nrow = 100)

err_sim = rnorm(30000, 0, sqrt(SCE / (length(x) - 2)))
simerr = matrix(err_sim, nrow = 100)

simy = beta1 * simx + beta0 + simerr

(ii) Calcul de \(\hat{\beta}_1\) pour chaque échantillon :

Code
simbeta = numeric(100)
for (i in 1:100) {
  simbeta[i] = cov(simx[i, ], simy[i, ]) / var(simx[i, ])
}

(iii) Comparaison avec la loi de Student :

Code
plot(density((simbeta - mean(simbeta)) / sqrt(var(simbeta))),
     main = "Distribution de β̂₁ standardisé",
     col = "blue", lwd = 2)
discr = seq(-3, 3, 0.1)
lines(discr, dt(discr, 298), col = "red", lwd = 2, lty = 2)
legend("topright", legend = c("Simulations", "Student(298)"),
       col = c("blue", "red"), lwd = 2, lty = c(1, 2))


1.5 Intervalle de confiance de \(\beta_1\)

L’intervalle de confiance à 95% pour \(\beta_1\) est :

\[\hat{\beta}_1 + t_{n-2; 0.025} \cdot \hat{\sigma}_{\beta_1} \leq \beta_1 \leq \hat{\beta}_1 - t_{n-2; 0.025} \cdot \hat{\sigma}_{\beta_1}\]

Code
sbeta1 = sqrt(SCE / (398 * sum((x - mean(x))^2)))
binf = beta1 + qt(0.025, 298) * sbeta1
bsup = beta1 + qt(0.975, 298) * sbeta1
cat("Intervalle de confiance à 95% pour β₁ : [", round(binf, 4), ";", round(bsup, 4), "]")
Intervalle de confiance à 95% pour β₁ : [ 1.5074 ; 1.6914 ]

Valeurs de simbeta en dehors de l’intervalle :

Code
hors_ic = simbeta[simbeta > bsup | simbeta < binf]
cat("Nombre de valeurs hors IC :", length(hors_ic))
cat("\nValeurs :", round(hors_ic, 4))
Nombre de valeurs hors IC : 7
Valeurs : 1.707 1.4998 1.7587 1.4931 1.7066 1.5028 1.5061

On s’attend à environ 5 valeurs (5% de 100). Si c’est très différent, l’aléa informatique peut en être la cause.


2. Tests de la Régression Linéaire

2.1 Test de Fisher et ANOVA

Le coefficient de détermination \(R^2\) est inférieur à 0.8, mais cela ne signifie pas que \(X\) n’explique pas \(Y\). On teste l’hypothèse \(H_0 : \beta_1 = 0\) (le modèle n’apporte rien).

(i) Tableau d’ANOVA :

Code
anova(lm(y ~ x))
A anova: 2 × 5
Df Sum Sq Mean Sq F value Pr(>F)
<int> <dbl> <dbl> <dbl> <dbl>
x 1 17012.758 17012.75752 876.2722 9.777196e-91
Residuals 298 5785.647 19.41492 NA NA

Ou de manière équivalente :

Code
summary(aov(y ~ x))
             Df Sum Sq Mean Sq F value Pr(>F)    
x             1  17013   17013   876.3 <2e-16 ***
Residuals   298   5786      19                   
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Interprétation : La p-value (Pr(>F)) est proche de zéro → on rejette la nullité de \(\beta_1\). La variable \(X\) explique \(Y\) de manière significative.

(ii) Retrouver SCE et SCM : on les reconnaît dans les colonnes Sum Sq du tableau.

(iii) Statistique de Fisher :

\[F = \frac{SCM}{SCE / (n-2)} = \frac{298 \times SCM}{SCE}\]

Code
F_stat = 298 * SCM / SCE
cat("F observé =", round(F_stat, 2))
F observé = 876.27

(iv) Comparaison au seuil 5% :

Code
cat("Quantile F(0.95, 1, 298) =", round(qf(0.95, 1, 298), 4))
Quantile F(0.95, 1, 298) = 3.8729

(v) p-value :

Code
cat("p-value =", 1 - pf(F_stat, 1, 298))
p-value = 0

(vi) Résumé complet du modèle :

Code
summary(lm(y ~ x))

Call:
lm(formula = y ~ x)

Residuals:
     Min       1Q   Median       3Q      Max 
-11.4981  -3.0256   0.0987   2.9247  13.0025 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 59.12702    2.63496   22.44   <2e-16 ***
x            1.59941    0.05403   29.60   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 4.406 on 298 degrees of freedom
Multiple R-squared:  0.7462,    Adjusted R-squared:  0.7454 
F-statistic: 876.3 on 1 and 298 DF,  p-value: < 2.2e-16

On observe que la statistique du test de Student au carré est égale à la statistique de Fisher. C’est normal : les deux tests sont équivalents ici !


3. Prévision

On veut prédire la tension artérielle d’un homme de 50 ans avec un intervalle de confiance à 95%.

La formule de l’intervalle de prévision est :

\[\hat{Y}_0 \pm t_{n-2; 0.025} \sqrt{s_\epsilon^2 \left(1 + \frac{1}{n} + \frac{(X_0 - \bar{X})^2}{\sum (X_i - \bar{X})^2}\right)}\]

Code
se2 = SCE / 298
Y0 = beta1 * 50 + beta0
cat("Prévision de la tension pour un homme de 50 ans : Ŷ₀ =", round(Y0, 2), "mmHg")
Prévision de la tension pour un homme de 50 ans : Ŷ₀ = 139.1 mmHg
Code
bsup_prev = Y0 + qt(0.975, 298) * sqrt(se2 * (1 + 1/300 + (50 - mean(x))^2 / sum((x - mean(x))^2)))
binf_prev = Y0 - qt(0.975, 298) * sqrt(se2 * (1 + 1/300 + (50 - mean(x))^2 / sum((x - mean(x))^2)))
cat("Intervalle de prévision : [", round(binf_prev, 2), ";", round(bsup_prev, 2), "]")
Intervalle de prévision : [ 130.41 ; 147.78 ]

Visualisation avec intervalle de confiance

Code
plot(x, y,
     main = "Régression avec intervalle de prévision",
     xlab = "Âge", ylab = "Tension",
     pch = 19, col = "gray70")
abline(beta0, beta1, col = "blue", lwd = 2)
lines(c(50, 50), c(binf_prev, bsup_prev), col = "red", lwd = 3)
points(50, Y0, pch = 19, col = "red", cex = 1.5)

Bande de confiance complète

Code
plot(x, y,
     main = "Droite de régression avec bandes de prévision",
     xlab = "Âge", ylab = "Tension",
     pch = 19, col = "gray70")
abline(beta0, beta1, col = "blue", lwd = 2)

discr = 40:56
predicted = beta1 * discr + beta0
bsup_band = predicted + qt(0.975, 298) * sqrt(se2 * (1 + 1/300 + (discr - mean(x))^2 / sum((x - mean(x))^2)))
binf_band = predicted - qt(0.975, 298) * sqrt(se2 * (1 + 1/300 + (discr - mean(x))^2 / sum((x - mean(x))^2)))

for (i in 1:length(discr)) {
  lines(c(discr[i], discr[i]), c(binf_band[i], bsup_band[i]), lwd = 3, col = "orange")
}


4. Régression Linéaire Multiple

4.1 Contexte

Pour mesurer les performances auditives d’un individu, on le soumet à un signal sonore de fréquence donnée. On mesure le seuil (en décibel) à partir duquel le signal est perçu. Un seuil élevé indique un trouble de l’audition.

Les données contiennent les mesures pour 4 fréquences (500Hz, 1000Hz, 2000Hz, 4000Hz) et une auto-évaluation globale pour 1000 personnes de plus de 39 ans.

L’enjeu : Prédire globale par A5, A10, A20, A40.

4.2 Import des données

Code
audition = read.csv2("audition2.csv")
head(audition)
A5 = audition$A5
A10 = audition$A10
A20 = audition$A20
A40 = audition$A40
globale = audition$globale
A data.frame: 6 × 5
A5 A10 A20 A40 globale
<dbl> <dbl> <dbl> <dbl> <dbl>
1 0.01285502 0.9663823 0.87514354 -0.9128070 5.142168
2 0.12055133 -0.3726908 1.11712729 1.4278677 5.615904
3 -0.11320012 0.1210239 0.03302541 0.8079420 4.522749
4 2.05565754 2.0908744 0.36550190 -1.0883933 6.654767
5 1.44488695 0.9841622 0.45854632 -0.2759891 6.623146
6 0.60567511 1.0954039 1.95068690 2.1028709 7.228318

4.3 Coefficients de la régression

Code
lm(globale ~ A5 + A10 + A20 + A40)

Call:
lm(formula = globale ~ A5 + A10 + A20 + A40)

Coefficients:
(Intercept)           A5          A10          A20          A40  
     4.1423       0.7082       0.8602       0.6355       0.5106  

L’équation de la régression est :

\[\text{globale} = 4.14 + 0.71 \times A5 + 0.86 \times A10 + 0.64 \times A20 + 0.51 \times A40\]

On voit que le niveau d’audition évalué par le sujet augmente quand chaque seuil augmente, avec sensiblement la même importance.

4.4 Tests statistiques

Code
summary(lm(globale ~ A5 + A10 + A20 + A40))

Call:
lm(formula = globale ~ A5 + A10 + A20 + A40)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.63918 -0.19985  0.04163  0.27016  0.72287 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  4.14226    0.01274  325.10   <2e-16 ***
A5           0.70820    0.01808   39.17   <2e-16 ***
A10          0.86024    0.01948   44.17   <2e-16 ***
A20          0.63553    0.01439   44.17   <2e-16 ***
A40          0.51059    0.01333   38.30   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.3613 on 995 degrees of freedom
Multiple R-squared:  0.9653,    Adjusted R-squared:  0.9651 
F-statistic:  6911 on 4 and 995 DF,  p-value: < 2.2e-16

Interprétation :

  • Le test de Fisher (dernière ligne) montre que les variables explicatives ont, dans leur globalité, un effet significatif sur globale.
  • Tous les tests de Student sont significatifs → chaque variable contribue significativement.
  • Le coefficient \(R^2 \approx 0.97\) → 97% de la variance de globale est expliquée par le modèle linéaire.
  • On peut penser que le patient a une évaluation lucide de son état d’audition.

Résumé

Concept Description Fonction R
Régression simple \(y = \beta_1 x + \beta_0\) lm(y ~ x)
Résidus Écarts entre observé et prédit model$residuals
SCE, SCT, SCM Décomposition de la variance Calcul manuel
\(R^2\) Part de variance expliquée SCM/SCT ou summary()
Test de Fisher \(\beta_1 = 0\) ? anova(lm())
Prévision Prédiction avec IC Formule manuelle
Régression multiple Plusieurs variables explicatives lm(y ~ x1 + x2 + ...)