Dans le cadre de l’analyse macroéconomique, un économiste cherche à modéliser la croissance économique (taux de croissance du PIB) à partir de variables macroéconomiques structurelles.
| Symbole | Description | Unité |
|---|---|---|
| \(Y\) | Taux de croissance du PIB | % |
| \(X_1\) | Taux de chômage | % |
| \(X_2\) | Taux d’inflation | % |
| \(X_3\) | Taux d’investissement (% du PIB) | % |
\[ Y_i = \beta_0 + \beta_1 X_{1i} + \beta_2 X_{2i} + \beta_3 X_{3i} + \varepsilon_i, \quad \varepsilon_i \sim \mathcal{N}(0, \sigma^2) \]
# Librairies nécessaires
library(tidyverse) # Manipulation et visualisation des données
library(corrplot) # Visualisation des matrices de corrélation
library(car) # Variance Inflation Factor (VIF)
library(lmtest) # Tests économétriques
library(knitr) # Tableaux formatés
library(kableExtra) # Tableaux HTML/LaTeX améliorés
library(ggcorrplot) # Corrélogramme ggplot2
library(stargazer)
# Comparaison de modèles de régression# Saisie du jeu de données
df <- data.frame(
X1 = c(1.04, 2.10, 3.15, 4.05, 5.09, 6.40, 7.02, 8.09, 9.02, 10.10),
X2 = c(2.1, 4.0, 6.2, 8.1, 10.2, 12.1, 14.0, 16.2, 18.1, 20.2),
X3 = c(3, 7, 2, 9, 4, 8, 1, 6, 10, 5),
Y = c(9.8, 14.1, 15.3, 20.5, 21.2, 25.7, 26.4, 30.1, 34.2, 35.0)
)
# Aperçu du jeu de données
kable(df,
caption = "Tableau 1 — Données macroéconomiques (n = 10 observations)",
col.names = c("$X_1$ (Chômage)", "$X_2$ (Inflation)", "$X_3$ (Investissement)", "$Y$ (PIB)"),
align = "cccc",
digits = 2) %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
full_width = FALSE,
position = "center")| \(X_1\) (Chômage) | \(X_2\) (Inflation) | \(X_3\) (Investissement) | \(Y\) (PIB) |
|---|---|---|---|
| 1.04 | 2.1 | 3 | 9.8 |
| 2.10 | 4.0 | 7 | 14.1 |
| 3.15 | 6.2 | 2 | 15.3 |
| 4.05 | 8.1 | 9 | 20.5 |
| 5.09 | 10.2 | 4 | 21.2 |
| 6.40 | 12.1 | 8 | 25.7 |
| 7.02 | 14.0 | 1 | 26.4 |
| 8.09 | 16.2 | 6 | 30.1 |
| 9.02 | 18.1 | 10 | 34.2 |
| 10.10 | 20.2 | 5 | 35.0 |
La matrice de corrélation de Pearson mesure les associations linéaires bivariées entre toutes les variables du modèle.
# Calcul de la matrice de corrélation
mat_cor <- cor(df, method = "pearson")
# Affichage formatée
kable(round(mat_cor, 4),
caption = "Tableau 2 — Matrice de corrélation de Pearson",
align = "cccc") %>%
kable_styling(bootstrap_options = c("striped", "hover", "bordered"),
full_width = FALSE,
position = "center") %>%
column_spec(1, bold = TRUE)| X1 | X2 | X3 | Y | |
|---|---|---|---|---|
| X1 | 1.0000 | 0.9993 | 0.2431 | 0.9937 |
| X2 | 0.9993 | 1.0000 | 0.2343 | 0.9927 |
| X3 | 0.2431 | 0.2343 | 1.0000 | 0.3425 |
| Y | 0.9937 | 0.9927 | 0.3425 | 1.0000 |
# Visualisation du corrélogramme
ggcorrplot(mat_cor,
method = "circle",
type = "lower",
lab = TRUE,
lab_size = 4,
colors = c("#d73027", "white", "#1a9850"),
title = "Corrélogramme — Variables macroéconomiques",
ggtheme = theme_minimal(base_size = 12),
p.mat = cor_pmat(df), # Affiche les valeurs non significatives avec X
sig.level = 0.05)Figure 1 — Corrélogramme avec significativité
Lecture : Les cercles bleus (positifs) et rouges (négatifs) reflètent l’intensité des corrélations. Les cases marquées × indiquent une corrélation non significative au seuil de 5 %.
# Extraction des paires fortement corrélées (|r| > 0.9), hors diagonale
cor_long <- as.data.frame(as.table(mat_cor)) %>%
filter(Var1 != Var2) %>%
mutate(r_abs = abs(Freq)) %>%
filter(r_abs > 0.9) %>%
arrange(desc(r_abs)) %>%
distinct(r_abs, .keep_all = TRUE)
kable(cor_long,
caption = "Tableau 3 — Paires de variables avec |r| > 0.90",
col.names = c("Variable i", "Variable j", "Corrélation r", "|r|"),
digits = 4,
align = "cccc") %>%
kable_styling(bootstrap_options = c("striped", "hover"),
full_width = FALSE) %>%
row_spec(which(cor_long$r_abs > 0.99), bold = TRUE, color = "white", background = "#c0392b")| Variable i | Variable j | Corrélation r | |r| |
|---|---|---|---|
| X2 | X1 | 0.9993 | 0.9993 |
| Y | X1 | 0.9937 | 0.9937 |
| Y | X2 | 0.9927 | 0.9927 |
Constat : Le couple \((X_1, X_2)\) présente une corrélation quasi-parfaite \(r \approx 0.9999\), signe évident de colinéarité sévère. \(X_3\) ne pose pas de problème notable vis-à-vis des autres régresseurs.
La méthode des régressions auxiliaires consiste à régresser chaque régresseur \(X_j\) sur les autres et à extraire le \(R^2\) correspondant. Un \(R^2_j\) proche de 1 signale que \(X_j\) est quasi-linéairement expliqué par les autres variables, confirmant la colinéarité.
\[ X_1 = \alpha_0 + \alpha_2 X_2 + \alpha_3 X_3 + u_1 \]
reg_aux1 <- lm(X1 ~ X2 + X3, data = df)
R2_X1 <- summary(reg_aux1)$r.squared
cat(sprintf("R² (X1 ~ X2 + X3) = %.6f\n", R2_X1))## R² (X1 ~ X2 + X3) = 0.998706
##
## Call:
## lm(formula = X1 ~ X2 + X3, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.09608 -0.05743 -0.02782 0.02006 0.28336
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 0.029935 0.103224 0.290 0.780
## X2 0.496726 0.006968 71.286 2.81e-11 ***
## X3 0.009540 0.013988 0.682 0.517
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.1235 on 7 degrees of freedom
## Multiple R-squared: 0.9987, Adjusted R-squared: 0.9983
## F-statistic: 2701 on 2 and 7 DF, p-value: 7.8e-11
\[ X_2 = \beta_0 + \beta_1 X_1 + \beta_3 X_3 + u_2 \]
reg_aux2 <- lm(X2 ~ X1 + X3, data = df)
R2_X2 <- summary(reg_aux2)$r.squared
cat(sprintf("R² (X2 ~ X1 + X3) = %.6f\n", R2_X2))## R² (X2 ~ X1 + X3) = 0.998700
##
## Call:
## lm(formula = X2 ~ X1 + X3, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.56994 -0.04551 0.06276 0.10876 0.19985
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -0.04844 0.20810 -0.233 0.823
## X1 2.01041 0.02820 71.286 2.81e-11 ***
## X3 -0.01853 0.02820 -0.657 0.532
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.2485 on 7 degrees of freedom
## Multiple R-squared: 0.9987, Adjusted R-squared: 0.9983
## F-statistic: 2689 on 2 and 7 DF, p-value: 7.923e-11
\[ X_3 = \gamma_0 + \gamma_1 X_1 + \gamma_2 X_2 + u_3 \]
reg_aux3 <- lm(X3 ~ X1 + X2, data = df)
R2_X3 <- summary(reg_aux3)$r.squared
cat(sprintf("R² (X3 ~ X1 + X2) = %.6f\n", R2_X3))## R² (X3 ~ X1 + X2) = 0.113772
##
## Call:
## lm(formula = X3 ~ X1 + X2, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -4.7069 -1.2940 -0.3765 1.6556 4.1955
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 3.745 2.319 1.615 0.150
## X1 6.531 9.577 0.682 0.517
## X2 -3.135 4.771 -0.657 0.532
##
## Residual standard error: 3.232 on 7 degrees of freedom
## Multiple R-squared: 0.1138, Adjusted R-squared: -0.1394
## F-statistic: 0.4493 on 2 and 7 DF, p-value: 0.6553
tableau_R2 <- data.frame(
Variable = c("$X_1$", "$X_2$", "$X_3$"),
Modele = c("$X_1 \\sim X_2 + X_3$",
"$X_2 \\sim X_1 + X_3$",
"$X_3 \\sim X_1 + X_2$"),
R2 = round(c(R2_X1, R2_X2, R2_X3), 6),
Diagnostic = c("⚠️ Colinéarité très sévère",
"⚠️ Colinéarité très sévère",
"✅ Pas de colinéarité")
)
kable(tableau_R2,
caption = "Tableau 4 — Coefficients de détermination des régressions auxiliaires",
col.names = c("Variable", "Modèle auxiliaire", "$R^2_j$", "Diagnostic"),
align = "clcc",
escape = FALSE) %>%
kable_styling(bootstrap_options = c("striped", "hover", "bordered"),
full_width = FALSE) %>%
row_spec(1:2, background = "#fdecea") %>%
row_spec(3, background = "#eafaf1")| Variable | Modèle auxiliaire | \(R^2_j\) | Diagnostic |
|---|---|---|---|
| \(X_1\) | \(X_1 \sim X_2 + X_3\) | 0.998706 | ⚠️ Colinéarité très sévère |
| \(X_2\) | \(X_2 \sim X_1 + X_3\) | 0.998700 | ⚠️ Colinéarité très sévère |
| \(X_3\) | \(X_3 \sim X_1 + X_2\) | 0.113772 | ✅ Pas de colinéarité | |
Le VIF est défini par :
\[ \text{VIF}_j = \frac{1}{1 - R^2_j} \]
Un \(\text{VIF}_j > 10\) est généralement considéré comme le seuil critique de colinéarité inacceptable.
# Modèle complet pour extraction du VIF
modele_complet <- lm(Y ~ X1 + X2 + X3, data = df)
vif_vals <- vif(modele_complet)
# Calcul manuel pour validation
vif_manuel <- data.frame(
Variable = names(vif_vals),
R2_j = round(c(R2_X1, R2_X2, R2_X3), 6),
VIF_car = round(vif_vals, 4),
VIF_manuel = round(1 / (1 - c(R2_X1, R2_X2, R2_X3)), 2)
)
kable(vif_manuel,
caption = "Tableau 5 — Variance Inflation Factors",
col.names = c("Variable", "$R^2_j$", "VIF (package car)", "VIF (calcul manuel)"),
align = "cccc",
escape = FALSE) %>%
kable_styling(bootstrap_options = c("striped", "hover", "bordered"),
full_width = FALSE) %>%
row_spec(which(vif_manuel$VIF_car > 10), bold = TRUE, color = "white", background = "#c0392b")| Variable | \(R^2_j\) | VIF (package car) | VIF (calcul manuel) | |
|---|---|---|---|---|
| X1 | X1 | 0.998706 | 772.6296 | 772.63 |
| X2 | X2 | 0.998700 | 769.1741 | 769.17 |
| X3 | X3 | 0.113772 | 1.1284 | 1.13 |
Interprétation : Les VIF de \(X_1\) et \(X_2\) dépassent massivement le seuil de 10 (voire de 1 000), confirmant une colinéarité quasi-parfaite entre ces deux variables. \(X_3\) présente un VIF proche de 1, indiquant son indépendance vis-à-vis des autres régresseurs.
La colinéarité ne viole pas les hypothèses du modèle classique au sens strict, mais elle dégrade fortement la qualité des estimateurs :
| Propriété OLS | Impact de la colinéarité |
|---|---|
| Sans biais (E[β̂]=β) | ✅ Préservée — les estimateurs restent sans biais |
| Variance minimale | ❌ Variances \(\text{Var}(\hat\beta_j) = \frac{\sigma^2}{S_{jj}(1-R^2_j)}\) explosent |
| Intervalles de confiance | ❌ Très larges, estimateurs instables |
| Tests t individuels | ❌ Statistiques \(t\) faibles → non-rejet erroné de \(H_0\) |
| Signe des coefficients | ❌ Peut être inversé ou contre-intuitif |
# Démonstration : la variance des estimateurs est multipliée par VIF
sigma2_hat <- summary(modele_complet)$sigma^2
# Matrice de variance-covariance des estimateurs
vcov_matrix <- vcov(modele_complet)
cat("Matrice de variance-covariance des estimateurs :\n")## Matrice de variance-covariance des estimateurs :
## (Intercept) X1 X2 X3
## (Intercept) 0.1105 -0.0438 0.0175 -0.0080
## X1 -0.0438 1.4640 -0.7272 -0.0140
## X2 0.0175 -0.7272 0.3617 0.0067
## X3 -0.0080 -0.0140 0.0067 0.0021
##
## Call:
## lm(formula = Y ~ X1 + X2 + X3, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.40660 -0.20867 -0.07769 0.09546 0.69865
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 6.25023 0.33243 18.802 1.46e-06 ***
## X1 1.26677 1.20997 1.047 0.335458
## X2 0.73153 0.60144 1.216 0.269547
## X3 0.31703 0.04624 6.856 0.000474 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.3954 on 6 degrees of freedom
## Multiple R-squared: 0.9986, Adjusted R-squared: 0.9979
## F-statistic: 1406 on 3 and 6 DF, p-value: 6.267e-09
Observation : Malgré un \(R^2\) global élevé et un \(F\)-test significatif, les tests \(t\) individuels pour \(\hat\beta_1\) et \(\hat\beta_2\) ne sont pas significatifs. C’est le symptôme classique de la colinéarité : le modèle “explique bien” globalement, mais il est incapable d’isoler les effets partiels de \(X_1\) et \(X_2\).
Trois approches sont envisageables :
| Méthode | Avantages | Inconvénients |
|---|---|---|
| Suppression d’une variable | Simple, modèle parcimonieux | Perte d’information potentielle |
| Régression Ridge (\(\lambda\)) | Stabilise les coefficients | Biais introduit, \(\lambda\) à choisir |
| ACP (composantes principales) | Orthogonalisation complète | Perte d’interprétabilité économique |
Choix justifié : On supprime \(X_2\) (taux d’inflation) pour les raisons suivantes :
##
## Call:
## lm(formula = Y ~ X1 + X2 + X3, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.40660 -0.20867 -0.07769 0.09546 0.69865
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 6.25023 0.33243 18.802 1.46e-06 ***
## X1 1.26677 1.20997 1.047 0.335458
## X2 0.73153 0.60144 1.216 0.269547
## X3 0.31703 0.04624 6.856 0.000474 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.3954 on 6 degrees of freedom
## Multiple R-squared: 0.9986, Adjusted R-squared: 0.9979
## F-statistic: 1406 on 3 and 6 DF, p-value: 6.267e-09
\[ Y = \beta_0 + \beta_1 X_1 + \beta_3 X_3 + \varepsilon \]
##
## Call:
## lm(formula = Y ~ X1 + X3, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.4622 -0.1697 -0.1131 0.1971 0.6649
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 6.21479 0.34230 18.156 3.80e-07 ***
## X1 2.73744 0.04639 59.012 1.05e-10 ***
## X3 0.30348 0.04639 6.542 0.000321 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.4087 on 7 degrees of freedom
## Multiple R-squared: 0.9982, Adjusted R-squared: 0.9977
## F-statistic: 1973 on 2 and 7 DF, p-value: 2.336e-10
# Tableau comparatif via stargazer
stargazer(modele_initial, modele_corrige,
type = "html",
title = "Tableau 6 — Comparaison des modèles de régression",
column.labels = c("Modèle initial (M1)", "Modèle corrigé (M2)"),
dep.var.label = "Variable dépendante : Y (Croissance du PIB)",
covariate.labels = c("$X_1$ (Chômage)", "$X_2$ (Inflation)",
"$X_3$ (Investissement)", "Constante"),
digits = 4,
star.cutoffs = c(0.10, 0.05, 0.01),
notes = "Seuils de significativité : *p<0.1 ; **p<0.05 ; ***p<0.01",
notes.append = FALSE)| Dependent variable: | ||
| Y | ||
| Modèle initial (M1) | Modèle corrigé (M2) | |
| (1) | (2) | |
| X(Chômage) | 1.2668 | 2.7374*** |
| (1.2100) | (0.0464) | |
| X(Inflation) | 0.7315 | |
| (0.6014) | ||
| X(Investissement) | 0.3170*** | 0.3035*** |
| (0.0462) | (0.0464) | |
| Constante | 6.2502*** | 6.2148*** |
| (0.3324) | (0.3423) | |
| Observations | 10 | 10 |
| R2 | 0.9986 | 0.9982 |
| Adjusted R2 | 0.9979 | 0.9977 |
| Residual Std. Error | 0.3954 (df = 6) | 0.4087 (df = 7) |
| F Statistic | 1,405.9680*** (df = 3; 6) | 1,973.0940*** (df = 2; 7) |
| Note: | Seuils de significativité : p<0.1 ; p<0.05 ; p<0.01 | |
| Variable dépendante : Y (Croissance du PIB) |
| Seuils de significativité : p<0.1 ; p<0.05 ; p<0.01 |
# Tableau de comparaison des métriques
comp <- data.frame(
Critere = c("$R^2$", "$R^2$ ajusté", "F-stat", "AIC", "BIC",
"$\\hat\\beta_1$ (t-stat)", "$\\hat\\beta_3$ (t-stat)"),
M1_initial = c(
round(summary(modele_initial)$r.squared, 4),
round(summary(modele_initial)$adj.r.squared, 4),
round(summary(modele_initial)$fstatistic[1], 2),
round(AIC(modele_initial), 2),
round(BIC(modele_initial), 2),
paste0(round(coef(modele_initial)["X1"], 4),
" (t=", round(summary(modele_initial)$coef["X1","t value"], 2), ")"),
paste0(round(coef(modele_initial)["X3"], 4),
" (t=", round(summary(modele_initial)$coef["X3","t value"], 2), ")")
),
M2_corrige = c(
round(summary(modele_corrige)$r.squared, 4),
round(summary(modele_corrige)$adj.r.squared, 4),
round(summary(modele_corrige)$fstatistic[1], 2),
round(AIC(modele_corrige), 2),
round(BIC(modele_corrige), 2),
paste0(round(coef(modele_corrige)["X1"], 4),
" (t=", round(summary(modele_corrige)$coef["X1","t value"], 2), ")"),
paste0(round(coef(modele_corrige)["X3"], 4),
" (t=", round(summary(modele_corrige)$coef["X3","t value"], 2), ")")
)
)
kable(comp,
caption = "Tableau 7 — Comparaison quantitative des modèles M1 et M2",
col.names = c("Critère", "M1 — Modèle initial", "M2 — Modèle corrigé"),
align = "lcc",
escape = FALSE) %>%
kable_styling(bootstrap_options = c("striped", "hover", "bordered"),
full_width = FALSE) %>%
row_spec(c(1, 2, 3), background = "#eaf4fb")| Critère | M1 — Modèle initial | M2 — Modèle corrigé |
|---|---|---|
| \(R^2\) | 0.9986 | 0.9982 |
| \(R^2\) ajusté | 0.9979 | 0.9977 |
| F-stat | 1405.97 | 1973.09 |
| AIC | 14.71 | 14.92 |
| BIC | 16.23 | 16.13 |
| \(\hat\beta_1\) (t-stat) | 1.2668 (t=1.05) | 2.7374 (t=59.01) |
| \(\hat\beta_3\) (t-stat) | 0.317 (t=6.86) | 0.3035 (t=6.54) |
# Données pour graphique
# Données pour graphique
df_plot <- df %>%
mutate(
fitted_M1 = fitted(modele_initial),
fitted_M2 = fitted(modele_corrige),
obs_id = 1:n()
) %>%
# Passage au format long pour ggplot2
pivot_longer(cols = c(fitted_M1, fitted_M2),
names_to = "Modele",
values_to = "Fitted") %>%
# Correction de l'erreur ici : utilisation de case_match (plus moderne et sûr)
mutate(Modele = case_match(Modele,
"fitted_M1" ~ "M1 — Modèle initial",
"fitted_M2" ~ "M2 — Modèle corrigé"))
# Visualisation
ggplot(df_plot, aes(x = obs_id)) +
geom_line(aes(y = Y, color = "Observé"), linewidth = 1.2, linetype = "dashed") +
geom_line(aes(y = Fitted, color = Modele), linewidth = 1) +
geom_point(aes(y = Y), color = "black", size = 2) +
facet_wrap(~Modele, ncol = 2) +
scale_color_manual(values = c("Observé" = "black",
"M1 — Modèle initial" = "#e74c3c",
"M2 — Modèle corrigé" = "#2980b9")) +
labs(title = "Valeurs observées vs ajustées",
subtitle = "Comparaison entre le modèle colinéaire (M1) et le modèle corrigé (M2)",
x = "Indice d'observation",
y = "Y — Croissance du PIB (%)",
color = NULL) +
theme_minimal(base_size = 12) +
theme(legend.position = "top",
strip.text = element_text(face = "bold"))Figure 2 — Valeurs ajustées vs observées (M1 et M2)
## ====================================================
## SYNTHÈSE COMPARATIVE — M1 vs M2
## ====================================================
cat(sprintf(" R² : M1 = %.4f | M2 = %.4f\n",
summary(modele_initial)$r.squared,
summary(modele_corrige)$r.squared))## R² : M1 = 0.9986 | M2 = 0.9982
cat(sprintf(" R² ajusté : M1 = %.4f | M2 = %.4f\n",
summary(modele_initial)$adj.r.squared,
summary(modele_corrige)$adj.r.squared))## R² ajusté : M1 = 0.9979 | M2 = 0.9977
## AIC : M1 = 14.71 | M2 = 14.92
## BIC : M1 = 16.23 | M2 = 16.13
## ====================================================
Les résultats de cette analyse confirment que :
La colinéarité entre \(X_1\) et \(X_2\) est quasi-parfaite (\(r \approx 0.9999\), \(\text{VIF} \gg 10\)), rendant les estimateurs OLS du modèle initial numériquement instables et les tests individuels non informatifs.
Le modèle corrigé M2 (\(Y \sim X_1 + X_3\)) présente :
Interprétation économétrique finale :
Le modèle M2 est retenu comme modèle final, car il est plus parcimonieux, plus stable, et ses estimateurs sont économétriquement interprétables.
Fin du TP — Module : Économétrie Appliquée à la Finance | FST Errachidia | Prof. BEN HSSAIN