如何在R语言中创建带双Y轴的分组箱线图
问题描述
我需要在R语言中创建一个双Y轴分组箱线图:X轴是因子变量,每个因子水平对应两个连续变量的箱线图(左右并排),同时两个连续变量使用不同刻度的Y轴。现在我改编了基础绘图的示例代码,但第二个变量的箱线图和散点图总是出现在第一个的下方,而不是旁边。
我的代码如下:
data <- data.frame(Factor1=factor(c("A", "B","A", "B","A", "B","A", "B", "A", "B","A", "B","A", "B","A", "B", "A", "B","A", "B", "A", "B", "A", "B","A", "B", "A", "B","A", "B","A", "B","A", "B", "A", "B")), Cont1=rnorm(36, mean=100, sd= 15), Cont2=rnorm(36, mean=0.35, sd=0.05)) par(mar=c(5, 4, 4, 6) + 0.5) ## Plot first continuous variable # Boxplot boxplot(Cont1 ~ Factor1, data = data, col="white", boxcol=2, xlim=c(0.5, 3.5 + length(unique(data$Factor1))),axes=FALSE, xlab="", ylab="") # Points stripchart(Cont1 ~ Factor1, data = data,method = "jitter",pch = 19,col = 2,vertical = TRUE,add = TRUE) mtext("Cont1", side=2, line=2.5, col=2) box() axis(2, col=2, col.axis=2,las=1) ## las=1 makes horizontal labels ## Allow a second plot on the same graph par(new=TRUE) ## Plot the second continuous variable # Boxplot boxplot(Cont2 ~ Factor1, data = data, col="white", boxcol=3,xlim=c(0.5, 5.5), axes=FALSE, xlab="", ylab="") # with these limits the new boxplot and dotplot shows up below the previous ones, rather than next to them # Points stripchart(Cont2 ~ Factor1, data = data,method = "jitter", pch = 19, col = 3, vertical = TRUE, add = TRUE) ## a little farther out (line=4) to make room for labels mtext("Cont2", side=4,col=3,line=4) axis(4, col=3,col.axis=3,las=1) ## Draw the factor axis mtext("Factor1", side=1, col="black", line=2.5)
希望得到代码改进方案或新的实现思路。
解决方案
方案一:修复基础绘图系统代码
问题核心是第二个箱线图的X轴位置未偏移,导致和第一个图重叠错位。通过手动指定两个变量箱线图的X位置(给第二个变量的X坐标加偏移量),就能实现并排效果。
改进后的完整代码:
data <- data.frame(Factor1=factor(c("A", "B","A", "B","A", "B","A", "B", "A", "B","A", "B","A", "B","A", "B", "A", "B","A", "B", "A", "B", "A", "B","A", "B", "A", "B","A", "B","A", "B","A", "B", "A", "B")), Cont1=rnorm(36, mean=100, sd= 15), Cont2=rnorm(36, mean=0.35, sd=0.05)) # 调整边距,为右侧Y轴预留空间 par(mar=c(5, 4, 4, 6) + 0.5) # 定义偏移量,控制两个箱线图的间距 offset <- 0.3 factor_levels <- levels(data$Factor1) n_factors <- length(factor_levels) # --------------------- 绘制第一个变量(Cont1) --------------------- # 明确指定箱线图的X位置:1,2,...n_factors boxplot(Cont1 ~ Factor1, data = data, col="white", boxcol=2, at = 1:n_factors, xlim = c(0.5, n_factors + 1), # 扩展X轴范围,容纳偏移后的第二个箱线图 axes=FALSE, xlab="", ylab="") # 添加散点图,对应相同的X位置 stripchart(Cont1 ~ Factor1, data = data, method = "jitter", pch = 19, col = 2, vertical = TRUE, add = TRUE, at = 1:n_factors) # 左侧Y轴配置 mtext("Cont1", side=2, line=2.5, col=2) axis(2, col=2, col.axis=2, las=1) box() # --------------------- 绘制第二个变量(Cont2) --------------------- par(new=TRUE) # 第二个箱线图的X位置:1+offset, 2+offset,...n_factors+offset boxplot(Cont2 ~ Factor1, data = data, col="white", boxcol=3, at = 1:n_factors + offset, xlim = c(0.5, n_factors + 1), # 和第一个图保持一致的X范围 ylim = range(data$Cont2), # 锁定Y范围,避免自动缩放错位 axes=FALSE, xlab="", ylab="") # 添加散点图,对应偏移后的X位置 stripchart(Cont2 ~ Factor1, data = data, method = "jitter", pch = 19, col = 3, vertical = TRUE, add = TRUE, at = 1:n_factors + offset) # 右侧Y轴配置 mtext("Cont2", side=4, col=3, line=4) axis(4, col=3, col.axis=3, las=1) # --------------------- 绘制X轴标签 --------------------- # 将X轴刻度放在两个箱线图的中间位置 axis(1, at = 1:n_factors + offset/2, labels = factor_levels) mtext("Factor1", side=1, col="black", line=2.5)
方案二:使用ggplot2实现(更简洁易维护)
基础绘图需要手动调整坐标,ggplot2通过数据重塑和内置的分组、双Y轴功能,可以更高效实现需求,且代码可读性更强。
步骤说明:
- 将宽格式数据转为长格式,方便分组绘图
- 计算两个变量的刻度转换系数,实现双Y轴的映射
- 绘制分组箱线图和抖动散点图,配置双Y轴样式
完整代码:
library(ggplot2) library(tidyr) # 将数据从宽格式转为长格式 data_long <- pivot_longer(data, cols = c(Cont1, Cont2), names_to = "Variable", values_to = "Value") # 计算双Y轴的刻度转换系数(根据数据范围自动适配) scale_factor <- diff(range(data$Cont1)) / diff(range(data$Cont2)) shift_value <- min(data$Cont1) - min(data$Cont2) * scale_factor ggplot(data_long, aes(x = Factor1, y = Value, color = Variable)) + # 箱线图:设置分组偏移 geom_boxplot(position = position_dodge(width = 0.8), fill = "white") + # 抖动散点图:和箱线图对齐分组 geom_jitter(position = position_jitterdodge(jitter.width = 0.2, dodge.width = 0.8), size = 1) + # 配置双Y轴:左侧为Cont1刻度,右侧为转换后的Cont2原始刻度 scale_y_continuous( name = "Cont1", sec.axis = sec_axis(~ (. - shift_value)/scale_factor, name = "Cont2") ) + # 设置变量对应颜色 scale_color_manual(values = c("Cont1" = "red", "Cont2" = "green")) + # 调整主题样式 theme_classic() + theme( axis.title.y = element_text(color = "red"), axis.title.y.right = element_text(color = "green"), axis.text.y = element_text(color = "red"), axis.text.y.right = element_text(color = "green") )
内容的提问来源于stack exchange,提问作者Teresa
相关产品推荐
相关产品推荐

