I am following up on the question "Why does systemfit
yield identical results for OLS and WLS?". It deals with estimating a system of linear equations where each of them has one unique regressor and one common regressor. The estimation is done via OLS and WLS using the systemfit
package in R.
It turns out that the point estimates are equal, $\hat\beta_\text{OLS}=\hat\beta_\text{WLS}$
and the estimated coefficient covariance matrices are identical, too, $\widehat{\text{Cov}}_\text{OLS}(\hat\beta) = (X'\hat\Omega_\text{OLS}^{-1}X)^{-1} =(X'\hat\Omega_\text{WLS}^{-1}X)^{-1} = \widehat{\text{Cov}}_\text{WLS}(\hat\beta)$ because the estimated error covariance matrices are identical, $\hat\Omega_\text{OLS}=\hat\Omega_\text{WLS}$ (unless equal error variances across equations are imposed by control = systemfit.control(singleEqSigma = FALSE)
, but this is not the default option). I can also replicate the findings manually:
library(systemfit)
# Generate and prepare the data
n <- 8 # sample size
m <- 2 # length of the "second part" of the sample
N <- 3 # number of equations
set.seed(321); x <- matrix(rnorm(n*N),ncol=N); colnames(x) <- paste0("x",1:N) # generate regressors
dummy <- c( rep(0,n-m), rep(1,m) ) # generate a common regressor
x <- cbind(x,dummy) # include the common regressor with the rest of the regressors
set.seed(123); y <- matrix(rnorm(n*N),ncol=N); colnames(y) <- paste0("y",1:N) # a placeholder for dependent variables
for(i in 1:N){
y[,i] <- i + sqrt(i)*x[,i] - i*dummy + y[,i]*15*sqrt(i)
# y[,i] is a linear function of x[,i] and dummy,
# plus an error term with equation-specific variance - just what WLS is made for
}
data1 <- as.data.frame(cbind(y,x)) # create a data frame of all data (y and x)
# Create the model equations
eqSystem <- list()
for(i in 1:N){
eqSystem[[i]] <- as.formula(assign(paste0("eq",i), value=paste0("y",i," ~ x",i," + dummy"))) # define linear equations of SUR
}
# Estimate the model with `method="OLS"` and `method="WLS"`
m1a <- systemfit(formula=eqSystem, method="OLS", data=data1, control=systemfit.control(singleEqSigma=FALSE))
m1b <- systemfit(formula=eqSystem, method="OLS", data=data1, control=systemfit.control(singleEqSigma=TRUE ))
m2a <- systemfit(formula=eqSystem, method="WLS", data=data1, control=systemfit.control(residCovWeighted=TRUE ))
m2b <- systemfit(formula=eqSystem, method="WLS", data=data1, control=systemfit.control(residCovWeighted=FALSE))
#m3 <- systemfit(formula=eqSystem, method="SUR", data=data1)
summary(m1a, residCov=FALSE, equations=FALSE)
summary(m1b, residCov=FALSE, equations=FALSE)
summary(m2a, residCov=FALSE, equations=FALSE)
summary(m2b, residCov=FALSE, equations=FALSE)
#summary(m3, residCov=FALSE, equations=FALSE)
# Covariance matrix of the coefficients: OLS and WLS are equal
round(m1a$coefCov,4)
round(m1b$coefCov,4)
round(m2a$coefCov,4)
round(m2b$coefCov,4)
#round(m3$coefCov,4)
m1b$coefCov/m2a$coefCov # equal
# Covariance matrix of the residuals: all are equal
round(m1a$residCov,4)
round(m1b$residCov,4)
round(m2a$residCov,4)
round(m2b$residCov,4)
#round(m3$residCov,4)
m1b$residCov/m2a$residCov # equal
# Weighting matrix in the 2-step estimator: OLS is NULL, WLS and SUR differ
m1a$residCovEst
m1b$residCovEst
round(m2a$residCovEst,4)
round(m2b$residCovEst,4)
#round(m3$residCovEst,4)
#---------- Manual estimation under no restrictions
# Now generate x, dummy and y above, estimate the models above. Then:
# Manually construct the X matrix corresponding to equation (2) from the vignette
O_N3 <- matrix(0,nrow=n,ncol=N);
X <- rbind( cbind( cbind(1,x[,c(1,N+1)]),O_N3 ,O_N3 ) ,
cbind( O_N3 ,cbind(1,x[,c(2,N+1)]),O_N3 ) ,
cbind( O_N3 ,O_N3 ,cbind(1,x[,c(3,N+1)]) ) )
#round(X,2)
# Obtain $\widehat{Cov}_{OLS}(\hat\beta)$ and compare to the output from `systemfit`
sigma2 <- var(unlist(resid(m1a)))
Omega_OLSa <- sigma2 * diag(n*N)
inv_cov_OLSa<- t(X) %*% solve(Omega_OLSa) %*% X
cov_OLSa <- solve(inv_cov_OLSa)
round(cov_OLSa,4)
round(m1a$coefCov,4)
#round(m1b$coefCov,4)
round(cov_OLSa/m1a$coefCov,4)
#round(cov_OLSa/m1b$coefCov,4)
# The ratio of estimated covariances between my solution and that of `systemfit` is constant.
# The constant approaches 1 as the sample size increases (compare n=8 to n=100 to n=300).
# I guess `systemfit` uses a small sample correction while I do not.
# Obtain $\widehat{Cov}_{WLS}(\hat\beta)$ and compare to the output from `systemfit`
Omega_WLSa <- diag(diag(m2a$residCov)) %x% diag(n)
inv_cov_WLSa<- t(X) %*% solve(Omega_WLSa) %*% X
cov_WLSa <- solve(inv_cov_WLSa)
round(cov_WLSa,4)
round(m2a$coefCov,4)
round(cov_WLSa/m2a$coefCov,4)
# The ratio of estimated covariances between my solution and that of `systemfit` is 1.
# Obtain $\widehat{Cov}_{WLS}(\hat\beta)$ and compare to the output from `systemfit`
Omega_WLSb <- diag(diag(m2b$residCov)) %x% diag(n)
inv_cov_WLSb<- t(X) %*% solve(Omega_WLSb) %*% X
cov_WLSb <- solve(inv_cov_WLSb)
round(cov_WLSb,4)
round(m2b$coefCov,4)
round(cov_WLSb/m2b$coefCov,4)
# The ratio of estimated covariances between my solution and that of `systemfit` is 1.
However, things change under restricted estimation. E.g. if we impose some of the coefficients to be equal across equations, systemfit
reports different point estimates and estimated coefficient covariance matrices for OLS and WLS. This is puzzling, given the equations (26) and (27) from the package's vignette. There, the exact same equation is used for both OLS and WLS estimators. The only thing that could differ is the $\hat\Omega_{\text{OLS}}$ vs. $\hat\Omega_{\text{WLS}}$, but we have learned they are identical. So how come the actual numbers produced by systemfit
are different for OLS and WLS?
# OLS and WLS turn out to be different when the model has linear restrictions on coefficients from different equations
Rmat0 <- matrix(0, nrow=N-1, ncol=N*3) # nrow ~ # of restrictions, ncol ~ # of unrestr. coeffs
for(i in 1:(N-1)) Rmat0[i, c(3*i,3*(i+1))] <- c(1,-1)
qvec0 <- rep(0,nrow(Rmat0))
m1r <- systemfit(formula=eqSystem, method="OLS", restrict.matrix=Rmat0, restrict.rhs=qvec0, data=data1) # restricted model
m2r <- systemfit(formula=eqSystem, method="WLS", restrict.matrix=Rmat0, restrict.rhs=qvec0, data=data1) # restricted model
summary(m1r, residCov=FALSE, equations=FALSE)
summary(m2r, residCov=FALSE, equations=FALSE)
Update 1: Perhaps the key to the answer is in footnote 3 of the vignette? The footnote says that $\hat\Omega$ may denote two different things: (1) the estimated error covariance matrix based on the first-stage OLS estimates and (2) the estimated error covariance matrix (?) based on the second-stage WLS estimates (in case of the WLS estimator only) - what is that?
We just have to figure out which is which. Now, to make sure follow, I am trying to replicate the OLS and WLS estimators manually (see the code below). I am able to replicate the point estimates of OLS. (I have not tried to replicating the estimated covariance matrix of coefficients yet.) I am also able to replicate the estimated covariance matrix of the coefficients up to a scalar multiple. The difference shrinks with sample size, so that suggests I am not using the small-sample correction that systemfit
is using.
However, I cannot replicate WLS, as I do not understand what I should use for $\hat\Omega$. If I use the same as for OLS, then of course I am not getting the same results as by (See the answer right below.)systemfit
. How do I replicate restricted WLS?
Update 2: I have found the weighting matrix $\hat\Omega$ that yields the restricted WLS result from systemfit
. As an estimate of $\Sigma$, it takes the estimated error covariance matrix from the restricted OLS and sets the off-diagonal elements to zero. This is how to replicate the point estimate of restricted WLS. Similarly, I can replicate the estimated covariance matrix of the coefficients.
# Cross-equation restrictions followed by estimation by OLS and WLS
R <- matrix(0, nrow=N-1, ncol=N*3) # nrow ~ # of restrictions, ncol ~ # of unrestr. coeffs
for(i in 1:(N-1)) R[i, c(3*i,3*(i+1))] <- c(1,-1)
q <- rep(0,nrow(R))
m1ar <- systemfit(formula=eqSystem, method="OLS", restrict.matrix=R, restrict.rhs=q, data=data1, control=systemfit.control(singleEqSigma=FALSE))
m1br <- systemfit(formula=eqSystem, method="OLS", restrict.matrix=R, restrict.rhs=q, data=data1, control=systemfit.control(singleEqSigma=TRUE ))
m2ar <- systemfit(formula=eqSystem, method="WLS", restrict.matrix=R, restrict.rhs=q, data=data1, control=systemfit.control(residCovWeighted=TRUE ))
m2br <- systemfit(formula=eqSystem, method="WLS", restrict.matrix=R, restrict.rhs=q, data=data1, control=systemfit.control(residCovWeighted=FALSE))
# Covariance matrix of the coefficients: OLS (version b with different error variances across equations) and WLS are equal
round(m1ar$coefCov,4)
round(m1br$coefCov,4)
round(m2ar$coefCov,4)
round(m2br$coefCov,4)
#round(m3r$coefCov,4)
m1br$coefCov/m2ar$coefCov # equal
# Covariance matrix of the residuals: 1a=1b and 2a=2b but 1a!=2a
round(m1ar$residCov,4)
round(m1br$residCov,4)
round(m2ar$residCov,4)
round(m2br$residCov,4)
#round(m3r$residCov,4)
m1ar$residCov/m2ar$residCov # not equal
# Weighting matrix in the 2-step estimator: OLS is NULL, WLS and SUR differ
m1ar$residCovEst
m1br$residCovEst
round(m2ar$residCovEst,4)
round(m2br$residCovEst,4)
#round(m3r$residCovEst,4)
#---------- Manual estimation under cross-equation restrictions
# Choose only one of the following group of lines
Omega <- Omega_OLSa; inv_cov <- inv_cov_OLSa # OLS, replicates OLS from `systemfit`
#Omega <- Omega_WLSa; inv_cov <- inv_cov_WLSa # WLS
#Omega <- Omega_WLSb; #inv_cov <- inv_cov_WLSb # WLS, exact same results as the line above
Omega <- m2ar$residCovEst %x% diag(n); inv_cov <- t(X) %*% solve(Omega) %*% X # replicates WLS from `systemfit`
# Construct the restricted estimator: the first part
Mat0 <- matrix(0,nrow=nrow(R),ncol=nrow(R))
A1 <- cbind(inv_cov,t(R))
A2 <- cbind(R,Mat0)
A <- rbind(A1,A2)
#round(A,3)
# Construct the restricted estimator: the second part
B1 <- t(X) %*% solve(Omega) %*% cbind(c(y))
B2 <- cbind(q)
B <- rbind(B1,B2)
#round(B,3)
# Obtain the restricted estimator by combining the parts
beta_lambda <- solve(A) %*% B
#round(beta_lambda,3)
beta <- head(beta_lambda,-length(q))
# Compare the manually obtained restricted estimator with OLS and WLS from `systemfit`
table1 <- cbind(beta,coef(m1ar),coef(m1br),coef(m2ar),coef(m2br));
colnames(table1) <- c("Manual","OLSa","OLSb","WLSa","WLSb")
rownames(table1) <- c("eq1_(Intercept)","eq1_x1","eq1_dummy", "eq2_(Intercept)","eq2_x2","eq2_dummy", "eq3_(Intercept)","eq3_x3","eq3_dummy")
# OLS1 uses common error variance across equations; OLS2 uses different error variances in different equations (just like WLS)
round(table1,3)
# Covariance matrix of the coefficients
lenb=length(beta); cov_=solve(A)[1:lenb,1:lenb]; rownames(cov_)=colnames(cov_)=rownames(table1)
round(cov_,4)
round(m1ar$coefCov,4) # for cov_ obtained by manual OLS, this is a constant. It approaches 1 with sample size.
round(m1br$coefCov,4) # for cov_ obtained by manual WLS, this is 1, which is good.
round(m2ar$coefCov,4)
#round(m2br$coefCov,4)
round(cov_/m1ar$coefCov,4)
round(cov_/m1br$coefCov,4)
round(cov_/m2ar$coefCov,4)
#round(cov_/m2br$coefCov,4)
# The ratio of 1 is good.
systemfit
package and your 'digging through it' seems to be hard (which makes sense that it creates multiple questions). I am wondering what the underlying final project/problem is that you are working on${\color{red}{^*}}$. $\endgroup$