如何在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
相关产品推荐
相关产品推荐

