Another meta-sandwich

In a previous post, I provided some code to do robust variance estimation with metafor and sandwich. Here’s another example, replicating some more of the calculations from Tanner-Smith & Tipton (2013). (See here for the complete code.)

As a starting point, here are the results produced by the robumeta package:

library(grid)
library(robumeta)

data(corrdat)
rho <- 0.8

HTJ <- robu(effectsize ~ males + college + binge,
            data = corrdat, 
            modelweights = "CORR", rho = rho,
            studynum = studyid,
            var.eff.size = var, small = FALSE)
HTJ
## RVE: Correlated Effects Model  
## 
## Model: effectsize ~ males + college + binge 
## 
## Number of studies = 39 
## Number of outcomes = 172 (min = 1 , mean = 4.41 , median = 4 , max = 18 )
## Rho = 0.8 
## I.sq = 75.08352 
## Tau.sq = 0.1557714 
## 
##                Estimate  StdErr t-value dfs P(|t|>) 95% CI.L 95% CI.U Sig
## 1 X.Intercept.  0.31936 0.27784   1.149  35   0.258  -0.2447  0.88340    
## 2        males -0.00331 0.00376  -0.882  35   0.384  -0.0109  0.00431    
## 3      college  0.41226 0.18685   2.206  35   0.034   0.0329  0.79159  **
## 4        binge  0.13774 0.12586   1.094  35   0.281  -0.1178  0.39326    
## ---
## Signif. codes: < .01 *** < .05 ** < .10 *
## ---

To exactly re-produce the results with metafor, I’ll need to use the weights proposed by HTJ. In their approach to the correlated effects case, effect size \(i\) from study \(j\) receives weight equal to \(\left[\left(v_{\cdot j} + \hat\tau^2\right)(1 + (k_j - 1) \rho)\right]^{-1}\), where \(v_{\cdot j}\) is the average sampling variance of the effect sizes from study \(j\), \(\hat\tau^2\) is an estimate of the between-study variance, \(k_j\) is the number of correlated effects in study \(j\), and \(\rho\) is a user-specified value of the intra-study correlation. However, it appears that robumeta actually uses a slightly different set weights, which are equivalent to taking \(\rho = 1\). I calculate the latter weights, fit the model in metafor, and output the robust standard errors and \(t\)-tests:

devtools::source_gist(id = "11144005", filename = "metafor-sandwich.R")

corrdat <- within(corrdat, {
  var_mean <- tapply(var, studyid, mean)[studyid]
  k <- table(studyid)[studyid]
  var_HTJ <- as.numeric(k * (var_mean + as.numeric(HTJ$mod_info$tau.sq)))
})

meta1 <- rma.mv(effectsize ~ males + college + binge, 
                V = var_HTJ, 
                data = corrdat, method = "FE")
meta1$cluster <- corrdat$studyid
RobustResults(meta1)
## 
## t test of coefficients:
## 
##           Estimate Std. Error t value Pr(>|t|)  
## intrcpt  0.3193586  0.2778360  1.1494  0.25816  
## males   -0.0033143  0.0037573 -0.8821  0.38374  
## college  0.4122631  0.1868489  2.2064  0.03401 *
## binge    0.1377393  0.1258637  1.0944  0.28127  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

One could specify a similar (though not exactly identical model) in metafor as follows. In the HTJ approach, \(\rho\) represents the total correlation induced by both the within-study sampling error and intra-study correlation in true effects. In contrast, the metafor approach would take \(\rho\) to be correlation due to within-study sampling error alone. I’ll first need to create a block-diagonal covariance matrix given a user-specified value of \(\rho\).

library(Matrix)
equicorr <- function(x, rho) {
  corr <- rho + (1 - rho) * diag(nrow = length(x))
  tcrossprod(x) * corr 
} 
covMat <- as.matrix(bdiag(with(corrdat, tapply(var_mean, studyid, equicorr, rho = 0.8, simplify = FALSE))))

Passing this block-diagonal covariance matrix to rma.mv, I now estimate the model

\[T_{ij} = \mathbf{X}_{ij} \beta + \nu_i + e_{ij},\]

where \(Var(\nu_i) = \sigma^2\), \(Var(e_{ij}) = v_{ij}\), and \(Cor(e_{ij}, e_{ik}) = \rho\). Note that \(\sigma^2\) is now estimated via REML.

meta2 <- rma.mv(yi = effectsize ~ males + college + binge, 
                V = covMat, random = ~ 1 | studyid, 
                data = corrdat,
                method = "REML")
c(sigma.sq = meta2$sigma2)
##  sigma.sq 
## 0.2477825

The between-study heterogeneity estimate is considerably larger than the moment estimate from robumeta. Together with the difference in weighting, this leads to some changes in the coefficient estimates and their estimated precision:

RobustResults(meta2)
## 
## t test of coefficients:
## 
##           Estimate Std. Error t value Pr(>|t|)   
## intrcpt -0.8907096  0.4148219 -2.1472 0.038783 * 
## males    0.0163074  0.0055805  2.9222 0.006052 **
## college  0.3180139  0.2273396  1.3988 0.170658   
## binge   -0.0984026  0.0897269 -1.0967 0.280265   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

It is important to keep in mind that the estimate of between-study heterogeneity depends on the posited model for the covariance structure, including the assumed value of \(\rho\). HTJ recommend conducting sensitivity analysis across a range of values for the within-study effect correlation. Re-calculating the value of \(\sigma^2\) for \(\rho\) between 0.0 and 0.9 yields the following:

sigma2 <- function(rho) {
  covMat <- as.matrix(bdiag(with(corrdat, tapply(var_mean, studyid, equicorr, rho = rho, simplify = FALSE))))
  rma.mv(yi = effectsize ~ males + college + binge, 
                  V = covMat, random = ~ 1 | studyid, 
                  data = corrdat,
                  method = "REML")$sigma2
}
rho_sens <- seq(0,0.9,0.1)
sigma2_sens <- sapply(rho_sens, sigma2)
cbind(rho = rho_sens, sigma2 = round(sigma2_sens, 4))
##       rho sigma2
##  [1,] 0.0 0.2519
##  [2,] 0.1 0.2513
##  [3,] 0.2 0.2507
##  [4,] 0.3 0.2502
##  [5,] 0.4 0.2497
##  [6,] 0.5 0.2492
##  [7,] 0.6 0.2487
##  [8,] 0.7 0.2482
##  [9,] 0.8 0.2478
## [10,] 0.9 0.2474

The between-study heterogeneity is quite insensitive to the assumed value of \(\rho\).

The difference between the results based on metafor versus on robumeta appears to be due to the subtle difference in the weighting approach: metafor uses block-diagonal weights that contain off-diagonal terms for effects drawn from a common study, whereas robumeta uses entirely diagonal weights.

comments powered by Disqus

Related