Search code examples
rr-forestplot

R forestplot() function fix section widths


I want to generate multiple forest plots using the forestplot() function in R (from the package R/forestplot), and want to ensure that I can line up the text and graph sections in each so they can be usefully shown as a stacked plot, like this:

Stacked plot

(taken from R forestplot package blank lines with section headings)

but with the possibility that there may be different scales in each subplot, lining up the zero effect line in each plot.

Which attributes need changing in the forestplot() call to ensure that this occurs?

EDIT: to provide a minimum code example


library(forestplot)
library(tidyr)

cohort <- data.frame(Age = c(43, 39, 34, 55, 70, 59, 44, 83, 76, 44, 
                       75, 60, 62, 50, 44, 40, 41, 42, 37, 35, 55, 46), 
               Status = structure(c(2L, 
                                    2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 2L, 1L, 2L, 2L, 2L, 1L, 1L, 
                                    1L, 1L, 1L, 2L, 2L), levels = c("-", "+"), class = "factor"), 
               Group = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
                                   1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L), levels = c("1","2"), class = "factor"))

age.lm <- lm(Age ~ Group, data = cohort)
status.lm <- glm(Status ~ Group, data = cohort, family=binomial(link=logit))

age.data <- summary(age.lm)$coefficients[2,]
status.data <- summary(status.lm)$coefficients[2,]

age.data <- rbind(c(0,0,0,1,"Group 1", "n=15"),
                  c(age.data[1], age.data[1]-age.data[2]*1.95, age.data[1]+age.data[2]*1.95, age.data[4], "Group 2", "n=7"))
status.data <- rbind(c(0,0,0,1,"Group 1", "[+13,-2]"),
                     c(status.data[1], status.data[1]-status.data[2]*1.95, status.data[1]+status.data[2]*1.95, status.data[4], "Group 2", "[+2,-5]"))

colnames(age.data) <- c("mean","lower","upper","p-val","labeltext","numbers")
colnames(status.data) <- c("mean","lower","upper","p-val","labeltext","numbers")

age.data <- data.frame(age.data)
status.data <- data.frame(status.data)


age.data$mean <- as.numeric(age.data$mean)
age.data$lower <- as.numeric(age.data$lower)
age.data$upper <- as.numeric(age.data$upper)

status.data$mean <- exp(as.numeric(status.data$mean))
status.data$lower <- exp(as.numeric(status.data$lower))
status.data$upper <- exp(as.numeric(status.data$upper))


age.plot <- forestplot(age.data,
           labeltext = c(labeltext,numbers),
           boxsize = 0.1,
           xlog = FALSE,
           clip=c(-20,20),
           xticks=c(-20,-10,0,10,20),
           txt_gp = fpTxtGp(ticks=gpar(cex=1)),
           align=c("l","c","l"))
           
status.plot <- forestplot(status.data,
                          labeltext = c(labeltext,numbers),
                          boxsize = 0.1,
                          xlog = TRUE,
                          clip=c(1/100,100),
                          xticks=c(log(1e-2),log(1e-1),0,log(1e1),log(1e2)),
                          txt_gp = fpTxtGp(ticks=gpar(cex=1)),
                          align=c("l","c","l"))

Note that the age plot is a linear model and the status plot is a logistic model:

Age plot (linear model enter image description here

I want to be able to arrange the relative sizes of the text to the left and the plot to the right in order that the zero-effect lines (at 0 and at 1 respectively) line up so that the forest plots stack cleanly.


Solution

  • With the align argument you could left "l" align the parts of your plot, so the text can be left aligned. If you want to align your zero-effect lines you could use mar and play with the units to adjust one of the graphs. Here is a reproducible example:

    library(forestplot)
    library(tidyr)
    
    age.plot <- forestplot(age.data,
                           labeltext = c(labeltext,numbers),
                           boxsize = 0.1,
                           xlog = FALSE,
                           clip=c(-20,20),
                           xticks=c(-20,-10,0,10,20),
                           txt_gp = fpTxtGp(ticks=gpar(cex=1)),
                           align=c("l","l","l")
                           )
    
    status.plot <- forestplot(status.data,
                              labeltext = c(labeltext,numbers),
                              boxsize = 0.1,
                              xlog = TRUE,
                              clip=c(1/100,100),
                              xticks=c(log(1e-2),log(1e-1),0,log(1e1),log(1e2)),
                              txt_gp = fpTxtGp(ticks=gpar(cex=1)),
                              align=c("l","l","l"),
                              mar = unit(c(0,5,0,10.5), "mm")
                              )
    
    
    library(grid)
    grid.newpage()
    pushViewport(viewport(layout = grid.layout(2, 1)))
    pushViewport(viewport(layout.pos.row = 1))
    plot(age.plot)
    popViewport()
    pushViewport(viewport(layout.pos.row = 2))
    plot(status.plot)
    popViewport(2)
    

    Created on 2022-12-28 with reprex v2.0.2