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

如何用facet_wrap为Obs数据集添加两条含/不含异常值的回归线?

问题描述

我有一组按处理组拆分的图表(每个处理对应一个facet),每个图表包含观测数据(Obs)和模型输出数据(Model)。观测数据中存在疑似无效的异常值,因此需要为每个facet中的Obs数据集添加两条回归线:一条包含所有数据点,一条剔除异常值;Model数据集无需添加回归线。由于不同站点的处理组数量不固定,希望找到通用的适配方案。

示例代码与当前效果

df <- data.frame(year=factor(c(2000,2001,2002,2003,2004,2005,
                               2000,2001,2002,2003,2004,2005,
                               2000,2001,2002,2003,2004,2005,
                               2000,2001,2002,2003,2004,2005)),
                 treatment_code=c("T1","T1","T1","T1","T1","T1",
                                  "T1","T1","T1","T1","T1","T1",
                                  "T2","T2","T2","T2","T2","T2",
                                  "T2","T2","T2","T2","T2","T2"),
                 value=c(9,10,8.5,7.5,22,10.5,
                         11,9,12,9,10,11.5,
                         8,11,9.5,12,10,10.5,
                         7,9,11,10,12,11.5),
                 model=c("Obs","Obs","Obs","Obs","Obs","Obs",
                         "A","A","A","A","A","A",
                         "Obs","Obs","Obs","Obs","Obs","Obs",
                         "A","A","A","A","A","A"))

gS_calib <- df %>%
  ggplot(aes(x=year, y=value, color=model, show.legend=TRUE)) +
  geom_point(show.legend=TRUE) +
  xlab("Year") +
  facet_wrap(~treatment_code, ncol=length(unique(df$treatment_code))) 

gS_calib

当前图表效果:
当前图表效果

通用解决方案

完全可以实现,核心是先预处理生成Obs数据的两种回归拟合结果,再结合facet_wrap自动适配不同处理组数量,具体步骤如下:

1. 编写通用异常值剔除函数

这里用常用的IQR法(四分位距)识别并剔除异常值,你也可以根据需求换成标准差法或自定义阈值:

remove_outliers <- function(data, group_col, value_col) {
  data %>%
    group_by({{group_col}}) %>%
    # 标记异常值
    mutate(is_outlier = ifelse({{value_col}} < quantile({{value_col}}, 0.25) - 1.5*IQR({{value_col}}) |
                                {{value_col}} > quantile({{value_col}}, 0.75) + 1.5*IQR({{value_col}}),
                              TRUE, FALSE)) %>%
    # 剔除异常值
    filter(!is_outlier) %>%
    ungroup()
}

2. 生成两种回归拟合数据

分别对所有Obs数据和剔除异常值后的Obs数据做分组回归,用broom包生成拟合值:

library(broom)
library(dplyr)
library(ggplot2)

# 提取仅Obs的数据
obs_data <- df %>% filter(model == "Obs")

# 全数据的回归拟合(包含异常值)
fit_full <- obs_data %>%
  group_by(treatment_code) %>%
  do(augment(lm(value ~ as.numeric(year), data = .))) %>%
  mutate(fit_type = "全数据回归线")

# 剔除异常值后的回归拟合
obs_no_outliers <- remove_outliers(obs_data, treatment_code, value)
fit_clean <- obs_no_outliers %>%
  group_by(treatment_code) %>%
  do(augment(lm(value ~ as.numeric(year), data = .))) %>%
  mutate(fit_type = "剔除异常值回归线")

# 合并两种拟合结果
fit_data <- bind_rows(fit_full, fit_clean)

3. 绘制最终图表

把原始点和两条回归线整合到分面中,facet_wrap会自动根据处理组数量调整布局:

gS_calib <- df %>%
  ggplot(aes(x = year, y = value, color = model)) +
  # 绘制所有原始数据点
  geom_point(show.legend = TRUE) +
  # 添加全数据回归线(仅针对Obs)
  geom_line(data = fit_data %>% filter(fit_type == "全数据回归线"),
            aes(y = .fitted, color = fit_type), linetype = "solid") +
  # 添加剔除异常值后的回归线(仅针对Obs)
  geom_line(data = fit_data %>% filter(fit_type == "剔除异常值回归线"),
            aes(y = .fitted, color = fit_type), linetype = "dashed") +
  xlab("年份") +
  # 自动适配处理组数量的分面
  facet_wrap(~treatment_code) +
  # 自定义颜色区分不同数据类型
  scale_color_manual(values = c("Obs" = "black", "A" = "blue", 
                                "全数据回归线" = "red", "剔除异常值回归线" = "darkred")) +
  labs(color = "数据/拟合类型")

gS_calib

关键优势

  • 完全通用:不管处理组数量多少,facet_wrap都会自动调整分面布局,无需手动指定列数;
  • 灵活可改:异常值剔除逻辑、回归线样式(颜色、线型)都可以根据需求自定义;
  • 分组独立拟合:每个分面的回归线都是基于该处理组内的Obs数据生成,结果准确。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 11:14:57