拆分 data.frame 行并打乱顺序

Split data.frame rows and shuffle their order

我有一个 data.frame 这样的:

set.seed(1)
df <- data.frame(id = c("A","B;C","D","E","F;G;H","I"), val1 = rnorm(6), val2 = letters[1:6], stringsAsFactors=F)

有一个 id 列,它的一些值有一个分号,表示它结合了几个 id,并且它们在其余列中的值是共享的。

对于带有分号的 id 的每一行:

  1. 我想用分号分隔符 id 拆分
  2. 根据拆分的 id 行数复制 data.frame
  3. 随机shuffle复制顺序data.frame
  4. 用我在 3 中创建的行替换 df 中的原始行,这样 df 中所有其他行的顺序不变。

这是我的笨拙尝试:

idx <- which(grepl(";",df$id))
l <- lapply(idx, function(i){
  ids <- strsplit(df$id[i], split = ";")[[1]]
  df.i <- do.call("rbind", replicate(length(ids), df[i,,drop=F], simplify = FALSE))
  df.i$id <- ids[permute::shuffle(ids)]
  return(df.i)
})

idx.names <- df$id[idx]

for(i in 1:length(idx.names)){
  df <- rbind(df[1:(which(df$id == idx.names[i])-1),,drop=F],
              l[[i]],
              df[(which(df$id == idx.names[i])+1):nrow(df),,drop=F])
}

所以我正在寻找更优雅(可能使用 tidyversedata.table)和更快的东西。

我们可以用separate_rows,然后按'val2'分组,sample row_number()slice

library(tidyverse)
df %>% 
   separate_rows(id) %>%
   group_by(val2) %>%
   slice(sample(row_number()))