0

I was wondering if there might be a way to turn the following part of the OUTPUT of the res and res2 objects into a data.frame?

Note: answer below works with res but not res2.

A functional answer is appreciated as the data below is just toy.

library(metafor)

dat <- dat.konstantopoulos2011
res <- rma.mv(yi, vi, random = ~ 1 | district/school, data=dat)

#== OUTPUT (CAN WE TURN ONLY BELOW PART INTO A data.frame?):

#Variance Components:

#            estim    sqrt  nlvls  fixed           factor 
#sigma^2.1  0.0651  0.2551     11     no         district 
#sigma^2.2  0.0327  0.1809     56     no  district/school 

#Test for Heterogeneity:
#Q(df = 55) = 578.8640, p-val < .0001

# AND

res2 <- rma.mv(yi, vi, random = ~ factor(school) | district, data=dat)

#== OUTPUT (CAN WE TURN ONLY BELOW PART INTO A data.frame?):
#Variance Components:

#outer factor: district       (nlvls = 11)
#inner factor: factor(school) (nlvls = 11)

#            estim    sqrt  fixed 
#tau^2      0.0978  0.3127     no 
#rho        0.6653             no 

#Test for Heterogeneity:
#Q(df = 55) = 578.8640, p-val < .0001
2
  • 2
    You can check if it is compatible with broom package Commented Oct 6, 2021 at 2:07
  • @ViníciusFélix, if you can possibly post a solution, I would be glad to try it out. Commented Oct 6, 2021 at 2:09

2 Answers 2

1

If there is no default/standard way to extract the data then you can manipulate the output using capture.output.

return_data <- function(res) {
  tmp <- capture.output(res)
  #data start from second line after "Variance Components:"
  start <- which(tmp == "Variance Components:") + 2
  index <- which(tmp == "") 
  #Data ends before the empty line after "Variance Components:"
  end <- index[which.max(index > start)] - 1
  data <- read.table(text = paste0(tmp[start:end], collapse = '\n'), header = T)
  heterogeneity_index <- which(tmp == "Test for Heterogeneity:") + 1
  list(data = data, heterogeneity = tmp[heterogeneity_index])
}

res <- rma.mv(yi, vi, random = ~ 1 | district/school, data=dat)
return_data(res)

#$data
#           estim   sqrt nlvls fixed          factor
#sigma^2.1 0.0651 0.2551    11    no        district
#sigma^2.2 0.0327 0.1809    56    no district/school

#$heterogeneity
#[1] "Q(df = 55) = 578.8640, p-val < .0001"
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks! Can we possibly make this into a function to work with other models like: res2 <- rma.mv(yi, vi, random = ~ factor(school) | district, data=dat)
I see that res and res2 are quite different. Can't really generalise them and put them in a function.
Ok, what +2 and -1? If we assume all objects are like res but with more lines of output, can we make that into a function?
I added some comments in the answer and made it into a function.
Added res2 to the question. I also played around with +2 for res2 but could capture the variance component part correctly. Any idea to fix this?
0

Would this suit your purposes? The 'Test for Heterogeneity' doesn't really fit in the dataframe, so I added it as a seperate column and it gets duplicated as a result. I'm not sure how else you could do it.

library(tidyverse)
#install.packages("metafor")
library(metafor)
#> Loading required package: Matrix
#> 
#> Attaching package: 'Matrix'
#> The following objects are masked from 'package:tidyr':
#> 
#>     expand, pack, unpack
#> 
#> Loading the 'metafor' package (version 3.0-2). For an
#> introduction to the package please type: help(metafor)

dat <- dat.konstantopoulos2011
res <- rma.mv(yi, vi, random = ~ 1 | district/school, data=dat)
res
#> 
#> Multivariate Meta-Analysis Model (k = 56; method: REML)
#> 
#> Variance Components:
#> 
#>             estim    sqrt  nlvls  fixed           factor 
#> sigma^2.1  0.0651  0.2551     11     no         district 
#> sigma^2.2  0.0327  0.1809     56     no  district/school 
#> 
#> Test for Heterogeneity:
#> Q(df = 55) = 578.8640, p-val < .0001
#> 
#> Model Results:
#> 
#> estimate      se    zval    pval   ci.lb   ci.ub 
#>   0.1847  0.0846  2.1845  0.0289  0.0190  0.3504  * 
#> 
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

vc <- cbind(estim = res$sigma2,
            sqrt = res$sigma,
            nlvls = res$s.nlevels, 
            fixed = ifelse(res$vc.fix$sigma2, "yes", "no"), 
            factor = res$s.names,
            R = ifelse(res$Rfix, "yes", "no"),
            Test_for_heterogeneity = paste0("Q(df = ", res$k - res$p, ") = ", metafor:::.fcf(res$QE, res$digits[["test"]]), ", p-val ", metafor:::.pval(res$QEp, 
                                                                                                                               res$digits[["pval"]], showeq = TRUE, sep = " "))
            
)
rownames(vc) <- c("sigma^2.1", "sigma^2.2")
result <- as.data.frame(vc)
result
#>      estim                nlvls fixed factor            R    Test_for_heterogeneity                
#> sigma^2.1 "0.0650619442753117" "11"  "no"  "district"        "no" "Q(df = 55) = 578.8640, p-val < .0001"
#> sigma^2.2 "0.0327365170279351" "56"  "no"  "district/school" "no" "Q(df = 55) = 578.8640, p-val < .0001"

Created on 2021-10-06 by the reprex package (v2.0.1)

2 Comments

Really appreciate your time and effort. But as I indicated in my post I 'm looking for a functional (and possibly efficient) solution. Part of me says capture.output() is the solution.
I think @ronak-shah's solution is what you're looking for :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.