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

R语言ggplot2如何为多指标时序图添加多个并行Y轴

ggplot2 原生仅支持1个主Y轴+1个次Y轴的双轴配置,没有内置3个及以上并行Y轴的功能——这是官方的设计选择,核心原因是多Y轴可视化很容易通过调整刻度范围制造虚假相关性误导读者,但针对临床场景多生物标志物时间趋势同步对比的刚需,可以通过以下两种成熟方案实现需求,两种方案都完全适配你按RID循环批量出图的逻辑。

首先注意你提供的示例数据存在笔误:TIME向量中0,07为英文逗号,需要改为小数点0.07才能正常运行。

方案1:patchwork 分面拼接(推荐临床场景使用)

这个方案不需要对原始测量值做任何缩放,每个指标的Y轴完全独立,刻度范围自动适配指标实际取值,不会出现读值误差,是稳定性最高、可读性最好的方案。核心逻辑是为每个COMP指标单独绘制趋势图,沿X轴严格对齐拼接,隐藏冗余的坐标轴元素,视觉上和多并行Y轴效果一致。

# 加载依赖包
library(ggplot2)
library(patchwork)
library(dplyr)

# 配置每个COMP对应的参数:可替换为真实临床指标名称、自定义配色
comp_config <- tibble(
  COMP = c(0, 1, 2, 3),
  comp_name = c("体温(℃)", "血压(mmHg)", "心率(次/分)", "呼吸频率(次/分)"),
  line_color = c("red", "blue", "green", "yellow")
)

graph_fx_multi <- function(input_df) {
  current_rid <- unique(input_df$RID)
  # 拆分不同COMP的数据集
  df_split <- input_df %>%
    left_join(comp_config, by = "COMP") %>%
    group_split(COMP)
  x_range <- range(input_df$TIME)
  
  # 逐个生成单指标子图
  plot_list <- lapply(seq_along(df_split), function(idx) {
    sub_data <- df_split[[idx]]
    p <- ggplot(sub_data, aes(x = TIME, y = NDV)) +
      geom_path(color = sub_data$line_color[1], linewidth = 1) +
      geom_point(color = sub_data$line_color[1], size = 2) +
      scale_x_continuous(limits = x_range, name = "Time (h)") +
      scale_y_continuous(name = sub_data$comp_name[1]) +
      theme_bw(base_size = 10) +
      theme(
        axis.title.y = element_text(color = sub_data$line_color[1], face = "bold"),
        axis.text.y = element_text(color = sub_data$line_color[1]),
        plot.margin = margin(3,3,3,3)
      )
    # 隐藏非首图的Y轴元素,隐藏非末图的X轴元素
    if (idx != 1) {
      p <- p + theme(
        axis.title.y = element_blank(),
        axis.text.y = element_blank(),
        axis.ticks.y = element_blank()
      )
    }
    if (idx != length(df_split)) {
      p <- p + theme(
        axis.title.x = element_blank(),
        axis.text.x = element_blank(),
        axis.ticks.x = element_blank()
      )
    }
    # 给首图添加RID标题
    if (idx == 1) {
      p <- p + labs(title = paste0("RID = ", current_rid))
    }
    return(p)
  })
  
  # 横向拼接所有子图,强制X轴对齐
  wrap_plots(plot_list, nrow = 1)
}

# 测试运行(先修正原始数据的笔误)
RID <- c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
COMP <- c(0, 2, 1, 0, 1, 3, 0, 1, 3, 3, 2, 1, 3, 0, 1)
TIME <- c(0.01, 0.03, 0.03, 0.05, 0.06, 0.07, 0.07, 0.07, 0.08, 0.11, 0.11, 0.15, 0.16, 0.19, 0.21)
NDV <- c(23, 34, 54, 11, 23, 76, 45, 38, 22, 45, 98, 19, 10, 11, 56)
df <- data.frame(RID, COMP, TIME, NDV)

graph_fx_multi(df)

批量出图时,只需要用group_split(RID)按患者ID拆分总数据集,循环调用绘图函数再用ggsave保存即可。

方案2:ggh4x 单面板多轴(视觉更接近传统多Y轴)

如果需要把所有折线放在同一个绘图面板内,可以使用ggh4x扩展包提供的多轴叠加功能。注意这个方案需要先把不同量纲的NDV值线性缩放到统一区间,再通过坐标轴反向变换还原原始刻度,配置过程相对繁琐,批量出图时需要提前校验每个指标的缩放参数避免刻度错位。

# 安装加载依赖
# install.packages("ggh4x")
library(ggh4x)

graph_fx_ggh4x <- function(input_df) {
  current_rid <- unique(input_df$RID)
  # 对每个COMP的测量值做0-1归一化
  df_scaled <- input_df %>%
    group_by(COMP) %>%
    mutate(
      NDV_scaled = scales::rescale(NDV, to = c(0, 1)),
      val_min = min(NDV),
      val_max = max(NDV)
    ) %>%
    ungroup() %>%
    left_join(comp_config, by = "COMP")
  
  # 生成每个指标对应的独立Y轴配置
  sec_axis_list <- lapply(unique(df_scaled$COMP), function(c_val) {
    sub <- df_scaled %>% filter(COMP == c_val)
    # 定义归一化和反归一化函数
    reverse_scale <- function(x) (x - sub$val_min[1])/(sub$val_max[1] - sub$val_min[1])
    restore_scale <- function(x) x*(sub$val_max[1] - sub$val_min[1]) + sub$val_min[1]
    
    sec_axis(
      transform = reverse_scale,
      name = sub$comp_name[1],
      breaks = seq(sub$val_min[1], sub$val_max[1], length.out = 5),
      labels = ~round(.x, 1)
    )
  })
  
  ggplot(df_scaled, aes(x = TIME, y = NDV_scaled, color = as.factor(COMP))) +
    geom_path(linewidth = 1) +
    geom_point(size = 2) +
    labs(
      title = paste0("RID = ", current_rid),
      x = "Time (h)",
      color = "监测指标"
    ) +
    scale_y_continuous(
      name = comp_config$comp_name[comp_config$COMP == 0],
      sec.axis = do.call(dup_axis, c(list(name = NULL, labels = NULL), sec_axis_list))
    ) +
    scale_color_manual(values = setNames(comp_config$line_color, comp_config$COMP)) +
    theme_bw(base_size = 10) +
    theme(
      axis.title.y = element_text(color = comp_config$line_color[comp_config$COMP == 0], face = "bold"),
      # 配置右侧各轴的标题颜色
      ggh4x.axis.sec_axis.title.y.i = setNames(
        lapply(comp_config$line_color[-1], function(col) element_text(color = col, face = "bold")),
        1:3
      )
    )
}

# 测试运行
graph_fx_ggh4x(df)

示例输出图

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 16:27:38