如何在R中计算列表的笛卡尔幂

4

我想在 R 中生成任意集合的笛卡尔。 例如,在 Python 中,我会按照以下方式实现:

from itertools import product
c = [1, 2, 3]
n = 2
l = list(product(c, repeat=n))

这将导致以下输出。
[(1, 1)
(1, 2)
(1, 3)
(2, 1)
(2, 2)
(2, 3)
(3, 1)
(3, 2)
(3, 3)]

我对R语言还比较陌生,所以想知道是否有内置函数可以实现这个功能。需要注意的是,我特别关注如何增加功率(在Python中称为重复参数)。


4
do.call("expand.grid", rep(list(1:3), 2)) 的结果如同问题中所示。https://www.rdocumentation.org/packages/rje/versions/1.10.10/topics/powerSet 给出了幂集。 t(combn(1:3, 2))则给出了长度为2的唯一组合。 - G. Grothendieck
1
“expand.grid(1:3,1:3)”不是基本上可以实现相同的结果吗? - fabla
@G.Grothendieck expand.grid函数对于任意集合(例如c(1, 3, 7))并不足够。是否有解决方法? - JuNijland
1
@Base_R_Best_R 我想明确控制对任意集合进行计算的次数。这也意味着对于例如 expand.grid(c(1,4,9), c(1,4,9), c(1,4,9)),我要控制计算的次数。我正在寻找一个带有参数n的函数,该参数指定计算乘积的次数。 - JuNijland
2个回答

3
建议的解决方案忽略了顺序。您将注意到,expand.grid 在每次迭代中迭代左侧元素,这与Python的itertools.product生成器的顺序不同。请注意:
s <- c(1, 2, 3)
n <- 2
do.call("expand.grid", rep(list(s), n))
  Var1 Var2
1    1    1
2    2    1
3    3    1
4    1    2    <-- This is the second result using python's product
5    2    2
6    3    2
7    1    3    <-- This is the third result using python's product
8    2    3
9    3    3

与Python解决方案的输出相比:

from itertools import product
c = [1, 2, 3]
n = 2

list(product(c, repeat=n))
[(1, 1),
(1, 2),
(1, 3),
(2, 1),
(2, 2),
(2, 3),
(3, 1),
(3, 2),
(3, 3)]

itertools.product()文档(强调是我的):
嵌套循环会像读数器一样循环,右侧元素在每次迭代中前进。这种模式创建了词典序,因此如果输入的可迭代对象已排序,则按排序顺序发出乘积元组。
与本答案开头所述的内容(即最左边)进行比较。
幸运的是,在R(或任何语言)中生成完全相同的输出相对容易,因为这些只是重复排列。如果您想像python那样构建自己的生成器,则如文档所示算法相对简单(即“大致等效于生成器表达式中的嵌套for-loops”)。
有几个包能够以所需的顺序相当高效地生成这些。它们是gtoolsarrangementsRcppAlgos*
以下是所有三个的代码:
gtools::permutations(3, 2, repeats.allowed = T)

arrangements::permutations(3, 2, replace = T)

RcppAlgos::permuteGeneral(3, 2, T)

作为一个好处,这些解决方案比使用 expand.grid 更高效:
system.time(do.call("expand.grid", rep(list(1:7), 8)))
 user  system elapsed 
0.375   0.007   0.382

system.time(RcppAlgos::permuteGeneral(7, 8, T))
 user  system elapsed 
0.057   0.032   0.088

RcppAlgos::permuteCount(7, 8, T)
[1] 5764801

事实上,它们甚至比python方案更快:

import time

def getTime():
     start = time.time()
     list(product([1, 2, 3, 4, 5, 6, 7], repeat = 8))
     end = time.time()
     print(end - start)

getTime()
0.9604620933532715

公正地说,itertools旨在进行迭代操作,因此具有内存效率,并不是真正意义上的一次性生成所有结果。

*我是RcppAlgos的作者。


2
感谢 @G.Grothendieck 解决了这个问题!
s <- c(1, 2, 3)
n <- 2
do.call("expand.grid", rep(list(s), n))

以下是正确结果的 R 代码。


如果这个回答解决了你的问题,请点赞并接受它作为答案。请参阅当有人回答我的问题时我该怎么做? - G5W
或者更简短的写法:expand.grid(rep(list(s), n)) - ThomasIsCoding

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