如何优化ggplot2绘制的CSO溢流量及时长柱状图?
优化后的解决方案
以下是针对你需求优化后的代码,解决了Y轴从0开始、次轴刻度匹配时长范围的问题:
library(ggplot2) library(reshape2) # 原始数据 data1 <- c(280, 577, 363, 307) # 溢流量 data2 <- c(584, 493, 1188, 1017) # 时长 category <- factor(c("2019", "2020", "2021", "2022"), levels = c("2019", "2020", "2021", "2022")) # 数据整理:将时长转换为主轴比例(系数=1200/600=2,匹配次轴0-1200范围) df1 <- data.frame( category, `Number of Spills` = data1, `Duration in Hours` = data2 / 2 # 转换为主轴刻度比例 ) df1_melted <- melt(df1, id.vars = "category") # 绘图 p <- ggplot(df1_melted, aes(x = category, y = value, fill = variable)) + geom_bar(stat = "identity", position = "dodge", width = 0.7) + # 设置标题和坐标轴标签 labs(title = "Number and Duration of CSO Spills into the Porter Brook", x = "Year", y = "Number of Spills") + # 主Y轴设置:从0开始,范围0-600,无扩展边距 scale_y_continuous( limits = c(0, 600), expand = c(0, 0), breaks = seq(0, 600, 100), # 次Y轴设置:反向转换回时长刻度,范围0-1200 sec.axis = sec_axis( ~ . * 2, name = "Duration (Hours)", breaks = seq(0, 1200, 200) ) ) + # 图例设置 scale_fill_discrete(name = "") + theme( plot.title = element_text(hjust = 0.5), legend.position = "bottom", # 可选:调整次轴标签样式,区分主次轴 axis.title.y.right = element_text(color = "#F8766D"), axis.text.y.right = element_text(color = "#F8766D"), legend.text = element_text(size = 10) ) p
关键优化点说明
主Y轴从0开始:通过
scale_y_continuous的limits = c(0, 600)直接锁定Y轴范围,expand = c(0,0)去掉轴两侧的空白边距,替代原代码中无效的expand_limits和coord_cartesian组合(原代码无效是因为次轴的设置干扰了轴范围)。次轴匹配时长范围:
- 先将时长数据
data2除以转换系数(这里用2,因为主轴最大值600对应次轴最大值1200),让其高度在主轴刻度内合理显示; - 在
sec_axis中用~ . * 2反向转换,还原时长的真实数值,并通过breaks = seq(0, 1200, 200)设置次轴刻度为0-1200的间隔值。
- 先将时长数据
细节修正:
- 移除了原代码中冲突的
labs(fill = "Year"),避免和scale_fill_discrete的设置重复; - 调整了柱子宽度
width = 0.7让图表更美观; - 给次轴标签和刻度设置了不同颜色,增强可读性。
- 移除了原代码中冲突的
如果你需要更精准的转换系数,可以根据数据的最大值计算:比如主轴最大值取max(data1)=577,次轴最大值取max(data2)=1188,系数就是1188/577≈2.06,替换代码中的2即可,这样次轴刻度会完全匹配时长的实际最大值。
内容的提问来源于stack exchange,提问作者Veg
相关产品推荐
相关产品推荐

