You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 08:42:36