簡體   English   中英

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

[英]Generate list of all possible combinations of elements of vector

我正在嘗試在長度為 14 的向量中生成 0 和 1 的所有可能組合。是否有一種簡單的方法可以將 output 作為向量列表,或者更好的是 dataframe?

為了更好地展示我在尋找什么,假設我只想要一個長度為 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)

您正在尋找expand.grid

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

或者,對於長案例:

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

expand.grid(l)

tidyr有幾個類似於expand.grid()的選項。

tidyr::crossing()返回一個 tibble 並且不將字符串轉換為因子(盡管您可以執行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創建了一行。

作為@Justin 方法的替代方案,您還可以使用“data.table”包中的CJ 在這里,我還使用了replicate來創建我的 14 個零和一的列表。

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

我在這里討論一種通用方法來解決所有類似類型的問題,例如這個。 首先讓我們看看解決方案如何隨着 N 數量的增加而演變,以找出一般模式。

首先,長度為 1 的解是

0
1

現在對於長度 2,解決方案變為(由 | 分隔的第 2 列):

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

將其與長度為 1 的先前解決方案進行比較,很明顯,要獲得此新解決方案,我們只需將 0 和 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

有 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)

由於您正在處理 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 

這是與先前答案不同的方法。 如果您需要 1 和 0 的 14 個值的所有可能組合,這就像生成從 0 到 (2^14)-1 的所有可能數字並保持它們的二進制表示。

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

前言

這里有很多不錯的答案。 我想為我們這些似乎無法理解所提供的實現的人添加一個。 這里的解決方案本質上是循環的泛化,這就是遞歸解決方案看起來如此優雅的原因。 沒有人直接把它寫成一個循環——我認為給出最直接的解決方案是有好處的,只是為了追蹤實際發生的事情。

這不能保證有很好的性能——大多數其他答案更實用。 目的是讓您追蹤實際發生的事情。

數學

組合是集合的所有唯一選擇,其中元素的順序無關緊要( [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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM