Reputation: 163
I want to create annotated publication-ready forest plots comparing different models. How do I ensure that the labels are placed above and tight against the corresponding error bars and do not overlap?
I've also asked the RStudio community for help on March 9, 2022. Here is a link to the original post.
Thank you for your help!
Here is what I tried:
library(tidyverse)
#> Warning: package 'tibble' was built under R version 4.1.2
#> Warning: package 'tidyr' was built under R version 4.1.2
#> Warning: package 'readr' was built under R version 4.1.2
#> Warning: package 'dplyr' was built under R version 4.1.2
library(knitr)
#> Warning: package 'knitr' was built under R version 4.1.2
data <- tibble::tribble(
~term, ~n_obs, ~estimate, ~conf.low, ~conf.high, ~ci, ~p.value, ~group,
"A", 101L, 0.62, 0.497390282, 0.797429694, "0.50 to 0.80", 0.000123131, "One",
"A", 65L, 0.57, 0.468908017, 0.693458768, "0.47 to 0.69", 1.83138e-08, "Two",
"A", 36L, 0.81, 0.623804568, 1.064787867, "0.62 to 1.06", 0.133680272, "Three",
"B", 26L, 0.87, 0.665400224, 1.1489204, "0.67 to 1.15", 0.335220534, "One",
"B", 16L, 1.02, 0.755403541, 1.388386542, "0.76 to 1.39", 0.878076678, "Two",
"B", 10L, 0.29, 0.091804704, 0.978289216, "0.09 to 0.98", 0.045898245, "Three",
"C", 143L, 0.90, 0.749027775, 1.089930323, "0.75 to 1.09", 0.289131987, "One",
"C", 82L, 1.02, 0.82229374, 1.286815562, "0.82 to 1.29", 0.804649191, "Two",
"C", 61L, 0.61, 0.359730119, 1.036462037, "0.36 to 1.04", 0.06765433, "Three"
)
data %>% kable()
term | n_obs | estimate | conf.low | conf.high | ci | p.value | group |
---|---|---|---|---|---|---|---|
A | 101 | 0.62 | 0.4973903 | 0.7974297 | 0.50 to 0.80 | 0.0001231 | One |
A | 65 | 0.57 | 0.4689080 | 0.6934588 | 0.47 to 0.69 | 0.0000000 | Two |
A | 36 | 0.81 | 0.6238046 | 1.0647879 | 0.62 to 1.06 | 0.1336803 | Three |
B | 26 | 0.87 | 0.6654002 | 1.1489204 | 0.67 to 1.15 | 0.3352205 | One |
B | 16 | 1.02 | 0.7554035 | 1.3883865 | 0.76 to 1.39 | 0.8780767 | Two |
B | 10 | 0.29 | 0.0918047 | 0.9782892 | 0.09 to 0.98 | 0.0458982 | Three |
C | 143 | 0.90 | 0.7490278 | 1.0899303 | 0.75 to 1.09 | 0.2891320 | One |
C | 82 | 1.02 | 0.8222937 | 1.2868156 | 0.82 to 1.29 | 0.8046492 | Two |
C | 61 | 0.61 | 0.3597301 | 1.0364620 | 0.36 to 1.04 | 0.0676543 | Three |
data %>% rowwise() %>%
## plot with variable on the y axis and estimate (OR) on the x axis
ggplot(aes(x = estimate, y = term, color = group), alpha = .7, width = .7) +
## show the estimate as a point
geom_point(position = position_dodge(.9), size = 1.5) +
## add in an error bar for the confidence intervals
geom_errorbar(aes(xmin = conf.low, xmax = conf.high), position = position_dodge(.9), size = 1) +
## add in labels
geom_text(aes( label = paste0(estimate, " (", ci, ") n = ", n_obs), x = estimate, y = term),color = "black", position = position_dodge(.9), show.legend = FALSE, check_overlap = FALSE) +
ggplot2::theme_bw() +
scale_colour_grey() +
## show where OR = 1 is for reference as a dashed line
geom_vline(xintercept = 1, linetype = "dashed") +
ylab("") +
xlab("") +
theme(legend.position = "top")
Created on 2022-03-19 by the reprex package (v2.0.1)
Upvotes: 2
Views: 991
Reputation: 174468
You need to add a grouping variable to the text layer, since you remove its dodging variable by specifying color = 'black'
outside aes
.
Also, you can use vjust
to move the text above the bars:
data %>% rowwise() %>%
ggplot(aes(x = estimate, y = term, color = group), alpha = .7, width = .7) +
geom_point(position = position_dodge(.9), size = 1.5) +
geom_errorbar(aes(xmin = conf.low, xmax = conf.high),
position = position_dodge(.9), size = 1) +
geom_text(aes( label = paste0(estimate, " (", ci, ") n = ", n_obs),
x = estimate, y = term, group = group), color = "black",
position = position_dodge(0.9), vjust = -0.4,
show.legend = FALSE, check_overlap = FALSE) +
ggplot2::theme_bw() +
scale_colour_grey() +
geom_vline(xintercept = 1, linetype = "dashed") +
ylab("") +
xlab("") +
theme(legend.position = "top")
Upvotes: 2