两个(或更多)向量所有元素的唯一组合

144

我正在尝试在R中创建来自两个不同大小向量的所有元素的唯一组合。

例如,第一个向量是

a <- c("ABC", "DEF", "GHI")

第二种类型是目前以字符串形式存储的日期数据

b <- c("2012-05-01", "2012-05-02", "2012-05-03", "2012-05-04", "2012-05-05")

我需要创建一个像这样的具有两列的数据框:

> data
    a          b
1  ABC 2012-05-01
2  ABC 2012-05-02
3  ABC 2012-05-03
4  ABC 2012-05-04
5  ABC 2012-05-05
6  DEF 2012-05-01
7  DEF 2012-05-02
8  DEF 2012-05-03
9  DEF 2012-05-04
10 DEF 2012-05-05
11 GHI 2012-05-01
12 GHI 2012-05-02
13 GHI 2012-05-03
14 GHI 2012-05-04
15 GHI 2012-05-05

基本上,我想通过将一个向量(a)的所有元素与第二个向量(b)的所有元素并置来寻找唯一的组合。

理想的解决方案可以推广到更多输入向量。


另请参阅:
如何生成组合矩阵

7个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
176

这可能是你想要的内容。

> expand.grid(a,b)
   Var1       Var2
1   ABC 2012-05-01
2   DEF 2012-05-01
3   GHI 2012-05-01
4   ABC 2012-05-02
5   DEF 2012-05-02
6   GHI 2012-05-02
7   ABC 2012-05-03
8   DEF 2012-05-03
9   GHI 2012-05-03
10  ABC 2012-05-04
11  DEF 2012-05-04
12  GHI 2012-05-04
13  ABC 2012-05-05
14  DEF 2012-05-05
15  GHI 2012-05-05

如果得到的顺序不是您想要的,请稍后进行排序。如果您为 expand.grid 命名参数,则它们将成为列名:

df = expand.grid(a = a, b = b)
df[order(df$a), ]

expand.grid可以推广到任意数量的输入列。


4
而且不需要使用 plyr 就可以进行排序: result <- expand.grid(a=a,b=b); result <- result[order(result$a,result$b),]; - thelatemail
2
有比我更高的声望的人能接受这个答案吗? - Josh
如果顺序和名称应与问题中的相同:expand.grid(b=b,a=a)[2:1] - GKi
请注意标题为“唯一组合” - 此答案解决了 OP 的问题,但如果两列具有相同的数据类型并且应用 expand.grid,则会得到唯一的排列而不是唯一的组合。 - Brent

49

tidyr包提供了很好的替代方法crossing,它比经典的expand.grid函数效果更好,因为(1)字符串不会被转换为因子,(2)排序更加直观:

library(tidyr)

a <- c("ABC", "DEF", "GHI")
b <- c("2012-05-01", "2012-05-02", "2012-05-03", "2012-05-04", "2012-05-05")

crossing(a, b)

# A tibble: 15 x 2
       a          b
   <chr>      <chr>
 1   ABC 2012-05-01
 2   ABC 2012-05-02
 3   ABC 2012-05-03
 4   ABC 2012-05-04
 5   ABC 2012-05-05
 6   DEF 2012-05-01
 7   DEF 2012-05-02
 8   DEF 2012-05-03
 9   DEF 2012-05-04
10   DEF 2012-05-05
11   GHI 2012-05-01
12   GHI 2012-05-02
13   GHI 2012-05-03
14   GHI 2012-05-04
15   GHI 2012-05-05

expand.grid有一个stringsAsFactors参数,可以设置为FALSE,从而解决(1)。 - Jojanzing

27

在这个概述中被忽略的是包中的CJ函数。使用:

library(data.table)
CJ(a, b, unique = TRUE)
给出:
      a          b
 1: ABC 2012-05-01
 2: ABC 2012-05-02
 3: ABC 2012-05-03
 4: ABC 2012-05-04
 5: ABC 2012-05-05
 6: DEF 2012-05-01
 7: DEF 2012-05-02
 8: DEF 2012-05-03
 9: DEF 2012-05-04
10: DEF 2012-05-05
11: GHI 2012-05-01
12: GHI 2012-05-02
13: GHI 2012-05-03
14: GHI 2012-05-04
15: GHI 2012-05-05

注意: 自版本1.12.2起,CJ会为结果列自动命名(详见这里这里)。


10
自1.0.0版本以来,tidyr 提供了自己的版本 expand.grid()。它通过使用向量提供了一个低级函数,补充了现有的 expand(), nesting()crossing() 函数族。base::expand.grid() 相比:

首先对第一个元素进行变化。不会将字符串转换为因子。不添加任何其他属性。返回的是 tibble 而不是 data frame。可以扩展任何广义向量,包括数据框。

a <- c("ABC", "DEF", "GHI")
b <- c("2012-05-01", "2012-05-02", "2012-05-03", "2012-05-04", "2012-05-05")

tidyr::expand_grid(a, b)

   a     b         
   <chr> <chr>     
 1 ABC   2012-05-01
 2 ABC   2012-05-02
 3 ABC   2012-05-03
 4 ABC   2012-05-04
 5 ABC   2012-05-05
 6 DEF   2012-05-01
 7 DEF   2012-05-02
 8 DEF   2012-05-03
 9 DEF   2012-05-04
10 DEF   2012-05-05
11 GHI   2012-05-01
12 GHI   2012-05-02
13 GHI   2012-05-03
14 GHI   2012-05-04
15 GHI   2012-05-05

4
您可以使用order函数对任意数量的列进行排序。以您的示例为例。
df <- expand.grid(a,b)
> df
   Var1       Var2
1   ABC 2012-05-01
2   DEF 2012-05-01
3   GHI 2012-05-01
4   ABC 2012-05-02
5   DEF 2012-05-02
6   GHI 2012-05-02
7   ABC 2012-05-03
8   DEF 2012-05-03
9   GHI 2012-05-03
10  ABC 2012-05-04
11  DEF 2012-05-04
12  GHI 2012-05-04
13  ABC 2012-05-05
14  DEF 2012-05-05
15  GHI 2012-05-05

> df[order( df[,1], df[,2] ),] 
   Var1       Var2
1   ABC 2012-05-01
4   ABC 2012-05-02
7   ABC 2012-05-03
10  ABC 2012-05-04
13  ABC 2012-05-05
2   DEF 2012-05-01
5   DEF 2012-05-02
8   DEF 2012-05-03
11  DEF 2012-05-04
14  DEF 2012-05-05
3   GHI 2012-05-01
6   GHI 2012-05-02
9   GHI 2012-05-03
12  GHI 2012-05-04
15  GHI 2012-05-05`

1
在基本的R中,您可以尝试使用merge()、cbind()和expand.grid()函数。
a <- seq(1E4)
b <- c("2012-05-01", "2012-05-02", "2012-05-03", "2012-05-04", "2012-05-05")

 microbenchmark(
  "merge (1)" = mmm <- as.matrix(merge(a, b)),
  "diy (2)"   = {ccc <- cbind( rep(a, length(b)),
                               b[rep(seq_along(b), each = length(a))]
                        )
                },
 "diy R (3)"  = {ccc <- cbind( a,
                               b[rep(seq_along(b), each = length(a))]
                        )
                },
  "grid (4)"  = ggg <- expand.grid(a, b),
  times       = 2
)

输出。

Unit: milliseconds
      expr      min       lq     mean   median       uq      max neval
 merge (1) 863.3100 863.3100 888.6573 888.6573 914.0046 914.0046     2
   diy (2) 117.1912 117.1912 142.1394 142.1394 167.0875 167.0875     2
 diy R (3)  34.9320  34.9320  49.4119  49.4119  63.8918  63.8918     2
  grid (4)  45.1876  45.1876  46.1592  46.1592  47.1308  47.1308     2

1
你可以使用rep和基本的R数据框循环的事实:
data.frame(
  a = rep(a, each = length(b)),
  b = b
)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,