As per the question, I want to add text annotation in an arbitrary location in the generated forest plot:
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),
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)
age.data <- summary(age.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"))
colnames(age.data) <- c("mean","lower","upper","p-val","labeltext","numbers")
age.data <- data.frame(age.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)
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"))
print(age.plot)
This generates the following plot: but say I wanted to add text to this that is not part of the forestplot, like:
Is there a way of doing this by adding to the forestplot? R/forestplot uses grid graphics, but text("Text", location) doesn't work.
Of course, I could add everything in using an image editor, but this gets tedious with a lot of things to annotate, and I'd much prefer a procedural way of doing this with text that is appropriate to the forestplot (which would make it easier to line up what I need to add)
The reason I'm asking is because I have a two-group comparison forestplot, and the effect sizes/betas can't get added automatically so I need to add them in afterwards.
The image produced when you print
your forest plot object is created with the grid graphics system, so base graphics calls like text()
don't work. Instead, you would need to do something like:
library(grid)
print(age.plot)
grid.text("Text", 0.4, 0.45, gp = gpar(col = "red", font = 3, cex = 3))
Note that the x, y co-ordinates are in npc space (i.e. scaled from {x = 0, y = 0} at bottom left of the plotting window to {x = 1, y = 1} at the top right.
Personally, I think it is preferable to do the whole thing in ggplot
. This way, you have the ability to very easily style, manipulate, combine, store and save the plot, as well as having access to an entire ecosystem of extensions. You can also annotate
the plot in user co-ordinates. For example, we can create a near-identical plot using age.data
with the text annotation stored as part of the plot itself like this:
library(ggplot2)
age.plot <- ggplot(age.data, aes(mean, paste0(labeltext, " ", numbers))) +
geom_vline(xintercept = 0, col = "gray") +
geom_linerange(aes(xmin = lower, xmax = upper)) +
geom_point(shape = 22, fill = "black", size = 9) +
annotate("text", label = "Text", x = -15, y = 1.3, size = 10,
colour = "red", fontface = "italic") +
scale_x_continuous(NULL, limits = c(-26, 20)) +
scale_y_discrete(NULL, limits = ~rev(.x)) +
theme_classic(20) +
theme(axis.line.y = element_blank(),
axis.ticks.y = element_blank(),
axis.text.y = element_text(hjust = 0))
So now we get
age.plot