生成向量元素的所有可能组合的列表

我试图在一个长度为14的向量中生成0和1的所有可能组合。有没有一种简单的方法可以获得作为向量列表的输出,或者更好的方法,一个数据框架?

为了更好地演示我所寻找的内容,假设我只需要一个长度为3的向量。我希望能够产生以下结果:

 (1,1,1), (0,0,0), (1,1,0), (1,0,0), (1,0,1), (0,1,0), (0,1,1), (0,0,0)
105474 次浏览

你在找 expand.grid

expand.grid(0:1, 0:1, 0:1)

或者,从长远来看:

n <- 14
l <- rep(list(0:1), n)


expand.grid(l)

有16384种可能的排列。您可以使用 iterpc包迭代地获取结果。

library(iterpc)
I = iterpc(2, 14, label=c(0,1), order=T, replace=T)
getnext(I)
# [1] 0 0 0 0 0 0 0 0 0 0 0 0 0 0
getnext(I)
# [1] 0 0 0 0 0 0 0 0 0 0 0 0 0 1
getnext(I)
# [1] 0 0 0 0 0 0 0 0 0 0 0 0 1 0

如果希望得到所有结果,仍然可以使用 getall(I)

作为@Justin 方法的替代方法,您还可以使用“ data.table”包中的 CJ。在这里,我还使用了 replicate来创建包含14个0和1的列表。

library(data.table)
do.call(CJ, replicate(14, 0:1, FALSE))
#        V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 V13 V14
#     1:  0  0  0  0  0  0  0  0  0   0   0   0   0   0
#     2:  0  0  0  0  0  0  0  0  0   0   0   0   0   1
#     3:  0  0  0  0  0  0  0  0  0   0   0   0   1   0
#     4:  0  0  0  0  0  0  0  0  0   0   0   0   1   1
#     5:  0  0  0  0  0  0  0  0  0   0   0   1   0   0
#    ---
# 16380:  1  1  1  1  1  1  1  1  1   1   1   0   1   1
# 16381:  1  1  1  1  1  1  1  1  1   1   1   1   0   0
# 16382:  1  1  1  1  1  1  1  1  1   1   1   1   0   1
# 16383:  1  1  1  1  1  1  1  1  1   1   1   1   1   0
# 16384:  1  1  1  1  1  1  1  1  1   1   1   1   1   1

因为您处理的是0和1,所以以位来考虑整数似乎是很自然的。使用一个从这个 邮寄(下面的 MyIntToBit)稍微改动过的函数,以及您选择的 apply函数,我们可以得到期望的结果。

MyIntToBit <- function(x, dig) {
i <- 0L
string <- numeric(dig)
while (x > 0) {
string[dig - i] <- x %% 2L
x <- x %/% 2L
i <- i + 1L
}
string
}

如果你想要一个列表,像这样使用 lapply:

lapply(0:(2^14 - 1), function(x) MyIntToBit(x,14))

如果你更喜欢矩阵,sapply可以做到:

sapply(0:(2^14 - 1), function(x) MyIntToBit(x,14))

以下是产出示例:

> lapply(0:(2^3 - 1), function(x) MyIntToBit(x,3))
[[1]]
[1] 0 0 0


[[2]]
[1] 0 0 1


[[3]]
[1] 0 1 0


[[4]]
[1] 0 1 1


[[5]]
[1] 1 0 0


[[6]]
[1] 1 0 1


[[7]]
[1] 1 1 0


[[8]]
[1] 1 1 1




> sapply(0:(2^3 - 1), function(x) MyIntToBit(x,3))
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
[1,]    0    0    0    0    1    1    1    1
[2,]    0    0    1    1    0    0    1    1
[3,]    0    1    0    1    0    1    0    1

tidyr有几个类似于 expand.grid()的选项。

tidyr::crossing()返回一个字符串,并且不将字符串转换为因子(尽管您可以执行 expand.grid(..., stringsAsFactors = F))。

library(tidyr)


crossing(var1 = 0:1, var2 = 0:1, var3 = 0:1)
# A tibble: 8 x 3
var1  var2  var3
<int> <int> <int>
1     0     0     0
2     0     0     1
3     0     1     0
4     0     1     1
5     1     0     0
6     1     0     1
7     1     1     0
8     1     1     1

tidyr::expand()可以给出数据中出现的仅有值的两种组合,如下所示:

expand(mtcars, nesting(vs, cyl))
# A tibble: 5 x 2
vs   cyl
<dbl> <dbl>
1     0     4
2     0     6
3     0     8
4     1     4
5     1     6

或者两个变量的所有可能组合,即使数据中没有这些特定值的观测,像这样:

expand(mtcars, vs, cyl)
# A tibble: 6 x 2
vs   cyl
<dbl> <dbl>
1     0     4
2     0     6
3     0     8
4     1     4
5     1     6
6     1     8

(你可以看到在 vs == 1 & cyl == 8的原始数据中没有观测值)

tidyr::complete()也可以类似于 expand.grid()使用,下面是来自文档的一个例子:

df <- dplyr::tibble(
group = c(1:2, 1),
item_id = c(1:2, 2),
item_name = c("a", "b", "b"),
value1 = 1:3,
value2 = 4:6
)
df %>% complete(group, nesting(item_id, item_name))


# A tibble: 4 x 5
group item_id item_name value1 value2
<dbl>   <dbl> <chr>      <int>  <int>
1     1       1 a              1      4
2     1       2 b              3      6
3     2       1 a             NA     NA
4     2       2 b              2      5

这给出了每个组的 item _ id 和 item _ name 的所有可能组合——它为 group=2 item_id=1item_name=a创建了一行。

这是一个不同的方法,以前的答案。如果您需要14个1和0的值的所有可能的组合,这就像生成从0到(2 ^ 14)-1的所有可能的数字,并保留它们的二进制表示形式。

n <- 14
lapply(0:(2^n-1), FUN=function(x) head(as.integer(intToBits(x)),n))

我在这里讨论一种通用的方法来解决所有类似这样的问题。首先,让我们看看随着 N 数的增加,解是如何演化的,从而找出一般的模式。

首先,长度1的解是

0
1

现在对于长度2,解变成(第2列中间隔 |) :

0 | 0 0, 0 1
1 | 1 0, 1 1

与之前长度为1的解相比,很明显,为了得到这个新的解,我们只需将0和1分别附加到以前的解(第1列,0和1)。

长度3的解是(第3列) :

0 | 0 0 | 0 0 0, 0 0 1
1 | 1 0 | 1 0 0, 1 0 1
| 0 1 | 0 1 0, 0 1 1
| 1 1 | 1 1 0, 1 1 1

同样,这个新的解决方案是通过将0和1分别附加到前一个解决方案(长度为2的第2列)而得到的。

这种观察自然会导致递归解决方案。假设我们已经得到了 N-1 solution(c(0,1), N-1)长度的解,为了得到 N 的解,我们只需将0和1附加到解 N-1 append_each_to_list(solution(c(0,1), N-1), c(0,1))的每个项上。注意这里一个更复杂的问题(解决 N)是如何自然地分解成一个更简单的问题(解决 N-1)的。

然后我们只需要把这些简单的英语翻译成字面意思的 R 代码:

# assume you have got solution for a shorter length len-1 -> solution(v, len-1)
# the solution of length len will be the solution of shorter length appended with each element in v
solution <- function(v, len) {
if (len<=1) {
as.list(v)
} else {
append_each_to_list(solution(v, len-1), v)
}
}


# function to append each element in vector v to list L and return a list
append_each_to_list <- function(L, v) {
purrr::flatten(lapply(v,
function(n) lapply(L, function(l) c(l, n))
))
}

调用函数:

> solution(c(1,0), 3)
[[1]]
[1] 1 1 1


[[2]]
[1] 0 1 1


[[3]]
[1] 1 0 1


[[4]]
[1] 0 0 1


[[5]]
[1] 1 1 0


[[6]]
[1] 0 1 0


[[7]]
[1] 1 0 0

前言

这里有很多不错的答案。我想为我们这些似乎不能理解所提供的实现的人添加一个。这里的解决方案本质上是循环的泛化,这就是为什么递归解决方案看起来如此优雅的原因。没有人直接把它写成一个循环——我认为给出最直接的解决方案是有好处的,只是为了追踪实际发生的情况。

这并不能保证有很好的性能——而且大多数其他的答案更实用。其目的是让你追踪到实际发生的事情。

数学

组合是一个集合的所有唯一选择,其中元素的顺序并不重要([0,1]不同于 [1,0])。您的列表包含 N元素,并且您正在选择 K元素,以获得总数为 N ^ k的组合。

前女友。

你有三个字母,[‘ a’,‘ b’,‘ c’] ,想找到所有独特的方法来排列其中的两个字母,允许字母被重复拉出(因此 [‘ a’,‘ a’]是允许的)。N = 3K = 2——我们有三样东西,我们想找到所有不同的方法来选择其中的两样。有9种选择方法(3 ^ 2—— > N ^ k)。

准则

如前所述,最简单的解决方案需要大量的循环。

随着 K值的增加,不断添加循环和值以进行选择。

set <- c("a", "b", "c")
n <- length(set)


# k = 1
# There are only three ways to pick one thing from a selection of three items!
sprintf("Number of combinations:%4d", n^1)
for(i in seq_along(set)){
print(paste(set[i]))
}


# k = 2
sprintf("Number of combinations:%4d", n^2)
for(i in seq_along(set)){
for(j in seq_along(set)){
print(paste(set[i], set[j]))
}
}


# k = 3
sprintf("Number of combinations:%4d", n^3)
for(i in seq_along(set)){
for(j in seq_along(set)){
for(k in seq_along(set)){
print(paste(set[i], set[j], set[k]))
}
}
}


# See the pattern? The value of k corresponds
# to the number of loops and to the number of
# indexes on `set`

含有 cross()及其变体的 purrr解决方案:

library(purrr)


cross(list(0:1, 0:1, 0:1)) %>% simplify_all()


# [[1]]
# [1] 0 0 0
#
# [[2]]
# [1] 1 0 0
#
# [[3]]
# [1] 0 1 0
#
# ...
#
# [[8]]
# [1] 1 1 1
cross_df(list(var1 = 0:1, var2 = 0:1, var3 = 0:1))


# # A tibble: 8 × 3
#    var1  var2  var3
#   <int> <int> <int>
# 1     0     0     0
# 2     1     0     0
# 3     0     1     0
# 4     1     1     0
# 5     0     0     1
# 6     1     0     1
# 7     0     1     1
# 8     1     1     1

使用 dplyr,您可以使用 full_join(x, y, by = character())执行交叉连接,生成 xy的所有组合。

Reduce(\(x, y) full_join(x, y, by = character()),
list(tibble(var1 = 0:1), tibble(var2 = 0:1), tibble(var3 = 0:1)))


# # A tibble: 8 × 3
#    var1  var2  var3
#   <int> <int> <int>
# 1     0     0     0
# 2     0     0     1
# 3     0     1     0
# 4     0     1     1
# 5     1     0     0
# 6     1     0     1
# 7     1     1     0
# 8     1     1     1

一个漂亮的最小可重复例子 给你:

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"

所有功劳都归 @ RichScriven