如何按分组变量抽取无重复item_type的数据框随机样本?
带约束条件的抽样问题
第一个示例数据与需求
我有如下结构的tibble数据框:
dat <- tibble( item_type = rep(1:36, each = 6), condition1 = rep(c("a", "b", "c"), times = 72), condition2 = rep(c("y", "z"), each = 3, times = 36), ) %>% unite(unique, item_type, condition1, condition2, sep = "-", remove = F)
数据框内容预览:
# A tibble: 216 × 4 unique item_type condition1 condition2 <chr> <int> <chr> <chr> 1 1-a-y 1 a y 2 1-b-y 1 b y 3 1-c-y 1 c y 4 1-a-z 1 a z 5 1-b-z 1 b z 6 1-c-z 1 c z 7 2-a-y 2 a y 8 2-b-y 2 b y 9 2-c-y 2 c y 10 2-a-z 2 a z
我需要抽取36行随机样本,要求满足两个条件:
- 每个
condition1与condition2的组合恰好出现6次 - 所有样本的
item_type完全不重复
一开始尝试用slice_sample()按分组抽样:
set.seed(1) dat %>% slice_sample(n = 6, by = c("condition1", "condition2")) %>% count(condition1, condition2)
统计结果显示各组合确实都出现了6次:
condition1 condition2 n <chr> <chr> <int> 1 a y 6 2 a z 6 3 b y 6 4 b z 6 5 c y 6 6 c z 6
但进一步检查发现,抽样结果里的item_type存在重复:
set.seed(1) dat %>% slice_sample(n = 6, by = c("condition1", "condition2")) %>% count(item_type) %>% arrange(desc(n))
统计结果如下:
# A tibble: 22 × 2 item_type n <int> <int> 1 10 3 2 34 3 3 1 2 4 6 2 5 7 2 6 15 2 7 20 2 8 21 2 9 23 2 10 25 2 # … with 12 more rows
请问能不能只用slice_sample()实现满足所有条件的抽样?
补充的第二个示例数据
还有另一个结构的数据框:
dat <- tibble( item_type = rep(1:36, each = 3), condition1 = rep(c("a", "b"), each = 54), condition2 = rep(c("x", "y", "z"), times = 36), ) %>% unite(unique, item_type, condition1, condition2, sep = "-", remove = F)
数据预览:
# A tibble: 108 × 4 unique item_type condition1 condition2 <chr> <int> <chr> <chr> 1 1-a-x 1 a x 2 1-a-y 1 a y 3 1-a-z 1 a z 4 2-a-x 2 a x 5 2-a-y 2 a y 6 2-a-z 2 a z 7 3-a-x 3 a x 8 3-a-y 3 a y 9 3-a-z 3 a z 10 4-a-x 4 a x
我试过用以下代码抽样,但结果不符合各组合的计数要求:
inner_join( dat, distinct(dat,condition1, condition2) %>% uncount(n()) %>% mutate(item_type = sample(n())) )
生成的数据框长度为20,各组合计数如下:
condition1 condition2 n <chr> <chr> <int> 1 a x 4 2 a y 4 3 a z 4 4 b x 3 5 b y 4 6 b z 5
解决方案
针对第一个示例的需求
单独用slice_sample()没法实现,因为它是按condition1+condition2分组独立抽样,不同组之间不会规避重复的item_type。要同时满足两个约束,得换思路:
我们可以先构建一个符合要求的item_type与条件组合的映射,再和原数据关联:
set.seed(1) # 先列出所有需要的条件组合(每个组合6次) required_pairs <- expand_grid( condition1 = c("a", "b", "c"), condition2 = c("y", "z") ) %>% uncount(6) # 随机分配不重复的item_type给这些组合 sampled_map <- required_pairs %>% mutate(item_type = sample(unique(dat$item_type), n())) # 关联原数据得到最终样本 final_sample <- inner_join(dat, sampled_map, by = c("item_type", "condition1", "condition2")) # 验证条件 final_sample %>% count(condition1, condition2) # 每个组合6次 final_sample %>% count(item_type) %>% filter(n>1) # 无重复item_type
针对第二个示例的调整
假设需求是每个condition1+condition2组合抽取相同次数(比如每个组合6次,共6*6=36行,且item_type不重复),可以用类似逻辑:
set.seed(1) # 定义每个组合需要的抽样数量 sample_per_pair <- 6 required_pairs <- expand_grid( condition1 = unique(dat$condition1), condition2 = unique(dat$condition2) ) %>% uncount(sample_per_pair) # 分配不重复的item_type sampled_map <- required_pairs %>% mutate(item_type = sample(unique(dat$item_type), n())) final_sample <- inner_join(dat, sampled_map, by = c("item_type", "condition1", "condition2"))
如果需求是其他计数比例,只需要调整uncount()里的数值即可,核心是先构建满足计数和唯一性的映射,再关联原数据。
内容的提问来源于stack exchange,提问作者babylinguist
相关产品推荐
相关产品推荐

