如何在ggplot分组散点图中添加忽略异常值的趋势线?
分组散点图添加忽略异常值的趋势线方案
核心思路
要保留所有散点但让趋势线排除异常值,关键是为趋势线单独准备过滤掉异常值的数据集:原数据集用于绘制散点,过滤后的数据集专门用于拟合趋势线,两者互不干扰。
可行方案
1. 基于四分位距(IQR)过滤异常值(最通用)
这是统计学识别异常值的经典方法,对每个Name分组计算PG_Nr的四分位距,排除超出Q1 - 1.5*IQR到Q3 + 1.5*IQR范围的数据,适合大多数分布类型的数据。
示例代码:
library(dplyr) library(ggplot2) # 按Name分组过滤异常值 data_filtered <- data %>% group_by(Name) %>% mutate( Q1 = quantile(PG_Nr, 0.25, na.rm = TRUE), Q3 = quantile(PG_Nr, 0.75, na.rm = TRUE), IQR = Q3 - Q1, is_outlier = PG_Nr < (Q1 - 1.5*IQR) | PG_Nr > (Q3 + 1.5*IQR) ) %>% filter(!is_outlier) %>% ungroup() # 绘图:原数据画散点,过滤后数据拟合趋势线 ggplot(data = data, aes(x = Days, y = PG_Nr, colour = Name)) + geom_point() + # 保留所有原始散点 geom_smooth(data = data_filtered, method = "lm", se = FALSE) # 用过滤后的数据生成线性趋势线
2. 基于相邻Days的百分比范围过滤(对应你提到的思路)
如果数据是随Days连续变化的时序数据,可以计算每个数据点相对于同组前一个数据点的变化百分比,超出设定阈值(比如±50%)的标记为异常值,适合过滤突变型异常。
示例代码:
data_filtered <- data %>% group_by(Name) %>% arrange(Days) %>% # 先按Days排序保证时序正确 mutate( pct_change = (PG_Nr - lag(PG_Nr))/lag(PG_Nr)*100, # 第一个数据点无前置值默认保留,后续点超出±50%标记为异常 is_outlier = ifelse(row_number() == 1, FALSE, abs(pct_change) > 50) ) %>% filter(!is_outlier) %>% ungroup() # 绘图代码同上 ggplot(data = data, aes(x = Days, y = PG_Nr, colour = Name)) + geom_point() + geom_smooth(data = data_filtered, method = "lm", se = FALSE)
3. 自定义业务阈值过滤
如果你对PG_Nr的合理范围有明确业务判断(比如不可能为负或超过某个固定值),直接按规则过滤:
data_filtered <- data %>% filter(PG_Nr >= 0 & PG_Nr <= 100) # 示例:保留0-100范围内的数据 # 绘图代码同上 ggplot(data = data, aes(x = Days, y = PG_Nr, colour = Name)) + geom_point() + geom_smooth(data = data_filtered, method = "lm", se = FALSE)
补充说明
geom_smooth的method参数可按需调整:"lm"对应线性趋势,"loess"对应局部平滑趋势;若要保留趋势线的置信区间,去掉se = FALSE即可。- 两种过滤方法可结合使用,比如先用IQR过滤极端值,再用相邻百分比过滤突变值,进一步优化趋势线拟合效果。
内容的提问来源于stack exchange,提问作者Shaxi Liver
相关产品推荐
相关产品推荐

