如何在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
相关产品推荐
相关产品推荐

