B.3 Variance de l’estimateur des moindres carrés ordinaires
B.3.1 Cas homoscédastique
Faire l’hypothèse d’homoscédasticité revient à supposer que \(\mathbb{E}[\epsilon^2 \mid X]\) est constante égale à un certain réel positif \(\sigma^2\).
Dans ce cas, le calcul en est grandement simplifié puisque la matrice de variance-covariance \(\mathbb{E}[XX']^{-1}\mathbb{E}[XX'\epsilon^2]\mathbb{E}[XX']^{-1}\) peut se simplifier en \(\sigma^2 \mathbb{E}[XX']^{-1}\). Un estimateur du premier terme est \(\frac{1}{n} \sum_{i=1}^n \hat{\epsilon_i}^2\). Un estimateur du second terme est \((\frac{1}{n}\mathbf{X}'\mathbf{X})^{-1}\) que l’on a de toute façon déjà calculé pour évaluer \(\hat{\beta}\). Ainsi, un estimateur de la matrice de variance-covariance de \(\sqrt{n}(\hat{\beta}-\beta)\) est donné par \((\frac{1}{n}\mathbf{X}'\mathbf{X})^{-1} \left(\frac{1}{n}\sum_{i=1}^n \hat{\epsilon_i}^2\right)\), et donc un estimateur de la matrice de variance-covariance de \(\hat{\beta}\) est donné par \(\frac{1}{n}(\frac{1}{n}\mathbf{X}'\mathbf{X})^{-1} \left(\frac{1}{n}\sum_{i=1}^n \hat{\epsilon_i}^2\right)\).
Le code suivant permet de vérifier que c’est bien ainsi que la fonction pré-implémentée lm estime la matrice de variance-covariance par défaut.
library(AER,
quietly=TRUE)
library(data.table,
quietly=TRUE)
#On charge dans un premier temps les données du CPS 1985
data("CPS1985")
<-data.table(CPS1985)
CPS
#On estime la régression du salaire horaire sur l'éducation et l'âge
<-lm(wage~education + age,
reg_wage_educ_agedata=CPS)
#On récupère les résidus estimés
$estimated_residuals<-CPS$wage-
CPS$coefficients["(Intercept)"]-
reg_wage_educ_age$coefficients["education"]*CPS$education-
reg_wage_educ_age$coefficients["age"]*CPS$age
reg_wage_educ_age
#On estime sigma2 comme la moyenne empirique du carré des résidus
<-mean(CPS$estimated_residuals^2)
estimated_sigma2
#On a aussi besoin de X'X
#On construit la matrice X
<-as.matrix(cbind(rep(1,
matrice_covariablesnrow(CPS)),
CPS[,c("education",
"age")]))
#On calcule X'X : c'est bien une matrice 3*3
<-t(matrice_covariables)%*%matrice_covariables
XprimeX XprimeX
## V1 education age
## V1 534 6952 19669
## education 6952 94152 253613
## age 19669 253613 797769
#L'estimateur de la matrice de variance-covariance : n(X'X)-1 multiplié par
# l'estimateur de sigma2 multiplié par 1/n
<-(1)/(nrow(CPS)-nrow(XprimeX))*#On divise par n-d et pas par n pour
estimated_vcov#avoir de meilleures propriétés à distance finie
solve((1/nrow(CPS))*XprimeX)*
estimated_sigma2
estimated_vcov
## V1 education age
## V1 1.63677072 -0.0845952551 -0.0134615244
## education -0.08459526 0.0059360405 0.0001986127
## age -0.01346152 0.0001986127 0.0002952717
#On vérifie l'égalité entre cet estimateur et la matrice de variance-covariance
# estimée par lm
all.equal(as.numeric(estimated_vcov),
as.numeric(vcov(reg_wage_educ_age)))
## [1] TRUE
B.3.2 Cas hétéroscédastique
Faire l’hypothèse d’hétéroscédasticité revient à autoriser la dispersion de \(\epsilon\), mesurée par l’espérance conditionnelle \(\mathbb{E}[\epsilon^2 \mid X]\), à varier avec les variables indépendantes.
Dans ce cas, il faut estimer la matrice \(\mathbb{E}[XX'\epsilon^2]\). La solution usuelle est de considérer comme estimateur de \(\mathbb{E}[XX']^{-1}\mathbb{E}[XX'\epsilon^2]\mathbb{E}[XX']^{-1}\) la variable aléatoire \((\frac{1}{n}\mathbf{X}'\mathbf{X})^{-1} (\frac{1}{n} \mathbf{X}' \hat{\Sigma} \mathbf{X}) (\frac{1}{n}\mathbf{X}'\mathbf{X})^{-1}\), où \(\hat{\Sigma}\) est la matrice carrée de taille \(n \times n\), dont les termes diagonaux sont égaux à \(\hat{\epsilon_i}^2\) et les termes non-diagonaux sont nuls. En effet, en décomposant le terme central :
\[\begin{align} \mathbf{X}' \hat{\Sigma} \mathbf{X} & = \left( \begin{array}{ccc} X_1^1 & \cdots & X_n^1 \\ \vdots & \ddots & \vdots \\ X_1^d & \cdots & X_n^d \\ \end{array}\right) \left( \begin{array}{ccc} \hat{\epsilon_1}^2 & \cdots & 0 \\ \vdots & \ddots \vdots \\ 0 & \cdots & \hat{\epsilon_n}^2 \\ \end{array}\right) \left(\begin{array}{ccc} X_1^1 & \cdots & X_i^d \\ \vdots & \ddots & \vdots \\ X_n^1 & \cdots & X_n^d \\ \end{array}\right) \nonumber \\ &= \left( \begin{array}{ccc} X_1^1 \hat{\epsilon_1}^2 & \cdots & X_1^n \hat{\epsilon_1}^2 \\ \vdots & \ddots & \vdots \\ X_1^d \hat{\epsilon_1}^2 & \cdots & X_n^d \hat{\epsilon_n}^2 \\ \end{array} \right) \left(\begin{array}{ccc} X_1^1 & \cdots & X_i^d \\ \vdots & \ddots & \vdots \\ X_n^1 & \cdots & X_n^d \\ \end{array}\right) \nonumber \\ &= \left( \begin{array}{ccc} \sum_{i=1}^n X_i^1 X_i^1 \hat{\epsilon_i}^2 & \cdots & \sum_{i=1}^n X_i^1 X_i^d \hat{\epsilon_i}^2 \\ \vdots & \ddots & \vdots \\ \sum_{i=1}^n X_i^d X_i^1 \hat{\epsilon_i}^2 & \cdots & \sum_{i=1}^n X_i^d X_i^d \hat{\epsilon_i}^2 \\ \end{array} \right) \end{align}\]
Le code suivant permet de vérifier que c’est bien cette approche qui sert à R pour calculer la matrice de variance-covariance robuste à l’hétéroscédasticité.
library(AER,
quietly=TRUE)
library(data.table,
quietly=TRUE)
#On charge dans un premier temps les données du CPS 1985
data("CPS1985")
<-data.table(CPS1985)
CPS
#On estime la régression du salaire horaire sur l'éducation et l'âge
<-lm(wage~education + age,
reg_wage_educ_agedata=CPS)
#On récupère les résidus estimés
$estimated_residuals<-CPS$wage-
CPS$coefficients["(Intercept)"]-
reg_wage_educ_age$coefficients["education"]*CPS$education-
reg_wage_educ_age$coefficients["age"]*CPS$age
reg_wage_educ_age
#On construit la matrice X
<-as.matrix(cbind(rep(1,
matrice_covariablesnrow(CPS)),
CPS[,c("education",
"age")]))
#On construit la matrice diagonale Sigma
<-diag(x=CPS$estimated_residuals^2,
Sigmanrow=nrow(CPS),
ncol=nrow(CPS))
#On estime le terme central du sandwich : X'SigmaX
<-1/nrow(CPS)*t(matrice_covariables)%*%Sigma%*%matrice_covariables
XprimeSigmaX
#On estime les termes des côtés
<-t(matrice_covariables)%*%matrice_covariables
XprimeX
#On estime la matrice de variance-covariance
<-1/(nrow(CPS))*
estimated_vcovHCsolve(1/nrow(CPS)*XprimeX)%*%
%*%
XprimeSigmaXsolve(1/nrow(CPS)*XprimeX)
estimated_vcovHC
## V1 education age
## V1 1.74565165 -0.0935543358 -0.0156346157
## education -0.09355434 0.0067706327 0.0003106483
## age -0.01563462 0.0003106483 0.0003213295
#On vérifie que la matrice de variance-covariance robuste à l'hétéroscédaisticté
# estimée par R est égale à celle que l'on vient d'estimer
all.equal(as.numeric(estimated_vcovHC),
as.numeric(vcovHC(reg_wage_educ_age,
type="HC0")))
## [1] TRUE
#En fait le résultat par défaut prend en plus en compte une correction qui
# améliore la précision à distance finie
vcovHC(reg_wage_educ_age)
## (Intercept) education age
## (Intercept) 1.78606223 -0.095847356 -0.0159540103
## education -0.09584736 0.006927378 0.0003197900
## age -0.01595401 0.000319790 0.0003272044
B.3.3 En relâchant l’hypothèse d’indépendance
Grâce aux résultats de l’Annexe A.12, on sait que lorsque le nombre \(n_C\) de clusters devient très grand, la loi de la variable aléatoire \(\sqrt{n_C}(\hat{\beta}-\beta)\) s’identifie à la loi normale centrée de matrice de variance-covariance \(\mathbb{E}[\sum_{i \in c} X_iX_i']^{-1} \mathcal{V}(\tilde{\epsilon_c}) \mathbb{E}[\sum_{i \in c} X_iX_i']^{-1}\). Les termes latéraux de ce produit matriciel ne posent pas intrisèquement de difficulté : en effet, en reprenant les notations de la preuve en question, on voit que \(\frac{1}{n_C}\mathbf{X}'\mathbf{X}=\frac{1}{n_C}\sum_{c=1}^{n_C} \mathbf{X}_c'\mathbf{X}_c\) doit s’identifier quand \(n_C\) tend vers l’infini à cette matrice.
La difficulté est donc d’estimer le terme central \(\mathcal{V}(\tilde{\epsilon_c})\) avec : \[\tilde{\epsilon_c}:=\sum_{\begin{array}{c}i=1 \\ i \in c\end{array}}^n X_i \epsilon_i\] Pour ce faire, on va simplement remplacer dans la somme \(\epsilon_i\) qui est inconnu, par \(\hat{\epsilon_i}\). En définitive, le terme central de l’estimateur retenu de la matrice de variance-covariance de \(\sqrt{n_C}(\hat{\beta}-\beta)\) s’écrit : \[\frac{1}{n_C} \sum_{c=1}^{n_C} \left\{\left(\sum_{\begin{array}{c}i=1 \\ i \in c\end{array}}^n X_i \hat{\epsilon_i}\right)\left(\sum_{\begin{array}{c}i=1 \\ i \in c\end{array}}^n X_i \hat{\epsilon_i}\right)'\right\}\] Lorsque chaque observation correspond à un cluster, on retombe bien sur l’estimateur de la matrice de variance-covariance robuste à l’hétéroscédasticité examiné plus haut.
Le fragment de code suivant permet de vérifier sur l’exemple proposé en 2.9.5 que c’est bien de cette façon que R prend en compte les clusters dans le calcul des écarts-types.
library(AER,
quietly=TRUE)
library(data.table,
quietly=TRUE)
library(ggplot2,
quietly=TRUE)
#On charge dans un premier temps les données nécessaires
data("TeachingRatings")
<-data.table(TeachingRatings)
TeachingRatings
#On réplique Hamermesh and Parker, 2005, Table 3
<-lm(eval ~ beauty +
reg_eval+
gender +
minority +
native +
tenure +
division
credits,weights = students,
data = TeachingRatings)
#On récupère les résidus estimés
$residuals<-reg_eval$residuals
TeachingRatings
#On construit la matrice X
#Une difficulté ici : on a des variables qualitatives qu'il faut au préalable
# convertir en variables dichotomiques
#On récupère le nom des variables indépendantes incluses dans la régression
<-gsub(" ",
noms_covariables"",
strsplit(
strsplit(
as.character(reg_eval$call)[2],
"~")[[1]][2],
"\\+")[[1]])
#On récupère d'abord les colonnes des variables incluses dans la régression mais
# qui ne sont pas dans un format numérique
<-names(sapply(TeachingRatings[,..noms_covariables],
nonnumeric_variablessapply(TeachingRatings
is.numeric)[
[,..noms_covariables],==FALSE])
is.numeric)#Pour chaque colonne non-numérique il faut ensuite récupérer les niveaux de
# cette variable
<-lapply(TeachingRatings[,..nonnumeric_variables],
nonnumeric_variables_levels
levels)
#Lorsqu'il y a strictement plus de 1 niveau il faut créer une variable
# dichotomique d'appartenance à chaque niveau
<-function(variable,
dichotomique
niveau){
as.numeric(TeachingRatings[,..variable]==niveau)
}#On boucle sur les niveaux à l'intérieur de chaque variable non-numérique
<-function(variable){
dichotomique_list
if(variable %in% nonnumeric_variables){
if(length(nonnumeric_variables_levels[[variable]])>1){
<-sapply(nonnumeric_variables_levels[[
dichot
variable]][2:length(nonnumeric_variables_levels[[variable]])],
function(x){
dichotomique(variable,
x)
})
}
}else{
TeachingRatings[,..variable]
}
}
<-as.matrix(cbind(rep(1,
matrice_covariablesnrow(TeachingRatings)),
do.call(cbind,
sapply(noms_covariables,
dichotomique_list))))colnames(matrice_covariables)[1]<-"(Intercept)"
#On est dans une régression pondérée, on va aussi avoir besoin du vecteur des
# poids
if(!is.null(reg_eval$weights)){
<-reg_eval$weights
vecteur_poids
}if(is.null(reg_eval$weights)){
<-rep(1,
vecteur_poidsnrow(TeachingRatings))
}
#Une quantité utile : le nombre d'étudiants (le poids total des observations)
<-as.numeric(sum(vecteur_poids))
sum_weight
#On définit le niveau de clustering
<-"prof"
clustering
#Une quantité utile : n_C le nombre de clusters
<-nrow(unique(TeachingRatings[,..clustering]))
nb_clusters
#On estime les termes des côtés
<-
XprimeX1/sum_weight*
t(sqrt(vecteur_poids)*matrice_covariables)%*%
*sqrt(vecteur_poids))
(matrice_covariables
#La vraie difficulté : on veut estimer le terme central
#Il faut : sommer toutes les variables indépendantes*residus estimé à
# l'intérieur de chaque cluster = le groupe des observations correspondant à un
# enseignant
<-cbind(matrice_covariables*TeachingRatings$residuals,
Xepsilon
TeachingRatings[,..clustering],
vecteur_poids)colnames(Xepsilon)[ncol(Xepsilon)]<-"poids"
<-
sum_Xepsilon_cluster
Xepsilon[,lapply(X=.SD,
FUN=function(x){
sum(x*as.numeric(poids)/sum_weight)
}),=colnames(matrice_covariables),
.SDcols=clustering]
by
#Ensuite on multiplie la matrice obtenue par sa transposée
<-
terme_central1/nb_clusters*
t(as.matrix(
sum_Xepsilon_cluster[,
.SD,.SDcols=names(sum_Xepsilon_cluster)!=clustering]))%*%
as.matrix(
sum_Xepsilon_cluster[,
.SD,.SDcols=names(sum_Xepsilon_cluster)!=clustering])
#Enfin on multiplie par X'X^-1 des deux côtés
<-
estimated_vcov_cluster/(nb_clusters-1)*
nb_clusters1/nb_clusters*
solve(1/nb_clusters*XprimeX)%*%
%*%
terme_centralsolve(1/nb_clusters*XprimeX)
estimated_vcov_cluster
## (Intercept) beauty.beauty gender.female minority.yes
## (Intercept) 0.0093537390 3.274401e-04 -0.0018700974 0.0008650045
## beauty.beauty 0.0003274401 3.396539e-03 -0.0010481125 -0.0008142540
## gender.female -0.0018700974 -1.048113e-03 0.0071363205 -0.0023394829
## minority.yes 0.0008650045 -8.142540e-04 -0.0023394829 0.0122835749
## native.no -0.0009147969 5.952765e-05 -0.0001297671 -0.0044308522
## tenure.yes -0.0074473070 2.740039e-04 -0.0002957024 -0.0016536652
## division.lower -0.0039995046 6.002078e-04 -0.0014972379 -0.0016677874
## credits.single 0.0004566126 1.006060e-03 0.0015614848 -0.0020578474
## native.no tenure.yes division.lower credits.single
## (Intercept) -9.147969e-04 -7.447307e-03 -0.0039995046 4.566126e-04
## beauty.beauty 5.952765e-05 2.740039e-04 0.0006002078 1.006060e-03
## gender.female -1.297671e-04 -2.957024e-04 -0.0014972379 1.561485e-03
## minority.yes -4.430852e-03 -1.653665e-03 -0.0016677874 -2.057847e-03
## native.no 1.768224e-02 -5.910749e-04 0.0024595592 -2.059399e-03
## tenure.yes -5.910749e-04 8.744043e-03 0.0025752894 6.397171e-05
## division.lower 2.459559e-03 2.575289e-03 0.0099946976 -5.861908e-03
## credits.single -2.059399e-03 6.397171e-05 -0.0058619076 2.698866e-02
#On vérifie que la matrice de variance-covariance robuste à la dépendance
# estimée par R est égale à celle que l'on vient d'estimer
all.equal(as.numeric(estimated_vcov_cluster),
as.numeric(vcovCL(reg_eval,
type="HC0",
cluster=TeachingRatings[,..clustering])))
## [1] TRUE
#En fait l'estimateur par défaut utilise encore une correction supplémentaire
# qui prend en compte la dimension des variables indépendendantes (qui réduit
# les degrés de liberté)
all.equal((nrow(TeachingRatings)-1)/
nrow(TeachingRatings)-ncol(matrice_covariables))*
(as.numeric(estimated_vcov_cluster),
as.numeric(vcovCL(reg_eval,
cluster=TeachingRatings[,..clustering])))
## [1] TRUE