如何用ggplot2合并带共同Y轴的两个箱线图并添加Tukey标记字母
问题需求
现有两个独立箱线图,分别展示直链淀粉、支链淀粉含量,二者共享图例与Y轴范围(0-100%干基)。此前用patchwork将其合并为并排子图,但希望将二者整合到同一幅图中(每个Location-Year分组下同时展示两种淀粉的箱线图),同时在图上方添加Tukey多重比较的显著性标记字母。
当前效果图:左右并排的两个子图,左侧为直链淀粉含量箱线图,右侧为支链淀粉含量箱线图,右侧子图已隐藏Y轴标签与刻度。
解决方案
核心思路是将宽格式数据转换为长格式,让ggplot可以通过分组变量区分两种淀粉类型,再结合position_dodge实现同一分组下的箱线图并排,最后分别添加两类淀粉的Tukey标记。
步骤1:数据格式转换
将原宽格式的汇总数据转换为长格式,提取淀粉类型、均值、标准差、最值等关键字段。
步骤2:封装Tukey标记计算函数
避免重复代码,封装函数批量计算两类淀粉的显著性字母及标记位置。
步骤3:绘制整合式箱线图
利用长格式数据,设置分组与位置偏移,添加双系列的Tukey标记,统一图例与Y轴。
完整代码
library(dplyr) library(multcompView) library(ggplot2) library(tidyr) # 加载原始数据并创建locyr列 totalam <- read.csv("barleyaap1.csv", header = TRUE, stringsAsFactors = TRUE) totalam$locyr <- as.factor(paste(totalam$location, totalam$year, sep = " ")) # 加载汇总数据并转换为长格式 totalam2 <- read.csv("barleyaap2.csv", header = TRUE, stringsAsFactors = TRUE) totalam_long <- totalam2 %>% pivot_longer( cols = -locyr, names_to = c(".value", "starch_type"), names_pattern = "(avg|sd|min|max)(am|ap)" ) %>% mutate(starch_type = recode(starch_type, am = "Amylose", ap = "Amylopectin")) # 封装Tukey字母计算函数 get_tukey_letters <- function(response_var, data, group_var = "locyr") { aov_model <- aov(reformulate(group_var, response_var), data = data) tukey_res <- TukeyHSD(aov_model)[[group_var]] letters_df <- data.frame(Letter = multcompView::multcompLetters(tukey_res[,4])$Letters) letters_df[[group_var]] <- rownames(letters_df) # 计算字母标记位置(取对应组的上四分位数+偏移) placement <- data %>% group_by(!!sym(group_var)) %>% summarise(Placement.Value = quantile(!!sym(response_var), 0.75) + 5) left_join(letters_df, placement, by = group_var) } # 分别计算两类淀粉的Tukey字母 am_letters <- get_tukey_letters("adry", totalam) %>% mutate(starch_type = "Amylose") ap_letters <- get_tukey_letters("apdry", totalam) %>% mutate(starch_type = "Amylopectin") all_letters <- bind_rows(am_letters, ap_letters) # 绘制整合后的箱线图 ggplot(totalam_long, aes(x = locyr, y = avg, fill = locyr)) + geom_boxplot( aes(lower = avg - sd, upper = avg + sd, middle = avg, ymin = min, ymax = max), stat = "identity", position = position_dodge(width = 0.8), width = 0.7 ) + # 添加Tukey标记字母 geom_text( data = all_letters, aes(x = locyr, y = Placement.Value, label = Letter), position = position_dodge(width = 0.8), size = 4, fontface = "bold", hjust = 0.5 ) + scale_y_continuous("% (dry basis)", limits = c(0, 100), breaks = seq(0, 100, 5)) + scale_x_discrete("Location-Year") + guides(fill = guide_legend(title = "Location-Year")) + theme_classic() + theme( axis.title = element_text(size = 14, color = "black"), axis.text = element_text(size = 10, color = "black"), legend.title = element_text(size = 14), legend.text = element_text(size = 10), plot.title = element_text(size = 16, hjust = 0.5) ) + ggtitle("Starch Content by Location-Year")
代码说明
- 长格式转换:用
pivot_longer将原数据中直链淀粉(am后缀)和支链淀粉(ap后缀)的列整合,生成starch_type分组变量,让ggplot能识别两类数据。 - Tukey函数封装:通过函数复用逻辑,分别计算两类淀粉的显著性字母,并将标记位置设置为组内上四分位数+固定偏移,确保字母显示在箱线图上方。
- 箱线图绘制:使用
position_dodge实现同一locyr下两个箱线图的并排展示,同时将两类淀粉的Tukey字母对应偏移,保证位置匹配。 - 若仍希望保留类似原patchwork的分面布局,可添加
facet_wrap(~starch_type, nrow = 1)实现左右分面且共享Y轴。
内容的提问来源于stack exchange,提问作者Maany Ramanan
相关产品推荐
相关产品推荐

