在R语言中绘制带主次坐标轴的图表:实现方法与轴范围调整
实现双轴复合图表(柱状+折线)的R方案
一、ggplot2 实现方法(支持灵活调整轴范围)
ggplot2从2.2.0版本开始就支持次轴功能,只是需要通过数据线性转换来实现(这是其设计逻辑,避免随意使用次轴造成数据误解)。针对你的需求,我们可以通过自定义转换规则,让柱状图(人口数据)始终位于折线图(变量数据)下方,同时支持手动或自动调整轴范围。
代码示例
library(ggplot2) library(tidyr) # 将多列变量转成长格式,方便绘制多条折线 df_long <- df %>% pivot_longer(cols = starts_with("var_"), names_to = "variable", values_to = "value") # 自定义轴范围:可根据需求修改 main_y_min <- -500 # 主轴(变量)最小值 main_y_max <- 1500 # 主轴(变量)最大值 sec_y_min <- 0 # 次轴(人口)最小值 sec_y_max <- 30000000# 次轴(人口)最大值 # 定义人口数据到主轴刻度的转换函数 pop_to_main <- function(pop) { ((pop - sec_y_min) / (sec_y_max - sec_y_min)) * (main_y_max - main_y_min) + main_y_min } # 定义主轴刻度到人口数据的反向转换函数(用于次轴标签) main_to_pop <- function(y) { ((y - main_y_min) / (main_y_max - main_y_min)) * (sec_y_max - sec_y_min) + sec_y_min } # 绘制图表 ggplot() + # 绘制柱状图:使用转换后的人口值映射到主轴 geom_col(data = df, aes(x = city, y = pop_to_main(pop)), fill = "#4a86e8", width = 0.6) + # 绘制折线和数据点:使用原始变量值 geom_line(data = df_long, aes(x = city, y = value, color = variable, group = variable), linewidth = 1) + geom_point(data = df_long, aes(x = city, y = value, color = variable), size = 3) + # 设置主/次轴参数 scale_y_continuous( name = "Variable Values", limits = c(main_y_min, main_y_max), sec.axis = sec_axis(~ main_to_pop(.), name = "Population") ) + # 自定义折线颜色 scale_color_manual(values = c("var_1" = "#ff0000", "var_2" = "#009933", "var_3" = "#ff9900")) + labs(x = "City", color = "Variables") + theme_minimal() + # 区分主次轴颜色,提升可读性 theme( axis.title.y.right = element_text(color = "#4a86e8"), axis.text.y.right = element_text(color = "#4a86e8") )
核心说明
- 通过
pop_to_main函数将人口数据缩放到主轴的刻度区间内,确保柱状图视觉上位于折线下方; - 修改
main_y_min/main_y_max/sec_y_min/sec_y_max即可灵活调整轴范围; - 次轴标签通过反向转换函数还原真实人口数值,避免数据失真。
二、其他替代方案
1. Plotly 交互式图表
Plotly支持更直观的双轴设置,无需手动转换数据,适合需要交互操作的场景:
library(plotly) library(tidyr) df_long <- df %>% pivot_longer(cols = starts_with("var_"), names_to = "variable", values_to = "value") plot_ly() %>% # 添加柱状图,绑定到次轴 add_bars(data = df, x = ~city, y = ~pop, name = "Population", yaxis = "y2", marker = list(color = "#4a86e8")) %>% # 添加折线和数据点,绑定到主轴 add_lines(data = df_long, x = ~city, y = ~value, color = ~variable, name = ~variable, line = list(width = 2)) %>% add_markers(data = df_long, x = ~city, y = ~value, color = ~variable, name = ~variable, marker = list(size = 6)) %>% # 设置轴范围和布局 layout( xaxis = list(title = "City"), yaxis = list(title = "Variable Values", range = c(-500, 1500)), yaxis2 = list(title = "Population", range = c(0, 30000000), overlaying = "y", side = "right"), legend = list(x = 1.1, y = 1) )
2. Base R 原生绘图
如果不需要ggplot2的语法糖,Base R也可以直接实现双轴图表:
# 调整图形边距,预留次轴空间 par(mar = c(5, 4, 4, 4) + 0.1) # 先绘制折线图(主轴) plot(df$city, df$var_1, type = "b", col = "#ff0000", lwd = 2, pch = 16, ylim = c(-500, 1500), xlab = "City", ylab = "Variable Values") lines(df$city, df$var_2, type = "b", col = "#009933", lwd = 2, pch = 16) lines(df$city, df$var_3, type = "b", col = "#ff9900", lwd = 2, pch = 16) # 开启新图层,绘制柱状图(次轴) par(new = TRUE) barplot(df$pop, names.arg = df$city, col = "#4a86e8", width = 0.6, ylim = c(0, 30000000), axes = FALSE, xlab = "", ylab = "") axis(side = 4) mtext("Population", side = 4, line = 3) # 添加图例 legend("topright", legend = c("var_1", "var_2", "var_3", "Population"), col = c("#ff0000", "#009933", "#ff9900", "#4a86e8"), lty = c(1,1,1,NA), pch = c(16,16,16,15), bty = "n")
三、关于ggplot2次轴的支持情况
目前ggplot2已经完全支持次轴功能(通过sec_axis()函数实现),只是官方文档不鼓励滥用双轴——因为双轴容易造成数据解读偏差,但如果是像你这种需要对比量级差异极大的两类数据,合理使用是完全可行的,只要保证转换逻辑清晰、轴标签明确即可。
内容的提问来源于stack exchange,提问作者Alan
相关产品推荐
相关产品推荐

