如何用dplyr实现分位数内等大小随机子组分配?
解决方案:分位数组内的均匀随机子组分配
这个问题的核心是要在每个分位数组内部实现子组的均匀随机分配,而不是全局随机采样——后者自然会导致分布不均。这里有个适配任意分位数/子组数量、完全兼容dplyr管道的优雅方案:
实现代码
library(dplyr) # 构造示例数据 df <- structure(list(id = 1:300, value = runif(300, 20000, 100000)), class = "data.frame", row.names = c(NA, -300L)) groups <- 5 subgroups <- 3 set.seed(30) result.df <- df %>% # 第一步:按value划分分位数组 mutate(group = ntile(value, n = groups)) %>% # 第二步:按分位数组分组,在组内分配均匀随机的子组 group_by(group) %>% mutate( subgroup = sample( # 生成均匀分配的子组序列:先分配基础均等数量,再处理余数 c(rep(1:subgroups, each = n() %/% subgroups), rep(1:(n() %% subgroups), each = 1)), # 打乱顺序实现随机化 size = n(), replace = FALSE ) ) %>% # 取消分组(可选,根据后续操作需求) ungroup()
代码逻辑说明
- 分位数组划分:用
ntile完成基础的分位数分组,这个和你原来的逻辑一致。 - 组内均匀分配:
- 对每个分位数组,先计算每个子组的基础大小:
n() %/% subgroups(总组数除以子组数的整数部分)。 - 如果组内行数不能被子组数整除,把剩余的行依次分配给前几个子组(每个多1行),用
rep(1:(n() %% subgroups), each = 1)实现。 - 最后用
sample打乱这个均匀序列,保证子组分配是随机的,但数量严格均等(或仅差1行)。
- 对每个分位数组,先计算每个子组的基础大小:
验证结果
运行table(result.df$group, result.df$subgroup),你会得到完全均匀的分布:
1 2 3 1 20 20 20 2 20 20 20 3 20 20 20 4 20 20 20 5 20 20 20
(如果你的数据行数不是完美的倍数,比如301行,那么部分子组会是21行,其余是20行,依然是最均等的分配)
通用性说明
这个方案完全适配任意正整数的groups和subgroups参数:不管分位数组数量、子组数量是多少,都会自动计算每个组内的最优子组大小,同时保持随机性。
内容的提问来源于stack exchange,提问作者Lorcán
相关产品推荐
相关产品推荐

