Forest Plot in R
Forest plots are useful when conducting meta-analyses, to visualize the results of multiple scientific studies in a single graph. Forest plots are also commonly used in subgroup analyses to visualize the results of the different categories of a subgroup, or different categories from multiple subgroups on a single graph.
The Dataset
In this post, we will use dummy datasets generated in a previous post to illustrate how to create forest plots in R. For each subgroup, we will plot the difference in means between the two groups and the corresponding confidence interval.
country | diff | lowerCL | upperCL |
USA | 58.65 | 52.21 | 65.09 |
UK | 8.97 | 4.70 | 13.25 |
Canada | 17.97 | 15.11 | 20.83 |
… | … | … | … |
Creating the Forest Plots
In the following example, we will plot the difference in means (active group minus reference group) and the corresponding 95% confidence interval per subgroup category.
#Forest plot
a <- ggplot(diffci_3, aes(x=diff, y=country, color=country, shape=country)) +
geom_errorbar(aes(xmin = lowerCL, xmax = upperCL), width = 0.5) +
geom_point(size = 4) +
labs(x="Difference and 95% CI", y = "Country") +
scale_x_continuous(breaks=seq(-20,80,20), limits = c(-20,80)) +
#xlim(-10, 70) +
scale_color_manual(values = c("#4e8ca5", "#ff6d6d", "#66aa66", "#aa9966")) +
scale_shape_manual(values=c(15,16,17,18)) +
theme_classic() +
theme(legend.position = "none")
#a. Basic forest plot - without legend
a
#b. Adding a reference line on x-axis using geom_vline()
b <- a + geom_vline(xintercept = 20, linetype = "longdash")
b
#c. Making the plot more aesthetically pleasing and adjusting non-data components
c <- b + theme(legend.position = "none",
#remove y-axis line and ticks
axis.line.y = element_blank(),
axis.ticks.y = element_blank())
c
Adding Table to Forest Plot
To add one or more tables to a forest plot, we will first create the plot and tables separately. Then we will use put the plot and tables together using the gridExtra package. This can be installed and loaded as follows:
- install.packages(“gridExtra”)
- library(gridExtra)
1. Making the Plot
#Make plot
p <- ggplot(diffci_3, aes(x=diff, y=country, color=country, shape=country)) +
geom_errorbar(aes(xmin = lowerCL, xmax = upperCL), width = 0.5) +
geom_point(size = 3) +
labs(x="Difference and 95% CI", y = "Country") +
scale_x_continuous(breaks=seq(-20,80,20), limits = c(-20,80)) +
scale_color_manual(values = c("#6699ff", "#638160", "#d35e56", "#aa9966")) +
scale_shape_manual(values=c(15,16,17,18)) +
theme_classic() +
geom_vline(xintercept = 20, linetype = "longdash") +
theme(legend.position = "none",
#remove y-axis line and ticks
axis.line.y = element_blank(),
axis.ticks.y = element_blank())
p
2. Making the Tables
#Make table 1
t1 <- ggplot(data=diffci_3) +
geom_text(aes(y=country, x=1, label= paste0(round(diff, digits=2))), vjust=0) +
ggtitle("Difference") +
xlab(" ") +
theme_classic(base_size=13) +
theme(
axis.line = element_blank(),
axis.text.y = element_blank(),
axis.ticks = element_blank(),
axis.title.y = element_blank(),
axis.text.x = element_text(color="white"),
plot.title = element_text(size=12, hjust =0.5)
)
t1
#Make table2
t2 <- ggplot(data=diffci_3) +
geom_text(aes(y=country, x=1, label= paste0("(",round(lowerCL, digits=2),"; ", round(upperCL, digits=2),")")), vjust=0) +
ggtitle("90% CI") +
xlab(" ") +
theme_classic(base_size=13) +
theme(
axis.line = element_blank(),
axis.text.y = element_blank(),
axis.ticks = element_blank(),
axis.title.y = element_blank(),
axis.text.x = element_text(color="white"),
plot.title = element_text(size=12, hjust =0.5)
)
t2
3. Put Plot and Tables Together
#Merge plot and tables
#The size of 1 (the plot) is biggest followed by size of 2 (Table 1) then 3 (Table 2)
mlayout <- rbind(c(1,1,1,1,2,3,3),
c(1,1,1,1,2,3,3),
c(1,1,1,1,2,3,3))
#Plot and tables together
grid.arrange(p, t1, t2, layout_matrix = mlayout)
#The plot and tables can also be place together without using a layout matrix. See below
Alternatively, the plot and tables can be placed together with grid.arrange() without using the layout matrix. The layout matrix above can be replaced with the parameter widths=c(4,1,2), specifying the width of each item. As shown below, the width of the plot will be four times longer than that of Table 1, and the width of Table 1 is half that of Table 2:
grid.arrange(p, t1, t2, widths=c(4,1,2))