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

R中基于多指标的分层抽样:疫苗组与未疫苗组匹配抽样实现

疫苗接种组与未接种组匹配抽样方案

问题描述

现有数据集包含两组人群:

  • vaccinated(接种疫苗)组:每行对应唯一ID及唯一T0,无重复ID;
  • unvaccinated(未接种疫苗)组:长格式数据,同一ID对应多行不同T0;
  • 每行包含三个核心指标:PCP_visits(PCP就诊次数)、specialty_visits(专科就诊次数)、lab_visits(实验室就诊次数)。

需求:在R中完成抽样,最终数据需满足每行对应唯一ID,且两组上述三个指标的均值尽可能接近。

示例数据

以下是用于测试的模拟数据集:

set.seed(123)
data <- data.frame(
  ID = c(1:10, rep(11:20, 4)), # 接种组ID唯一,未接种组ID重复
  group = c(rep("vaccinated", 10), rep("unvaccinated", 40)),
  T0 = rep(1:10, 5),
  PCP_visits = c(sample(0:10, 10, replace = TRUE), 
                 sample(3:13, 40, replace = TRUE)),
  specialty_visits = c(sample(0:5, 10, replace = TRUE),
                       sample(2:7, 40, replace = TRUE)),
  lab_visits = c(sample(0:8, 10, replace = TRUE),
                 sample(2:10, 40, replace = TRUE))
)

数据预览:

ID        group T0 PCP_visits specialty_visits lab_visits
1   1   vaccinated  1          2                0          3
2   2   vaccinated  2          2                5          0
3   3   vaccinated  3          9                4          5
4   4   vaccinated  4          1                0          2
5   5   vaccinated  5          5                1          7
6   6   vaccinated  6         10                3          2
7   7   vaccinated  7          4                3          7
8   8   vaccinated  8          3                5          0
9   9   vaccinated  9          5                5          6
10 10   vaccinated 10          8                2          6
11 11 unvaccinated  1          9                5          6
12 12 unvaccinated  2         10                5          5
13 13 unvaccinated  3          4                0          6
14 14 unvaccinated  4          2                5          4
15 15 unvaccinated  5         10                1          5
16 16 unvaccinated  6          8                0          7
17 17 unvaccinated  7          8                1          4
18 18 unvaccinated  8          8                3          6
19 19 unvaccinated  9          2                4          3
20 20 unvaccinated 10          7                4          2
21 11 unvaccinated  1          9                5          8
22 12 unvaccinated  2          6                2          6
23 13 unvaccinated  3          9                0          5
24 14 unvaccinated  4          8                3          8
25 15 unvaccinated  5          2                5          6
26 16 unvaccinated  6          3                0          1
27 17 unvaccinated  7          0                5          2
28 18 unvaccinated  8         10                0          7
29 19 unvaccinated  9          6                2          3
30 20 unvaccinated 10          4                5          6
31 11 unvaccinated  1          9                3          3
32 12 unvaccinated  2          6                0          0
33 13 unvaccinated  3          8                5          7
34 14 unvaccinated  4          8                5          3
35 15 unvaccinated  5          9                2          8
36 16 unvaccinated  6          6                5          7
37 17 unvaccinated  7         10                4          5
38 18 unvaccinated  8          4                2          3
39 19 unvaccinated  9          6                5          7
40 20 unvaccinated 10          4                1          2
41 11 unvaccinated  1         10                4          3
42 12 unvaccinated  2          5                4          3
43 13 unvaccinated  3          8                2          5
44 14 unvaccinated  4          1                1          0
45 15 unvaccinated  5          4                1          3
46 16 unvaccinated  6          7                1          8
47 17 unvaccinated  7          1                3          6
48 18 unvaccinated  8          0                1          7
49 19 unvaccinated  9          8                1          4
50 20 unvaccinated 10         10                5          1

抽样前组间特征

抽样前两组指标均值存在明显差异:

# 计算抽样前组间特征
library(dplyr)
data %>% 
  group_by(group) %>% 
  summarise(n = n(),
            PCP_visits = mean(PCP_visits),
            specialty_visits = mean(specialty_visits),
            lab_visits = mean(lab_visits))

结果:

# A tibble: 2 × 5
  group            n PCP_visits specialty_visits lab_visits
  <chr>        <int>      <dbl>            <dbl>      <dbl>
1 unvaccinated    40        8.9             4.25       5.72
2 vaccinated      10        4.6             2.3        3.3 

解决方案

步骤1:预处理未接种组

未接种组每个ID对应多条记录,先聚合到ID层面,计算每个ID的三个指标均值:

# 聚合未接种组到ID级
unvaccinated_agg <- data %>%
  filter(group == "unvaccinated") %>%
  group_by(ID) %>%
  summarise(
    PCP_visits = mean(PCP_visits),
    specialty_visits = mean(specialty_visits),
    lab_visits = mean(lab_visits),
    .groups = "drop"
  )

# 提取接种组数据(已为ID唯一)
vaccinated_data <- data %>%
  filter(group == "vaccinated") %>%
  select(ID, group, PCP_visits, specialty_visits, lab_visits)

方法1:多次抽样筛选(无需额外包)

通过多次抽样未接种ID,找到与接种组均值差异最小的样本:

set.seed(123)
n_vaccinated <- nrow(vaccinated_data)
best_diff <- Inf
best_sample <- NULL

# 定义函数计算两组均值差异总和
calculate_diff <- function(sampled_ids, agg_data, target_data) {
  sampled <- agg_data %>% filter(ID %in% sampled_ids)
  target_means <- target_data %>% summarise(across(c(PCP_visits, specialty_visits, lab_visits), mean))
  sampled_means <- sampled %>% summarise(across(c(PCP_visits, specialty_visits, lab_visits), mean))
  sum(abs(as.numeric(sampled_means) - as.numeric(target_means)))
}

# 尝试1000次抽样,获取最优样本
for (i in 1:1000) {
  sampled_ids <- sample(unvaccinated_agg$ID, n_vaccinated)
  current_diff <- calculate_diff(sampled_ids, unvaccinated_agg, vaccinated_data)
  if (current_diff < best_diff) {
    best_diff <- current_diff
    best_sample <- sampled_ids
  }
}

# 从原始数据中提取最优样本(每个ID取第一行)
sampled_unvaccinated <- data %>%
  filter(group == "unvaccinated", ID %in% best_sample) %>%
  group_by(ID) %>%
  slice(1) %>%
  ungroup()

# 合并最终数据
final_data <- bind_rows(vaccinated_data, sampled_unvaccinated)

方法2:马氏距离匹配(精准匹配,需MatchIt包)

利用马氏距离为每个接种ID匹配最相似的未接种ID,确保组间均值高度接近:

# 安装并加载MatchIt包
# install.packages("MatchIt")
library(MatchIt)

# 准备匹配数据集
match_data <- bind_rows(
  vaccinated_data %>% mutate(treatment = 1),
  unvaccinated_agg %>% mutate(group = "unvaccinated", treatment = 0)
) %>%
  select(treatment, PCP_visits, specialty_visits, lab_visits)

# 1:1马氏距离匹配
m.out <- matchit(treatment ~ PCP_visits + specialty_visits + lab_visits,
                 data = match_data, method = "nearest", distance = "mahalanobis")

# 获取匹配后的未接种ID
matched_unvaccinated_ids <- unvaccinated_agg$ID[m.out$match.matrix[,1]]

# 提取匹配后的未接种组数据(每个ID取第一行)
matched_unvaccinated <- data %>%
  filter(group == "unvaccinated", ID %in% matched_unvaccinated_ids) %>%
  group_by(ID) %>%
  slice(1) %>%
  ungroup()

# 合并最终数据
final_data_matched <- bind_rows(vaccinated_data, matched_unvaccinated)

验证抽样结果

以方法2为例,验证抽样后组间特征:

final_data_matched %>%
  group_by(group) %>%
  summarise(
    n = n(),
    PCP_visits = mean(PCP_visits),
    specialty_visits = mean(specialty_visits),
    lab_visits = mean(lab_visits)
  )

结果示例(因随机种子可能略有差异):

# A tibble: 2 × 5
  group            n PCP_visits specialty_visits lab_visits
  <chr>        <int>      <dbl>            <dbl>      <dbl>
1 unvaccinated    10        4.7             2.4        3.5
2 vaccinated      10        4.6             2.3        3.3

两组均值已高度接近,满足需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 05:25:53