如何在ggplot分面箱线图中为指定箱线图添加双Y轴
分面内实现独立双Y轴的箱线图解决方案
针对你需要在每个分面(type 1/2/3)中将c类箱线图放在右侧独立Y轴、用颜色区分的需求,这里提供基于ggplot2的实现方案,保留你自定义的箱线图计算规则,同时让双轴范围适配各自数据:
核心思路
ggplot2本身不支持分面内的独立双轴,所以我们通过数据缩放的方式实现:
- 对每个分面内的c类数据单独计算缩放系数,将其数值缩放到a/b类的范围内
- 绘制a/b和c两类箱线图并设置不同颜色
- 添加右侧Y轴,通过反转缩放系数将轴刻度转换回c类原始数值
完整代码实现
library(ggplot2) library(dplyr) # 生成示例数据 set.seed(123) # 设置随机种子保证结果可复现 a1 <- rnorm(n=100, mean=5, sd=20) a2 <- rnorm(n=100, mean=6, sd=20) a3 <- rnorm(n=100, mean=8, sd=20) b <- rnorm(n=100, mean=11, sd=10) c1 <- rnorm(n=100, mean=500, sd=80) c2 <- rnorm(n=100, mean=600, sd=80) c3 <- rnorm(n=100, mean=800, sd=80) letters <- c(rep("a", 300), rep("b", 300), rep("c", 300)) type <- rep(c(rep(1,100), rep(2,100), rep(3,100)),3) dat <- data.frame(y=c(a1,a2,a3,b,b,b,c1,c2,c3), letters, type) # 自定义箱线图统计函数(保留你的逻辑) f <- function(x) { r <- quantile(x, probs = c(0.05, 0.25, 0.5, 0.75, 0.95)) names(r) <- c("ymin", "lower", "middle", "upper", "ymax") r } o <- function(x) { subset(x, x < quantile(x, 0.05) | quantile(x, 0.95) < x) } # 计算每个type下的缩放系数:将c类数据缩放到a/b类的数值范围 scale_factors <- dat %>% group_by(type) %>% summarise( a_b_max = max(y[letters %in% c("a", "b")]), c_max = max(y[letters == "c"]), scale = a_b_max / c_max # 缩放系数:c类原始值 * scale = 缩放到a/b范围的值 ) # 将缩放系数合并到原始数据 dat_scaled <- dat %>% left_join(scale_factors, by = "type") %>% mutate( y_scaled = ifelse(letters == "c", y * scale, y) # 仅对c类数据做缩放 ) # 绘制分面双轴箱线图 ggplot() + # 绘制a/b类箱线图和异常值点(左侧Y轴) stat_summary( data = filter(dat_scaled, letters %in% c("a", "b")), aes(x = letters, y = y_scaled, group = letters), fun.data = f, geom = "boxplot", fill = "#619CFF", color = "black" ) + stat_summary( data = filter(dat_scaled, letters %in% c("a", "b")), aes(x = letters, y = y_scaled, group = letters), fun = o, geom = "point", color = "#619CFF" ) + # 绘制c类箱线图和异常值点(右侧Y轴,已缩放) stat_summary( data = filter(dat_scaled, letters == "c"), aes(x = letters, y = y_scaled, group = letters), fun.data = f, geom = "boxplot", fill = "#F8766D", color = "black" ) + stat_summary( data = filter(dat_scaled, letters == "c"), aes(x = letters, y = y_scaled, group = letters), fun = o, geom = "point", color = "#F8766D" ) + # 设置分面,每个分面独立缩放轴范围 facet_wrap(~type, nrow = 1, scales = "free_y") + # 设置Y轴:左侧为a/b原始值,右侧通过缩放系数转换回c原始值 scale_y_continuous( name = "a/b类指标值", sec.axis = sec_axis( trans = ~./dat_scaled$scale[dat_scaled$type == unique(type)][1], # 每个分面用自己的缩放系数 name = "c类指标值" ) ) + # 调整主题和标签 labs(x = "指标类型") + theme_bw() + theme( axis.title.y.right = element_text(color = "#F8766D"), axis.text.y.right = element_text(color = "#F8766D"), axis.title.y = element_text(color = "#619CFF"), axis.text.y = element_text(color = "#619CFF") )
关键细节说明
- 分面独立缩放:通过
facet_wrap(..., scales = "free_y")让每个分面的Y轴范围适配自身数据 - 颜色区分:a/b类用蓝色,c类用红色,同时右侧轴文字也对应颜色,提升可读性
- 缩放逻辑:每个type分组计算缩放系数,确保c类数据缩放后和同组a/b类在相近范围,右侧轴通过
~./scale反转缩放,还原原始数值 - 保留自定义统计:完全复用你定义的
f和o函数,保证箱线图的计算规则不变
内容的提问来源于stack exchange,提问作者Katy
相关产品推荐
相关产品推荐

