You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ggplot2分面森林图顶部添加统一的风险比方向标注

解决ggplot2分面森林图顶部仅添加一次效应方向标注的问题

问题说明

用ggplot2绘制分面森林图时,希望在整个绘图区域的顶部添加风险比(HR)的效应方向标注(如“有利于药物”“有利于对照”),但使用annotate()会导致每个分面顶部都重复显示标注;尝试draw_plot_label()又需要绝对坐标,无法适配不同图表的x轴数值,需要一种能自动适配且仅显示一次标注的方案。

解决方案:使用patchwork包拼接标注与森林图

通过将标注文本做成一个无背景的独立ggplot对象,再与森林图垂直拼接,既保证标注仅出现一次,又能自动匹配森林图的x轴范围,无需手动调整坐标,适合批量生成图表的场景。

完整代码

library(ggplot2)
library(patchwork)

# 样本数据
final_cox_dat <- data.frame(
  comparator = rep(c("Group A", "Group B", "Group C"), each = 2),
  estimate = c(1.2, 1.5, 0.8, 0.9, 1.4, 1.7),
  conf.low = c(1.0, 1.3, 0.7, 0.8, 1.3, 1.5),
  conf.high = c(1.5, 1.7, 0.9, 1.0, 1.6, 1.9),
  model = rep(c("Weighted", "Unweighted"), times = 3)
)

# 绘制核心森林图
forest_plot <-
  ggplot(
    final_cox_dat,
    aes(
      x = estimate, y = comparator,
      xmin = conf.low, xmax = conf.high, 
      color = model
    )
  ) +
  geom_pointrange(position = position_dodge(width = 0.8)) +
  geom_vline(xintercept = 1, linetype = "dashed") +
  facet_wrap(~comparator, ncol = 1, scales = "free_y") +
  labs(
    title = NULL,
    x = "Hazard Ratio (95% CI)",
    y = NULL
  ) +
  theme_minimal() +
  theme(
    strip.text = element_blank(),
    legend.title = element_blank(),
    legend.position = "bottom",
    axis.title.y = element_blank(),
    axis.ticks.y = element_blank()
  ) +
  scale_color_manual(
    values = c("Unweighted" = "red", "Weighted" = "blue")
  ) +
  geom_text(
    aes(
      x = max(conf.high) + 0.05,
      group = model,
      label = sprintf(
        "%0.2f (%0.2f, %0.2f)",
        estimate, conf.low, conf.high
      )
    ),
    hjust = 0, vjust = 0.5, size = 4.5, color = "black",
    position = position_dodge(width = 0.8)
  ) +
  scale_x_continuous(breaks = seq(0, max(final_cox_dat$conf.high + 0.2), by = 0.2), expand = expansion(mult = 0.1)) +
  theme(text = element_text(size = 18))  +
  guides(color = guide_legend(reverse=TRUE))

# 创建顶部标注的无背景文本图,匹配森林图x轴范围
annotation_plot <- ggplot() +
  geom_text(aes(x = 1, y = 1, label = "有利于药物"), hjust = 1.5, size = 6) +
  geom_text(aes(x = 1, y = 1, label = "有利于对照"), hjust = -0.5, size = 6) +
  scale_x_continuous(limits = ggplot_build(forest_plot)$layout$panel_params[[1]]$x.range) +
  theme_void() # 移除所有无关元素

# 拼接标注图与森林图,标注仅占极小高度
final_plot <- annotation_plot + forest_plot + plot_layout(ncol = 1, heights = c(0.05, 0.95))

# 输出最终图
print(final_plot)

方案优势

  1. 标注仅在整体顶部显示一次,不会随分面重复
  2. 自动匹配森林图的x轴范围,无需手动调整坐标,适配不同数据生成的图表
  3. 代码结构清晰,适合批量生成数十份图表的自动化场景

替代方案:使用grid包添加全局文本

如果不想引入patchwork包,可通过grid包直接在绘图设备的全局视口添加文本:

library(ggplot2)
library(grid)

# 先绘制森林图(代码同前,省略重复部分)
print(forest_plot)

# 获取森林图的x轴范围
x_range <- ggplot_build(forest_plot)$layout$panel_params[[1]]$x.range

# 将HR=1的位置转换为视口坐标比例
hr1_pos <- (1 - x_range[1]) / (x_range[2] - x_range[1])

# 在顶部添加标注
grid.text("有利于药物", 
          x = unit(hr1_pos + 0.1, "npc"), # 相对于HR=1的位置向右偏移
          y = unit(1, "npc") - unit(0.02, "npc"), 
          size = 6)

grid.text("有利于对照", 
          x = unit(hr1_pos - 0.1, "npc"), # 相对于HR=1的位置向左偏移
          y = unit(1, "npc") - unit(0.02, "npc"), 
          size = 6)

内容的提问来源于stack exchange,提问作者krtbris

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 03:12:11