5

Assume the code below (as given in Viechtbauer, 2010):

library(metafor)
data("dat.bcg", package = "metafor")
dat <- escalc(measure = "RR", ai = tpos, bi = tneg, ci = cpos, di = cneg, data = dat.bcg, append = TRUE)
res <- rma(ai = tpos, bi = tneg, ci = cpos, di = cneg, data = dat, measure = "RR")
forest(res, slab = paste(dat$author, dat$year, sep = ", "), xlim = c(-16, 6), at = log(c(0.05, 0.25, 1, 4)), atransf = exp, ilab = cbind(dat$tpos, dat$tneg, dat$cpos, dat$cneg), ilab.xpos = c(-9.5, -8, -6, -4.5), cex = 0.75)
op <- par(cex = 0.75, font = 2)
text(c(-9.5, -8, -6, -4.5), 15, c("TB+", "TB-", "TB+", "TB-"))
text(c(-8.75, -5.25), 16, c("Vaccinated", "Control"))
text(-16, 15, "Author(s) and Year", pos = 4)
text(6, 15, "Relative Risk [95% CI]", pos = 2)
par(op)

This gives a forest graph as below:

Forest plot

So how can I change the format of confidence intervals in the graph? Is it possible to replace brackets with parentheses and use "to" instead of ","? How about using "-" or long hypen instead of ","? This should change i.e. [0.13, 1.26] to (0.13 to 1.26) or (0.13 – 1.26)

M. Er
  • 881
  • 1
  • 7
  • 13
  • I'll consider adding this as an optional feature (changing brackets to parentheses and/or the "separation" symbol for the CIs). But the various ``forest()`` functions already have so many arguments, I am a bit hesitant to add even more. – Wolfgang Aug 11 '16 at 07:34
  • Actually this is not my personal preference, but some journals nowadays ask for that! – M. Er Aug 11 '16 at 15:50
  • I also have two more questions, if you don't mind giving some tips for them: 1- By which option I can add length to the CI lines, so that they don't appear as arrows? 2- I use `dev.copy()` to save the forest graph. The saved graph has big space between the columns, unless I change the ratios in `dev.copy()`. Is there any options for changing the spaces in forest graph? – M. Er Aug 11 '16 at 20:12
  • Did you try using `png` or `pdf` with width and height arguments? – IRTFM Aug 11 '16 at 21:47
  • 1. Arrows are drawn if the CI bounds are beyond the axis limits. So increase the axis limits by using wider ``at`` values (or ``alim``). 2. I don't quite understand what you are asking. I never use ``dev.copy()``. As suggested by @42- use ``png`` or ``pdf`` (or whatever device you prefer) and set the ``width`` and ``height`` arguments. – Wolfgang Aug 11 '16 at 21:51
  • Thanks again. For increasing the axis limits I used `at` and it worked. The only problem is that the lines of confidence intervals column interfere the numbers in Relative Risk CI column. Is there any option to change the position where the column of CI lines begins? This way I can move this column a bit more to left so that it does not interfere Relative Risk CI column. – M. Er Aug 13 '16 at 09:19

2 Answers2

5

You need to do some hacking of the code for forest.rma. Several steps:

After displaying the current version of the code by typing the function name:

 forest.rma   # Copy the name and the code and paste into the console 
              #  Add an assignment operator `<-`
              # leave off the bytecode and environment notations at the bottom

Or you can do this in an editor, which would probably be the preferred method since you might then want to save this code to a .Rprofile file.

1) Add parameters to the argument list:

forest.rma <- 
function (x, annotate = TRUE, addfit = TRUE, addcred = FALSE, 
    showweights = FALSE, xlim, alim, clim, ylim, at, steps = 5, 
    level = x$level, digits = 2, refline = 0, xlab, slab, mlab, 
    ilab, ilab.xpos, ilab.pos, order, transf, atransf, targs, 
    rows, efac = 1, pch = 15, psize, col, border, lty, cex, cex.lab, 
    cex.axis, annosep = " , ", bkt=c("[", "]"), ...) 
{  #  ....not showing all the _long_ function body 
   # Scroll down to almost the bottom of the function body

2) Find and change arguments to the annotext cbind-assignment. There are several places where annotext might get constructed, but only one of them matches your "format target". Find the one that looks like this:

# annotext <- cbind(annotext[, 1], " [ ", annotext[, 
#                2], " , ", annotext[, 3], " ]")

Change to this:

annotext <- cbind(annotext[, 1], bkt[1], annotext[, 
                 2], annosep, annotext[, 3], bkt[2] )
# hit enter to get the modification to hold in your workspace

3) Now assign the correct environment to the function so it can play well with its siblings:

environment(forest.rma) <- environment(forest.default)
# if you forget this step you see this error:

Error in forest.rma(res, slab = paste(dat$author, dat$year, sep = ", "), : could not find function ".setlab"

And call it with the new arguments of your choosing:

png(); forest(res, slab = paste(dat$author, dat$year, sep = ", "), xlim = c(-16, 6), at = log(c(0.05, 0.25, 1, 4)), atransf = exp, ilab = cbind(dat$tpos, dat$tneg, dat$cpos, dat$cneg), ilab.xpos = c(-9.5, -8, -6, -4.5), cex = 0.75, annosep=" to ", bkt = c( "(", ")" ) )
op <- par(cex = 0.75, font = 2)
text(c(-9.5, -8, -6, -4.5), 15, c("TB+", "TB-", "TB+", "TB-"))
text(c(-8.75, -5.25), 16, c("Vaccinated", "Control"))
text(-16, 15, "Author(s) and Year", pos = 4)
text(6, 15, "Relative Risk [95% CI]", pos = 2)
dev.off()

enter image description here

IRTFM
  • 258,963
  • 21
  • 364
  • 487
2

Here is a solution that does not require changing the code of the forest.rma() function. I use annotate=FALSE so that the function does not annotate the forest plot and instead add those annotations myself.

library(metafor)
data("dat.bcg", package = "metafor")
dat <- escalc(measure = "RR", ai = tpos, bi = tneg, ci = cpos, di = cneg, data = dat.bcg, append = TRUE)

res <- rma(ai = tpos, bi = tneg, ci = cpos, di = cneg, data = dat, measure = "RR")

### note the use of annotate=FALSE in forest()
forest(res, slab = paste(dat$author, dat$year, sep = ", "), xlim = c(-16, 6), 
       at = log(c(0.05, 0.25, 1, 4)), atransf = exp, 
       ilab = cbind(dat$tpos, dat$tneg, dat$cpos, dat$cneg), ilab.xpos = c(-9.5, -8, -6, -4.5), 
       cex = 0.75, annotate=FALSE) 

op <- par(cex = 0.75, font = 2)
text(c(-9.5, -8, -6, -4.5), 15, c("TB+", "TB-", "TB+", "TB-"))
text(c(-8.75, -5.25), 16, c("Vaccinated", "Control"))
text(-16, 15, "Author(s) and Year", pos = 4)
text(6, 15, "Relative Risk [95% CI]", pos = 2)

### add annotations manually
tmp <- summary(dat, transf=exp)[,c("yi","ci.lb","ci.ub")] ### for the individual studies
tmp <- rbind(tmp, with(predict(res, transf=exp), c(pred, ci.lb, ci.ub))) ### add model estimate and CI bounds
sav <- apply(tmp, 2, formatC, format="f", digits=2)
annotext <- apply(sav, 1, function(x) {paste0(x[1], " (", x[2], " to ", x[3], ")")})
text(6, c(res$k:1, -1), annotext, pos=2, font=1)

par(op)
Wolfgang
  • 2,810
  • 2
  • 15
  • 29
  • Thanks for the neat code. Actually it's not my personal preference, but some journals now ask for that. I also have two more questions, if you don't mind giving some tips for that: – M. Er Aug 11 '16 at 20:04