ggplot2双Y轴geom_line绘图报错及分组年度总和值连线实现技术问询
问题1:解决数值运算错误
你遇到的第一个错误 "Error in p - lim2[1] : argument of binary operator is not numerical" 根源出在数据框的创建上——用cbind()组合列的时候,因为year是字符类型,所有列被统一转换成了字符型,导致value列不是数值,后续的缩放函数自然没法做减法运算。
修复这个问题很简单,不要用cbind()+as.data.frame(),直接用data.frame()创建数据框,它会自动识别各列的类型:
year <- c("2000", "2000", "2000", "2000", "2025", "2025", "2025", "2025", "2050", "2050", "2050", "2050") group <- c("a","a","b","b","a","a","b","b","a","a","b","b") value <- c(1,1.5,2,2.5,2,2.2,3,3.3,4,4.4,5,5.3) # 直接用data.frame创建,自动识别类型 d <- data.frame(year, group, value)
另外,你最初用stat_summary(fun = "sum")直接放到geom_line的y参数里的写法也不对,stat_summary需要配合stat参数或者单独使用,不过后来你改成新增sum列的方式是正确的方向。
问题2:绘制总和值的连线
你后来的代码里geom_line没生效,是因为把它放在了ggplot对象外面,没有和前面的pd对象用+拼接起来。另外,用mutate(sum = sum(value))会给每一行都添加组内总和,数据里每个(year, group)组合有重复的sum值,直接画连线会因为重复点导致线条异常,最好先单独汇总出每个(year, group)的总和,再用这个汇总数据来画点和线。
给你调整后的完整代码:
# 先定义缩放函数(和你原来的一致) y1.lim <- c(0, 15) y2.lim <- c(0, 25) variable_scaler <- function(p, lim1, lim2){ to_zero <- p-lim2[1] y1_range <- lim1[2]-lim1[1] y2_range <- lim2[2]-lim2[1] scaled <- to_zero*y1_range/y2_range from_zero <- scaled + lim1[1] return(from_zero) } axis_scaler <- function(p, lim1, lim2){ to_zero <- p-lim1[1] y1_range <- lim1[2]-lim1[1] y2_range <- lim2[2]-lim2[1] scaled <- to_zero*y2_range/y1_range from_zero <- scaled + lim2[1] return(from_zero) } # 1. 创建原始数据(修复类型问题) year <- c("2000", "2000", "2000", "2000", "2025", "2025", "2025", "2025", "2050", "2050", "2050", "2050") group <- c("a","a","b","b","a","a","b","b","a","a","b","b") value <- c(1,1.5,2,2.5,2,2.2,3,3.3,4,4.4,5,5.3) d <- data.frame(year, group, value) # 2. 单独汇总(year, group)的总和,避免重复值 sum_df <- d %>% group_by(year, group) %>% summarise(sum_val = sum(value), .groups = "drop") # .groups="drop"取消分组状态 # 3. 绘图:原始数据画箱线图,汇总数据画点和线 pd <- d %>% ggplot() + # 原始数据的箱线图 geom_boxplot(aes(x = year, y = value, color = group, group = interaction(year, group)), alpha = 0.1, width = 0.75) + # 汇总数据的点(注意用sum_df的数据) geom_point(data = sum_df, aes(x = year, y = variable_scaler(sum_val, y1.lim, y2.lim), color = group), position = position_dodge(width = 0.75), size = 2) + # 汇总数据的线(同样用sum_df,group要设为group,保证同组跨年份连线) geom_line(data = sum_df, aes(x = year, y = variable_scaler(sum_val, y1.lim, y2.lim), color = group, group = group), position = position_dodge(width = 0.75)) + # 双Y轴设置 scale_y_continuous(limits = y1.lim, breaks = c(0, 5, 10, 15), sec.axis = sec_axis(~axis_scaler(., y1.lim, y2.lim), breaks = c(0,5, 10,15, 20, 25), name = 'sum')) + labs(color = "Group") # 可选:给图例改个名字 plot(pd)
关键调整点:
- 用
summarise替代mutate生成汇总数据,每个(year, group)只保留一行总和,避免重复值干扰连线。 geom_line的group参数设为group,这样ggplot会把同group的不同年份的点连起来,而不是按interaction(year, group)分组(那样每个年份的点都是单独组,没法连线)。- 把
geom_line正确加到ggplot对象里,用+拼接。 - 绘图时给
geom_point和geom_line指定data = sum_df,明确用汇总数据来画总和的点和线,和原始数据的箱线图区分开。
这样就能正常画出每个分组的年度总和点,以及跨年份的连线了。
内容的提问来源于stack exchange,提问作者marie
相关产品推荐
相关产品推荐

