【问题标题】:Unordered combinations of all lengths所有长度的无序组合
【发布时间】:2020-07-15 17:13:52
【问题描述】:

我正在寻找一个函数,它返回一个向量的所有无序组合。例如

x <- c('red','blue','black')
uncomb(x)
[1]'red'
[2]'blue'
[3]'black'
[4]'red','blue'
[5]'blue','black'
[6]'red','black'
[7]'red','blue','black'

我猜有些库中有一个函数可以做到这一点,但找不到它。我正在尝试使用permutationsgtool,但这不是我要寻找的功能。

【问题讨论】:

  • 我不会发布我的答案,因为它真的很接近 Richard Scriven 的答案。但是,如果你想利用gtool 包,你可以使用combinations 而不是permutationssapply(seq_along(x), combinations, v = x, n = length(x))

标签: r combinations powerset


【解决方案1】:

不使用任何外部包的矩阵结果解决方案:

store <- lapply(
  seq_along(x), 
  function(i) {
    out <- combn(x, i) 
    N <- NCOL(out)
    length(out) <- length(x) * N
    matrix(out, ncol = N, byrow = TRUE)
})
t(do.call(cbind, store))

     [,1]    [,2]    [,3]   
[1,] "red"   NA      NA     
[2,] "blue"  NA      NA     
[3,] "black" NA      NA     
[4,] "red"   "black" NA     
[5,] "blue"  "blue"  NA     
[6,] "red"   "black" NA     
[7,] "red"   "blue"  "black"

【讨论】:

  • 您可以将3L 更改为length(x) 以获得更通用的解决方案
【解决方案2】:

我从List All Combinations With combn 被重定向到这里,因为这是被骗的目标之一。这是一个老问题,@RichScriven 提供的答案非常好,但我想为社区提供一些可以说更自然、更有效的选择(最后两个)。

我们首先注意到输出与Power Set 非常相似。从rje 包中调用powerSet,我们看到我们的输出确实匹配幂集中的每个元素,除了第一个元素等效于Empty Set

x <- c("red", "blue", "black")
rje::powerSet(x)
[[1]]
character(0)   ## empty set equivalent

[[2]]
[1] "red"

[[3]]
[1] "blue"

[[4]]
[1] "red"  "blue"

[[5]]
[1] "black"

[[6]]
[1] "red"   "black"

[[7]]
[1] "blue"  "black"

[[8]]
[1] "red"   "blue"  "black"

如果您不想要第一个元素,您可以轻松地将 [-1] 添加到函数调用的末尾,如下所示:rje::powerSet(x)[-1]

接下来的两个解决方案来自较新的包arrangementsRcppAlgos(我是作者),这将为用户带来极大的效率提升。这两个包都能够生成Multisets 的组合。

为什么这很重要?

可以证明有一个one-to-one mappingA的幂集到多重集c(rep(emptyElement, length(A)), A)的所有组合选择length(A),其中emptyElement是空集的表示(比如零或空白)。考虑到这一点,请注意:

## There is also a function called combinations in the
## rje package, so we fully declare the function with
## scope operator
library(arrangements)
arrangements::combinations(x = c("",x), k = 3, freq = c(2, rep(1, 3)))
     [,1]  [,2]   [,3]   
[1,] ""    ""     "red"  
[2,] ""    ""     "blue" 
[3,] ""    ""     "black"
[4,] ""    "red"  "blue" 
[5,] ""    "red"  "black"
[6,] ""    "blue" "black"
[7,] "red" "blue" "black"

library(RcppAlgos)
comboGeneral(c("",x), 3, freqs = c(2, rep(1, 3)))
     [,1]  [,2]   [,3]   
[1,] ""    ""     "red"  
[2,] ""    ""     "blue" 
[3,] ""    ""     "black"
[4,] ""    "red"  "blue" 
[5,] ""    "red"  "black"
[6,] ""    "blue" "black"
[7,] "red" "blue" "black"

如果您不喜欢处理空白元素和/或矩阵,您还可以返回一个使用lapply 的列表。

lapply(seq_along(x), comboGeneral, v = x)
[[1]]
     [,1]   
[1,] "red"  
[2,] "blue" 
[3,] "black"

[[2]]
     [,1]   [,2]   
[1,] "red"  "blue" 
[2,] "red"  "black"
[3,] "blue" "black"

[[3]]
     [,1]  [,2]   [,3]   
[1,] "red" "blue" "black"


lapply(seq_along(x), function(y) arrangements::combinations(x, y))
[[1]]
     [,1]   
[1,] "red"  
[2,] "blue" 
[3,] "black"

[[2]]
     [,1]   [,2]   
[1,] "red"  "blue" 
[2,] "red"  "black"
[3,] "blue" "black"

[[3]]
     [,1]  [,2]   [,3]   
[1,] "red" "blue" "black"

现在我们证明最后两种方法效率更高(注意,我从@RichSciven 提供的答案中删除了do.call(c,simplify = FALSE,以便比较相似输出的生成。我还包括rje::powerSet 好测量):

set.seed(8128)
bigX <- sort(sample(10^6, 20)) ## With this as an input, we will get 2^20 - 1 results.. i.e. 1,048,575
library(microbenchmark)
microbenchmark(powSetRje = powerSet(bigX),
               powSetRich = lapply(seq_along(bigX), combn, x = bigX),
               powSetArrange = lapply(seq_along(bigX), function(y) arrangements::combinations(x = bigX, k = y)),
               powSetAlgos = lapply(seq_along(bigX), comboGeneral, v = bigX),
               unit = "relative")

Unit: relative
          expr        min        lq      mean   median        uq      max neval
     powSetRje 64.4252454 44.063199 16.678438 18.63110 12.082214 7.317559   100
    powSetRich 61.6766640 43.027789 16.009151 17.88944 11.406994 7.222899   100
 powSetArrange  0.9508052  1.060309  1.080341  1.02257  1.262713 1.126384   100
   powSetAlgos  1.0000000  1.000000  1.000000  1.00000  1.000000 1.000000   100

更进一步,arrangements 配备了一个名为layout 的参数,它允许用户为他们的输出选择一种特定的格式。其中之一是layout = "l" 用于列表。它类似于在combn 中设置simplify = FALSE,并允许我们获得类似于powerSet 的输出。观察:

do.call(c, lapply(seq_along(x), function(y) {
                    arrangements::combinations(x, y, layout = "l")
                  }))
[[1]]
[1] "red"

[[2]]
[1] "blue"

[[3]]
[1] "black"

[[4]]
[1] "red"  "blue"

[[5]]
[1] "red"   "black"

[[6]]
[1] "blue"  "black"

[[7]]
[1] "red"   "blue"  "black"

以及基准测试:

microbenchmark(powSetRje = powerSet(bigX)[-1],
               powSetRich = do.call(c, lapply(seq_along(bigX), combn, x = bigX, simplify = FALSE)),
               powSetArrange = do.call(c, lapply(seq_along(bigX), function(y) arrangements::combinations(bigX, y, layout = "l"))),
               times = 15, unit = "relative")
Unit: relative
          expr      min       lq     mean   median       uq      max neval
     powSetRje 5.539967 4.785415 4.277319 4.387410 3.739593 3.543570    15
    powSetRich 4.994366 4.306784 3.863612 3.932252 3.334708 3.327467    15
 powSetArrange 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000    15    15

【讨论】:

  • 我将如何使用此函数来获取一系列长度的所有组合?例如。如果我的输入向量是 x
【解决方案3】:

您可以在combn() 函数的m 参数上应用长度为x 的序列。

x <- c("red", "blue", "black")
do.call(c, lapply(seq_along(x), combn, x = x, simplify = FALSE))
# [[1]]
# [1] "red"
# 
# [[2]]
# [1] "blue"
# 
# [[3]]
# [1] "black"
# 
# [[4]]
# [1] "red"  "blue"
# 
# [[5]]
# [1] "red"   "black"
# 
# [[6]]
# [1] "blue"  "black"
# 
# [[7]]
# [1] "red"   "blue"  "black"

如果您更喜欢矩阵结果,则可以将stringi::stri_list2matrix() 应用于上面的列表。

stringi::stri_list2matrix(
    do.call(c, lapply(seq_along(x), combn, x = x, simplify = FALSE)),
    byrow = TRUE
)
#      [,1]    [,2]    [,3]   
# [1,] "red"   NA      NA     
# [2,] "blue"  NA      NA     
# [3,] "black" NA      NA     
# [4,] "red"   "blue"  NA     
# [5,] "red"   "black" NA     
# [6,] "blue"  "black" NA     
# [7,] "red"   "blue"  "black"

【讨论】:

  • 是的 - unlist(lapply(seq_along(x), combn, x=x, simplify=FALSE),recursive=FALSE) 用于另一个潜在的输出变化。不等长的数据对象非常适合list
  • 我同意,但在评论中提示我更接近所需的输出。即使lapply(seq_along(x), combn, x = x) 完全按照应有的方式读取,这是按列显示的
  • 该列表(在我的变体中)几乎正是 OP 在问题中作为所需输出呈现的内容。由于所有的 NA,使用矩阵似乎要传递给其他函数要困难得多。
  • 我完全同意@thelatemail - 我已经在第一部分进行了编辑。出于某种原因,我更喜欢do.call(c, ...) 而不是unlist(..., recursive = FALSE)
  • 很多东西 - “番茄,番茄,让我们把这一切都取消吧……”
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-02-02
  • 1970-01-01
  • 1970-01-01
  • 2011-06-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多