如何在ggplot中为箱线图按数值阈值分区域填充两种颜色
问题描述
现有数据集SO1:
dput(SO1) structure(list(country_n = c("Austria", "Belgium", "Czechia", "Denmark", "Finland", "France", "Germany", "Hungary", "Ireland", "Italy", "Latvia", "Netherlands", "Romania", "Spain", "Sweden", "Austria", "Belgium", "Czechia", "Denmark", "Finland", "France", "Germany", "Hungary", "Ireland", "Italy", "Latvia", "Netherlands", "Romania", "Spain", "Sweden", "Austria", "Belgium", "Czechia", "Denmark", "Finland", "France", "Germany", "Hungary", "Ireland", "Italy", "Latvia", "Netherlands", "Romania", "Spain", "Sweden", "Austria", "Belgium", "Czechia", "Denmark", "Finland", "France", "Germany", "Hungary", "Ireland", "Italy", "Latvia", "Netherlands", "Romania", "Spain", "Sweden"), org = c("MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG", "MAG"), P99_9s = c(1.24, 1.13, 0.887, 1.36, 2.07, 1.46, 1.24, 1.1, 0.832, 1.85, 1.07, 1.33, 1.91, 2.33, 1.29, 0.407, 0.367, 0.268, 0.415, 0.491, 0.397, 0.432, 0.339, 0.364, 0.464, 0.46, 0.417, 0.602, 0.53, 0.443, 0.327, 0.311, 0.227, 0.284, 0.331, 0.348, 0.362, 0.306, 0.313, 0.397, 0.289, 0.337, 0.417, 0.433, 0.285, 0.323, 0.304, 0.223, 0.278, 0.322, 0.344, 0.363, 0.303, 0.303, 0.409, 0.281, 0.329, 0.413, 0.439, 0.285)), class = "data.frame", row.names = c(NA, -60L))
想要制作箱线图,将P99_9s值大于1和小于等于1的部分在同一个箱线内填充两种不同颜色。当前使用代码:
colors = c( "lightgreen","#1565c0") mcolors <- ifelse(SO1$P99_9s>1,colors[1],colors[2]) pSO <- ggplot(SO1 , aes(x=reorder(org,P99_9s), y=P99_9s , fill=mcolors, colour=mcolors) ) + geom_boxplot(stat='boxplot', alpha=0.8) + coord_flip()+ geom_hline(yintercept =1, col='red')+ scale_fill_identity()+scale_colour_identity()
生成的箱线图被拆分为两部分,而预期是同一个箱线图内分色,请问如何实现?
解决方案
问题出在直接用原始数据的颜色映射,ggplot会把不同颜色的子集识别为不同分组,导致箱线被拆分。要实现单箱线内分色,需要先计算箱线的统计数据,再手动绘制分色箱体:
步骤1:计算箱线关键统计量
先提取箱线图所需的最小值、四分位数、中位数、最大值等统计值:
library(ggplot2) library(dplyr) # 计算箱线统计量并保持原排序 box_stats <- SO1 %>% group_by(org) %>% summarize( ymin = min(P99_9s), lower = quantile(P99_9s, 0.25), middle = median(P99_9s), upper = quantile(P99_9s, 0.75), ymax = max(P99_9s) ) %>% mutate(x = reorder(org, middle))
步骤2:手动绘制分色箱线图
通过geom_rect分别绘制y≤1和y>1的箱体部分,再补充须线、中位数线和极值点:
colors = c("lightgreen", "#1565c0") pSO <- ggplot() + # 绘制下半部分箱体(y ≤1) geom_rect(data = box_stats, aes(xmin = as.numeric(x) - 0.35, xmax = as.numeric(x) + 0.35, ymin = ymin, ymax = pmin(upper, 1)), fill = colors[2], color = "black") + # 绘制上半部分箱体(y >1) geom_rect(data = box_stats %>% filter(upper > 1), aes(xmin = as.numeric(x) - 0.35, xmax = as.numeric(x) + 0.35, ymin = 1, ymax = upper), fill = colors[1], color = "black") + # 绘制下须线和最小值点 geom_segment(data = box_stats, aes(x = as.numeric(x), xend = as.numeric(x), y = lower, yend = ymin), color = "black") + geom_point(data = box_stats, aes(x = as.numeric(x), y = ymin), shape = 95, size = 5) + # 绘制上须线和最大值点 geom_segment(data = box_stats, aes(x = as.numeric(x), xend = as.numeric(x), y = upper, yend = ymax), color = "black") + geom_point(data = box_stats, aes(x = as.numeric(x), y = ymax), shape = 95, size = 5) + # 绘制中位数线 geom_segment(data = box_stats, aes(x = as.numeric(x) - 0.35, xend = as.numeric(x) + 0.35, y = middle, yend = middle), color = "black", size = 1) + # 添加参考线 geom_hline(yintercept = 1, col = 'red') + # 调整坐标轴和主题 scale_x_continuous(breaks = 1:nrow(box_stats), labels = box_stats$x) + coord_flip() + labs(x = "org", y = "P99_9s") + theme_minimal() print(pSO)
可选补充
如果想展示原始数据分布,可以添加散点:
pSO + geom_jitter(data = SO1, aes(x = reorder(org, P99_9s), y = P99_9s), alpha = 0.5, width = 0.2)
内容的提问来源于stack exchange,提问作者efz
相关产品推荐
相关产品推荐

