You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何实现带无重复ID约束的分层随机抽样?

解决按组分层抽样且每个ID仅出现一次的问题

我完全理解你的需求——你需要按group变量分层随机抽样,但每个ID只能被选中一次,绝对不能在多个组里重复出现。你之前尝试的replace=FALSE只控制了组内的抽样不重复,但完全没考虑跨组的ID冲突,这确实是这类抽样任务里的常见陷阱。

问题根源分析

你原来的代码是在每个组内独立抽样,完全没有同步其他组已选中的ID列表,所以同一个ID会在不同组的抽样池中被反复选中,最终出现在多个组的结果里。要解决这个问题,核心是先给每个ID“绑定”到一个随机的组(该ID实际存在的组),再基于这个绑定关系进行分层抽样。

解决方案一:简洁的Tidyverse实现

这个方法先给每个ID随机分配一个它所属的组,再按组抽样,从根源上避免跨组ID重复:

library(tidyverse)
set.seed(1)

# 你的原始数据
data <- data.frame(
 id = c("A", "C", "B", "D", "E", "F", "A", "A", "B", "B", "B", "D", "D", "E", "E", "F"),
 group = c("1", "1", "2", "2", "3", "3", "2", "1", "1", "2", "3", "2", "3", "2", "1", "3"),
 length = c("54", "52", "43", "42", "60", "46", "59", "60", "51", "45", "47", "58", "48", "46", "56", "57")
)

# 核心抽样逻辑
sampled_data <- data %>%
  # 1. 给每个ID随机选一个它所在的组作为专属组(确保ID只绑定一个组)
  group_by(id) %>%
  slice_sample(n = 1) %>%
  ungroup() %>%
  # 2. 按group分层,每个组抽取2个ID(和你的需求匹配)
  group_by(group) %>%
  slice_sample(n = 2) %>%
  ungroup() %>%
  # 3. 关联回原数据的对应行(若只需一行可跳过此步)
  left_join(data, by = c("id", "group")) %>%
  # 最终确保每个ID仅保留一行(保险措施)
  distinct(id, .keep_all = TRUE)

# 查看结果
sampled_data

代码逻辑说明:

  • group_by(id) %>% slice_sample(n=1):对每个ID随机挑选它存在的一个组,让ID和组形成唯一绑定,彻底避免跨组重复的可能。
  • 后续的分层抽样是基于这个绑定关系进行的,每个组只会从绑定到自身的ID中抽取样本。
  • 最后用distinct(id, .keep_all=TRUE)再次确保结果中没有重复ID,让输出更严谨。

解决方案二:分步抽样(更灵活可控)

如果需要更精细地控制抽样顺序(比如先抽某个组再抽另一个),可以用循环逐步抽样,每次抽完后移除已选中的ID:

set.seed(1)
# 定义每个组需要抽取的ID数量
sample_sizes <- c("1"=2, "2"=2, "3"=2)

# 初始化结果数据集和剩余待抽样数据
final_result <- tibble()
remaining_data <- data

# 按组依次抽样
for (current_group in names(sample_sizes)) {
  # 筛选当前组内未被选中的ID
  eligible_ids <- remaining_data %>%
    filter(group == current_group) %>%
    pull(id) %>%
    unique()
  
  # 随机抽取指定数量的ID
  selected_ids <- sample(eligible_ids, size = sample_sizes[current_group])
  
  # 从原数据中取出这些ID在当前组的行(随机选一行)
  new_samples <- data %>%
    filter(id %in% selected_ids, group == current_group) %>%
    group_by(id) %>%
    slice_sample(n=1) %>%
    ungroup()
  
  # 添加到结果中
  final_result <- bind_rows(final_result, new_samples)
  
  # 从剩余数据中移除已选中的ID,避免后续组重复抽取
  remaining_data <- remaining_data %>%
    filter(!id %in% selected_ids)
}

# 查看结果
final_result

这个方法适合处理更复杂的抽样规则,比如不同组的抽样优先级不同,或者需要动态调整抽样数量的场景。

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

火山引擎 最新活动