如何拆分R数据框:每个块含因子列所有唯一值且最大化分块数
问题与解决方案
问题背景
我们通过以下R代码生成了数据框df:
library(dplyr) library(stringr) letters_comb <- expand.grid(LETTERS, LETTERS) %>% mutate(Combination = paste0(Var1, Var2)) %>% select(Combination) %>% sample_n(100) set.seed(123) n_times <- round(runif(100, 80, 120)) df <- data.frame(fac_var = as.factor(rep(letters_comb$Combination, times = n_times)))
需要将df拆分成尽可能多的子块,满足两个核心要求:
- 每个子块都包含
fac_var列的所有唯一取值 - 原数据的每一行都必须被分配到某个子块中
优先采用dplyr或tidyverse生态下的实现方案。
实现方案
核心思路
要拆分出最多的有效块,本质是找到所有fac_var分组中行数最少的那个组——这个组的行数就是能拆分的最大块数。我们给每个fac_var组内的行按顺序编号,然后将编号相同的行归为一个块,这样每个块都会覆盖所有fac_var的取值。
代码实现
library(dplyr) # 第一步:为每个fac_var分组内的行分配组内序号 df_with_block <- df %>% group_by(fac_var) %>% mutate(block_id = row_number()) %>% ungroup() # 第二步:按序号拆分数据,得到子块列表 df_blocks <- df_with_block %>% group_split(block_id, .keep = FALSE)
代码说明
group_by(fac_var) %>% mutate(block_id = row_number()):对每个fac_var的分组,给组内每行从1开始递增编号。比如某个fac_var有90行,它的block_id就是1到90;另一个只有81行,编号就是1到81。group_split(block_id, .keep = FALSE):根据block_id拆分数据,每个编号对应一个子块。最终子块的数量等于所有fac_var分组中最小的行数(因为超过该行数的编号,会有部分fac_var分组没有对应行,无法形成有效块)。.keep = FALSE用于移除辅助列block_id,让子块只保留原数据的列。
结果验证
可以通过以下代码确认所有子块都满足要求:
# 验证第一个子块是否包含所有fac_var唯一值 length(unique(df_blocks[[1]]$fac_var)) == length(unique(df$fac_var)) # 批量验证所有子块 all(sapply(df_blocks, function(block) { length(unique(block$fac_var)) == length(unique(df$fac_var)) }))
内容的提问来源于stack exchange,提问作者nicholas
相关产品推荐
相关产品推荐

