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

如何在R中自适应设置双Y轴时间序列图的轴限值?

自适应双Y轴时间序列图实现(R语言)

示例数据集

df <- structure(list(date = c("2021-07-31", "2021-08-31", "2021-09-30", 
"2021-10-31", "2021-11-30", "2021-12-31", "2022-01-31", "2022-02-28", 
"2022-03-31", "2022-04-30", "2022-05-31"), PMI = c(52.4, 48.9, 
51.7, 50.8, 52.2, 52.2, 51, 51.2, 48.8, 62.7, 48.4), Exchange_rate = c(5.1, 
5.1, 4.9, 4.9, 5, 5.1, 5.3, 5.5, 5.8, 6.1, 5.9), BCI = c(54.6, 
50, 54.5, 51.6, 49.2, 45.1, 52.6, 53.8, 51.3, 40.8, 37.3)), class = "data.frame", row.names = c(NA, 
-11L))

需求说明

我们需要实现自适应双Y轴时间序列图,满足以下要求:

  • 左Y轴绑定Exchange_rate列,右Y轴绑定PMI和BCI列
  • 根据数据极值自动计算轴限值,给数据留出合适的上下边距,适配任意数值范围的数据集

实现方案

1. 核心思路

  • 先将日期列转换为标准日期格式,便于时间序列绘图
  • 编写通用函数,根据数据极值和指定比例计算轴限值,自动处理特殊情况(如所有数据值相同)
  • 通过刻度比例转换实现双Y轴对齐,确保两条轴的刻度逻辑一致

2. 完整代码

library(ggplot2)
library(dplyr)

# 自适应计算轴限值的函数
calc_axis_limits <- function(data_cols, margin_ratio = 0.1) {
  data_min <- min(data_cols, na.rm = TRUE)
  data_max <- max(data_cols, na.rm = TRUE)
  data_range <- data_max - data_min
  
  # 处理所有值相同的情况
  if (data_range == 0) {
    margin <- 0.1 * data_min
    return(c(data_min - margin, data_max + margin))
  }
  
  # 按比例计算边距
  margin <- data_range * margin_ratio
  return(c(data_min - margin, data_max + margin))
}

# 数据预处理:转换日期格式
df <- df %>%
  mutate(date = as.Date(date))

# 指定左右轴对应的列名(可根据不同数据集修改)
left_col <- "Exchange_rate"
right_cols <- c("PMI", "BCI")

# 计算左右轴的限值
left_limits <- calc_axis_limits(df[[left_col]])
right_limits <- calc_axis_limits(unlist(df[right_cols]))

# 计算双Y轴的刻度转换比例
left_range <- left_limits[2] - left_limits[1]
right_range <- right_limits[2] - right_limits[1]
scale_ratio <- left_range / right_range

# 绘制双Y轴图
ggplot(df, aes(x = date)) +
  # 左轴曲线:Exchange_rate
  geom_line(aes(y = .data[[left_col]], color = left_col), size = 1) +
  # 右轴曲线:先转换到左轴刻度范围
  geom_line(aes(y = (PMI - right_limits[1]) * scale_ratio + left_limits[1], color = "PMI"), size = 1) +
  geom_line(aes(y = (BCI - right_limits[1]) * scale_ratio + left_limits[1], color = "BCI"), size = 1) +
  # 配置左轴
  scale_y_continuous(
    name = left_col,
    limits = left_limits,
    expand = c(0, 0)
  ) +
  # 配置右轴,反向转换刻度
  scale_y_continuous(
    name = "PMI & BCI",
    limits = right_limits,
    expand = c(0, 0),
    sec.axis = sec_axis(
      trans = ~ (. - left_limits[1]) / scale_ratio + right_limits[1],
      name = "PMI & BCI"
    )
  ) +
  # 颜色配置
  scale_color_manual(values = c("Exchange_rate" = "blue", "PMI" = "red", "BCI" = "green")) +
  labs(x = "日期", color = "指标") +
  theme_minimal() +
  theme(
    axis.title.y.right = element_text(color = "darkred"),
    axis.text.y.right = element_text(color = "darkred"),
    axis.title.y.left = element_text(color = "darkblue"),
    axis.text.y.left = element_text(color = "darkblue")
  )

3. 关键说明

  • calc_axis_limits函数:默认按数据范围的10%添加上下边距,可通过修改margin_ratio参数调整边距大小(比如设为0.05就是5%边距)
  • 双Y轴的核心是刻度转换:通过左右轴的范围比例,将右轴数据映射到左轴的刻度空间绘制,再通过sec_axis反向转换显示右轴原始刻度
  • 只需修改left_col和right_cols的列名,即可适配不同结构的数据集

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 07:18:27