确定所有组合但使用分组变量

Determining All Combinations but With a Grouping Variable

我有以下数据列表。

Input <- list(c("1", "2"), c("3", "4"), c("5", "6", "7"))

我想从每个列表元素中取出一个项目并将它们组合成一个向量。然后,从每个列表元素中的剩余项目中,我想重复这个过程,从每个列表元素中取出另一个项目并将它们组合到另一个向量中。我想重复这些步骤,直到达到某个预定值(在本例中为 2;2 是最大数字,因为它恰好是 Input 列表中每个列表元素的最小长度)。

有很多可能的方法可以做到这一点,我希望找到一种可以 return 每种可能性的方法,例如下面的 Output 列表。我真的不关心输出的形式,只要它包含相同的信息即可。

Output <- lapply(list(rbind(as.character(c(1, 3, 5)), as.character(c(2, 4, 6))), rbind(as.character(c(1, 3, 5)), as.character(c(2, 4, 7))), rbind(as.character(c(1, 3, 6)), as.character(c(2, 4, 5))), rbind(as.character(c(1, 3, 6)), as.character(c(2, 4, 7))), rbind(as.character(c(1, 3, 7)), as.character(c(2, 4, 5))), rbind(as.character(c(1, 3, 7)), as.character(c(2, 4, 6))), rbind(as.character(c(1, 4, 5)), as.character(c(2, 3, 6))), rbind(as.character(c(1, 4, 5)), as.character(c(2, 3, 7))), rbind(as.character(c(1, 4, 6)), as.character(c(2, 3, 5))), rbind(as.character(c(1, 4, 6)), as.character(c(2, 3, 7))), rbind(as.character(c(1, 4, 7)), as.character(c(2, 3, 5))), rbind(as.character(c(1, 4, 7)), as.character(c(2, 3, 6)))), function (x) {
  lapply(as.data.frame(t(x)), function (y) {
    y
  })
})

这个例子非常小。实际上,我可能会有更多的组(Input 列表中的列表元素)和每个组中的更多元素,并且组的大小可能与我的示例中的不同。是否有一种有效的、程序化的方式来执行此操作?我很想看到使用 base 函数的解决方案,但我对任何事情都持开放态度。 expand.grid() 函数不起作用,因为它没有考虑我的分组变量。

更新

之前好像误解了你的问题,现在我想明白了:

lst <- expand.grid(Input)

res <- Filter(
    length,
    combn(
        1:nrow(lst),
        min(lengths(Input)),
        function(x) {
            if (all(colSums(apply(lst[x, ], 2, duplicated)) == 0)) {
                lst[x, ]
            }
        },
        simplify = FALSE
    )
)

这给出了

> res
[[1]]
  Var1 Var2 Var3
1    1    3    5
8    2    4    6

[[2]]
   Var1 Var2 Var3
1     1    3    5
12    2    4    7

[[3]]
  Var1 Var2 Var3
2    2    3    5
7    1    4    6

[[4]]
   Var1 Var2 Var3
2     2    3    5
11    1    4    7

[[5]]
  Var1 Var2 Var3
3    1    4    5
6    2    3    6

[[6]]
   Var1 Var2 Var3
3     1    4    5
10    2    3    7

[[7]]
  Var1 Var2 Var3
4    2    4    5
5    1    3    6

[[8]]
  Var1 Var2 Var3
4    2    4    5
9    1    3    7

[[9]]
   Var1 Var2 Var3
5     1    3    6
12    2    4    7

[[10]]
   Var1 Var2 Var3
6     2    3    6
11    1    4    7

[[11]]
   Var1 Var2 Var3
7     1    4    6
10    2    3    7

[[12]]
  Var1 Var2 Var3
8    2    4    6
9    1    3    7

上一个答案

您可以使用 expand.grid + combn

尝试下面的代码
lst <- lapply(Input, combn, min(lengths(Input)))
res <- do.call(
    rbind,
    apply(
        expand.grid(lapply(lst, function(v) seq(ncol(v)))),
        1,
        function(k) {
            expand.grid(
                Map(
                    function(x, y) x[, y],
                    lst,
                    k
                )
            )
        }
    )
)

这给出了

> res
   Var1 Var2 Var3
1     1    3    5
2     2    3    5
3     1    4    5
4     2    4    5
5     1    3    6
6     2    3    6
7     1    4    6
8     2    4    6
9     1    3    5
10    2    3    5
11    1    4    5
12    2    4    5
13    1    3    7
14    2    3    7
15    1    4    7
16    2    4    7
17    1    3    6
18    2    3    6
19    1    4    6
20    2    4    6
21    1    3    7
22    2    3    7
23    1    4    7
24    2    4    7

获取输入的最小长度n
获取Input的第一个最小长度的元素i.
创建一个列表 x,每个输入包含大小 n 的所有唯一排列。
只取元素i.
的一种组合 expand.grid x 得到结果。

i <- which.min(lengths(Input))
n <- length(Input[[i]])
x <- lapply(Input, \(x) {
  . <- do.call(expand.grid, rep(list(x), n))
  asplit(.[!apply(., 1, anyDuplicated),], 1)
})
x[[i]]  <- x[[i]][1]
y <- do.call(expand.grid, x)
y
#   Var1 Var2 Var3
#1  2, 1 4, 3 6, 5
#2  2, 1 3, 4 6, 5
#3  2, 1 4, 3 7, 5
#4  2, 1 3, 4 7, 5
#5  2, 1 4, 3 5, 6
#6  2, 1 3, 4 5, 6
#7  2, 1 4, 3 7, 6
#8  2, 1 3, 4 7, 6
#9  2, 1 4, 3 5, 7
#10 2, 1 3, 4 5, 7
#11 2, 1 4, 3 6, 7
#12 2, 1 3, 4 6, 7

#Or more in a format like given in the question
apply(y, 1, \(.) {matrix(unlist(.), length(.[[1]]))}, simplify = FALSE)
#[[1]]
#     [,1] [,2] [,3]
#[1,] "2"  "4"  "6" 
#[2,] "1"  "3"  "5" 
#
#[[2]]
#     [,1] [,2] [,3]
#[1,] "2"  "3"  "6" 
#[2,] "1"  "4"  "5" 
#
#[[3]]
#     [,1] [,2] [,3]
#[1,] "2"  "4"  "7" 
#[2,] "1"  "3"  "5" 
#
#[[4]]
#     [,1] [,2] [,3]
#[1,] "2"  "3"  "7" 
#[2,] "1"  "4"  "5" 
#
#[[5]]
#     [,1] [,2] [,3]
#[1,] "2"  "4"  "5" 
#[2,] "1"  "3"  "6" 
#
#[[6]]
#     [,1] [,2] [,3]
#[1,] "2"  "3"  "5" 
#[2,] "1"  "4"  "6" 
#
#[[7]]
#     [,1] [,2] [,3]
#[1,] "2"  "4"  "7" 
#[2,] "1"  "3"  "6" 
#
#[[8]]
#     [,1] [,2] [,3]
#[1,] "2"  "3"  "7" 
#[2,] "1"  "4"  "6" 
#
#[[9]]
#     [,1] [,2] [,3]
#[1,] "2"  "4"  "5" 
#[2,] "1"  "3"  "7" 
#
#[[10]]
#     [,1] [,2] [,3]
#[1,] "2"  "3"  "5" 
#[2,] "1"  "4"  "7" 
#
#[[11]]
#     [,1] [,2] [,3]
#[1,] "2"  "4"  "6" 
#[2,] "1"  "3"  "7" 
#
#[[12]]
#     [,1] [,2] [,3]
#[1,] "2"  "3"  "6" 
#[2,] "1"  "4"  "7" 

生成排列的部分可以改进,例如通过使用 gtools::permutations.

基准:

Input <- list(c("1", "2"), c("3", "4"), c("5", "6", "7"))
bench::mark(check=FALSE,
  TIC = {
    lst <- expand.grid(Input)
    Filter(
      length,
      combn(
        1:nrow(lst),
        min(lengths(Input)),
        function(x) {
          if (all(colSums(apply(lst[x, ], 2, duplicated)) == 0)) {
            lst[x, ]
          }
        },
        simplify = FALSE
      )
    )
  } ,
  GKi = {
    n <- min(lengths(Input))
    i <- match(n, lengths(Input))
    x <- lapply(Input, \(x) {
      . <- do.call(expand.grid, rep(list(x), n))
      asplit(.[!apply(., 1, anyDuplicated),], 1)
    })
  x[[i]]  <- x[[i]][1]
  do.call(expand.grid, x) #Here we have the result
}
)
#  expression      min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc
#  <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>
#1 TIC          8.66ms   9.78ms      102.    37.6KB     32.9    37    12
#2 GKi         644.1µs 676.21µs     1345.      19KB     25.7   628    12

@GKi 在这个例子中比@ThomasIsCoding 快 10 倍 并且使用更少的内存。