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

如何在tidyverse的ggplot2中修改箱线图分位数为20%和80%

问题

使用tidyverse的ggplot2绘制箱线图时,如何将默认的25%/75%分位数(Q1/Q3)替换为20%和80%分位数?目前已能通过qboxplot包的probs参数实现,但ggplot2中尝试多种方法未成功,需要绘制7个分组的箱线图。

样本数据

dataset <- structure(
  list(
    PM1 = c(0.4, 6.2, 5.1, 7.8, 8, NA, NA, 5.2), 
    PM2 = c(2, 8, 5.6, 8, NA, 6.4, 10.3, 7), 
    PM3 = c(NA, 7.2, 4.8, 4.4, NA, NA, 10.3, 5.9), 
    PM4 = c(1.2, 8.7, 5.4, NA, NA, NA, NA, NA), 
    PM5 = c(3.5, NA, 1.9, 2.2, NA, 3.5, 9.4, 0.3), 
    PM6 = c(1.3, NA, 1.1, NA, NA, 2.8, NA, NA), 
    PM7 = c(NA, NA, NA, 0.4, NA, NA, 8.8, 0.6)), 
  row.names = c(NA, -8L), 
  class = c("tbl_df", "tbl", "data.frame")
)

已实现的qboxplot代码

library(qboxplot)
dataset %>% 
  qboxplot(
    main = "Dissolved Oxygen",
    probs = c(0.20, 0.50, 0.80),
    ylim = c(0, 12),
    ylab = "mg/L",
    xlab = "Monitoring Points"
  )

现有ggplot2代码(未实现自定义分位数)

library(tidyverse)
dataset %>% 
  pivot_longer(
    cols = everything(),
    names_to = "monitoring_point",
    values_to = "oxigenio_dissolvido"
  ) %>% 
  ggplot(
    aes(x = monitoring_point,
        y = oxigenio_dissolvido)
  )+
  stat_boxplot(
      geom = "errorbar",
      width = 0.3,
      position = position_dodge(width = 0.65)
    )+
  geom_boxplot()+
  labs(title = "Dissolved Oxygen",
       y = "oxigenio_dissolvido (mg/L)")+
  scale_y_continuous(
      expand = expansion(mult = c(0,0)),
      limits = c(0, 12)
    )+
  theme_bw()+
  theme(
    plot.title = element_text(hjust = 0.5)
  )

解决方案

ggplot2的geom_boxplot()和stat_boxplot()默认使用boxplot.stats()计算分位数,要替换为20%/80%分位数,需自定义统计函数,并在两个图层中指定该函数来统一计算逻辑。

修改后的完整代码

library(tidyverse)

# 自定义箱线图统计量计算函数,返回20%/50%/80%分位数及须的范围
custom_boxplot_stats <- function(x) {
  x <- na.omit(x)
  # 计算指定分位数
  qs <- quantile(x, probs = c(0.2, 0.5, 0.8), na.rm = TRUE)
  # 基于自定义IQR计算须的上下限(沿用1.5*IQR逻辑)
  iqr <- qs[3] - qs[1]
  lower_whisker <- max(min(x), qs[1] - 1.5*iqr)
  upper_whisker <- min(max(x), qs[3] + 1.5*iqr)
  
  # 返回ggplot2要求的结构
  tibble(
    ymin = lower_whisker,
    lower = qs[1],
    middle = qs[2],
    upper = qs[3],
    ymax = upper_whisker
  )
}

dataset %>% 
  pivot_longer(
    cols = everything(),
    names_to = "monitoring_point",
    values_to = "oxigenio_dissolvido"
  ) %>% 
  ggplot(
    aes(x = monitoring_point,
        y = oxigenio_dissolvido)
  )+
  # 给误差线指定自定义统计函数
  stat_boxplot(
      geom = "errorbar",
      width = 0.3,
      position = position_dodge(width = 0.65),
      fun.data = custom_boxplot_stats
    )+
  # 给箱线图指定自定义统计函数,保持分组对齐
  geom_boxplot(
      position = position_dodge(width = 0.65),
      fun.data = custom_boxplot_stats
    )+
  labs(title = "Dissolved Oxygen",
       y = "oxigenio_dissolvido (mg/L)",
       x = "Monitoring Points")+
  scale_y_continuous(
      expand = expansion(mult = c(0,0)),
      limits = c(0, 12)
    )+
  theme_bw()+
  theme(
    plot.title = element_text(hjust = 0.5)
  )

关键说明

  • 必须同时修改stat_boxplot()和geom_boxplot()的fun.data参数:两者会独立计算统计量,只有同时指定才能让误差线和箱线图的分位数保持一致。
  • 自定义函数中保留了默认的须部计算逻辑(1.5*IQR),但IQR替换为80%分位数与20%分位数的差值,若需调整须部规则可修改这部分代码。
  • 两个图层的position_dodge(width)参数需保持一致,避免箱线与误差线错位。

内容的提问来源于stack exchange,提问作者leonardofwink

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 13:01:16