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

如何在ggplot箱线图中按条件标注指定点及解决异常点匹配问题

问题描述

初始需求

用ggplot绘制箱线图时,图中存在须线外的点,希望仅标注数值≥50或≤5的点子集,且为这些点单独标注对应的数值(例如数值为70的点旁标注“70”)。

后续问题

按自定义逻辑筛选红点后,发现标记的红色点与stat_summary()识别的异常点不匹配,部分红点甚至触碰箱线图的须线,相关代码如下:

# 自定义箱线图分位数和异常点函数
{
  Individualized_Boxplot_Quantiles <- function(x) {
    r <- quantile(x, probs = c(0.01, 0.25, 0.5, 0.75, 0.99))
    names(r) <- c("ymin", "lower", "middle", "upper", "ymax")
    r
  }
  
  Definition_of_Outliers = function(x) 
  {
    subset(x, 
           quantile(x,0.99) < x | quantile(x,0.01) > x)
  }
}
# 全局筛选分位数外的点
Data_Above_99th_Percentile = filter(Data,variable_of_interest > quantile(Data$variable_of_interest, probs = 0.99))
Data_Below_1st_Percentile = filter(Data,variable_of_interest < quantile(Data$variable_of_interest,probs = 0.01))
# 绘制箱线图和默认异常点
stat_summary(fun.data = Individualized_Boxplot_Quantiles, 
             geom="boxplot",
             lwd = 0.1) +
  
  stat_summary(fun.y = Definition_of_Outliers, 
               geom="point",
               size = 0.5) + 
# 叠加自定义红色点
geom_point(data = Data_Above_99th_Percentile,
             colour = "red",
             size = 0.5) + 
  
  geom_point(data = Data_Below_1st_Percentile,
             colour = "red",
             size = 0.5)

问题原因

核心矛盾是分位数计算的维度不一致:

  • 自定义筛选红点时,用的是整个数据集的全局分位数(quantile(Data$variable_of_interest, 0.99))
  • 而stat_summary()中的函数是按箱线图的分组维度计算分位数(ggplot的stat层默认会自动按x轴分组处理数据)
    这就导致全局筛选的点和分组计算的须线范围不匹配,出现红点触碰须线的情况。

解决方案

1. 统一分位数计算逻辑(按分组计算)

先按箱线图的分组列计算每个组的分位数,再筛选每个组内符合要求的目标点(包括自定义的≥50/≤5,或分位数外的点)。

2. 整合绘图与标注逻辑

用预处理后的数据集直接绘制目标点并添加数值标注,确保和箱线图的分位数范围一致。

完整代码示例:

library(ggplot2)
library(dplyr)

# 替换为你的实际分组列,比如group = category
your_group_column <- "category"

# 预处理数据:按分组计算分位数,筛选目标点
processed_data <- Data %>%
  group_by(!!sym(your_group_column)) %>%
  mutate(
    q01 = quantile(variable_of_interest, 0.01, na.rm = TRUE),
    q99 = quantile(variable_of_interest, 0.99, na.rm = TRUE),
    # 自定义目标点条件:可根据需求调整
    is_target = (variable_of_interest >= 50 | variable_of_interest <= 5)
    # 如果需要同时保留分位数外的点,改用:
    # is_target = (variable_of_interest > q99 | variable_of_interest < q01) | (variable_of_interest >=50 | variable_of_interest <=5)
  ) %>%
  ungroup() %>%
  filter(is_target)

# 绘制箱线图
ggplot(Data, aes(x = !!sym(your_group_column), y = variable_of_interest)) +
  # 自定义分组箱线图
  stat_summary(
    fun.data = function(x) {
      r <- quantile(x, probs = c(0.01, 0.25, 0.5, 0.75, 0.99), na.rm = TRUE)
      names(r) <- c("ymin", "lower", "middle", "upper", "ymax")
      r
    },
    geom = "boxplot",
    lwd = 0.1
  ) +
  # 可选:绘制所有分组分位数外的异常点
  stat_summary(
    fun.y = function(x) {
      q01 <- quantile(x, 0.01, na.rm = TRUE)
      q99 <- quantile(x, 0.99, na.rm = TRUE)
      subset(x, x > q99 | x < q01)
    },
    geom = "point",
    size = 0.5,
    color = "gray"
  ) +
  # 绘制自定义红色目标点
  geom_point(
    data = processed_data,
    color = "red",
    size = 0.5
  ) +
  # 添加数值标注,调整位置避免遮挡
  geom_text(
    data = processed_data,
    aes(label = variable_of_interest),
    color = "red",
    size = 3,
    nudge_x = 0.15,
    na.rm = TRUE
  ) +
  theme_bw()

关键注意事项

  • 务必将your_group_column替换为你箱线图x轴对应的实际分组列名
  • na.rm = TRUE参数用于处理数据中的缺失值,避免计算报错
  • 可根据需求调整is_target的条件,比如只保留≥50或≤5的点,去掉分位数相关逻辑
  • geom_text()的nudge_x参数可调整标注的水平偏移量,避免点和标注重叠

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 23:10:27