在Mathematica中,我可以使用:
制作数组Table[f(x1, x2, ..., xn), {x1, v1}, {x2, v2}, ..., {xn, vn}]
f
是任何函数,xi
是变量,相应的vi
是变量所采用的值的向量。
如果vi
的长度为Li
,则数组为L1xL2x...xLn
。
这个功能在R中是否具有某些内置功能,或者我是否必须编写自己的程序才能执行此操作?
这是我编写的一个功能,可以完成我想要的功能,但有没有内置功能可以做到这一点?
# Function definition
xapply <- function(fun,vecs){
n <- length(vecs)
dims <- sapply(vecs,length)
prods <- c(1,cumprod(dims))
ivecs <- vector(mode="list",length=n)
for(i in seq(n)){
ivecs[[i]] <- rep(rep(vecs[[i]],each=prods[i]),prods[n+1]/prods[i+1])
}
my.args <- c(fun,ivecs)
return(array(do.call(mapply,my.args),dims))
}
# Example
v1=c(1,2,3,4)
v2=c(10,20,30,40,50)
v3=c(100,200,300)
v123=list(v1,v2,v3)
my.array1 <- xapply(function(x,y,z) x+y+z,v123)
my.array1
答案 0 :(得分:1)
以下是我将如何执行此操作,使用lambda和向量列表v123
作为输入进行演示:
array(do.call(function(x,y,z) x+y+z,unname(do.call(expand.grid,v123))),lapply(v123,length));
## , , 1
##
## [,1] [,2] [,3] [,4] [,5]
## [1,] 111 121 131 141 151
## [2,] 112 122 132 142 152
## [3,] 113 123 133 143 153
## [4,] 114 124 134 144 154
##
## , , 2
##
## [,1] [,2] [,3] [,4] [,5]
## [1,] 211 221 231 241 251
## [2,] 212 222 232 242 252
## [3,] 213 223 233 243 253
## [4,] 214 224 234 244 254
##
## , , 3
##
## [,1] [,2] [,3] [,4] [,5]
## [1,] 311 321 331 341 351
## [2,] 312 322 332 342 352
## [3,] 313 323 333 343 353
## [4,] 314 324 334 344 354
##
identical(.Last.value,my.array1);
## [1] TRUE
此行优于基于apply()
- 或Map()
的解决方案的优势在于它是矢量化的。只有一次调用该函数。
首先,expand.grid()
通过do.call()
调用,矢量列表作为参数列表。这将生成一个data.frame,其中包含输入向量的所有可能组合,每行一个。然后,我们必须通过调用unname()
来删除data.frame中自动生成的列名。
然后我们可以将生成的未命名data.frame作为参数列表传递给lambda,再次通过do.call()
调用。这是因为data.frames是内部列表,每列有一个列表组件,因此data.frame适合用作do.call()
期望的参数列表。这也澄清了为什么需要unname()
电话;否则将使用命名参数(自动生成的名称为Var1
,Var2
,Var3
)调用lambda,这些参数将无法绑定到参数(x
,{ {1}},y
)。
矢量化lambda调用的结果是原子矢量。由于你需要一个数组,我们必须将它包装在array()
的调用中,并通过lapply()
从输入向量的长度传递适当的维数。
此外,您可能需要查看outer()
函数,该函数在内部执行此类笛卡尔展开式,矢量化调用和维度包装,但一次只能用于两个输入对象。
答案 1 :(得分:1)
我会使用rowSums(它占用你的函数的位置)并在expand.grid的结果上使用它来进行全组合扩展:
> array( rowSums( expand.grid(v123) ) , c(4,5,3) )
, , 1
[,1] [,2] [,3] [,4] [,5]
[1,] 111 121 131 141 151
[2,] 112 122 132 142 152
[3,] 113 123 133 143 153
[4,] 114 124 134 144 154
, , 2
[,1] [,2] [,3] [,4] [,5]
[1,] 211 221 231 241 251
[2,] 212 222 232 242 252
[3,] 213 223 233 243 253
[4,] 214 224 234 244 254
, , 3
[,1] [,2] [,3] [,4] [,5]
[1,] 311 321 331 341 351
[2,] 312 322 332 342 352
[3,] 313 323 333 343 353
[4,] 314 324 334 344 354
如果函数不是逐行操作,则可以使用较少的矢量化解决方案。 mapply
或Reduce
函数可以让您获得相同的向量:
> array( Reduce("+", expand.grid(v123) ) , c(4,5,3) )
, , 1
[,1] [,2] [,3] [,4] [,5]
[1,] 111 121 131 141 151
[2,] 112 122 132 142 152
[3,] 113 123 133 143 153
[4,] 114 124 134 144 154
, , 2
[,1] [,2] [,3] [,4] [,5]
[1,] 211 221 231 241 251
[2,] 212 222 232 242 252
[3,] 213 223 233 243 253
[4,] 214 224 234 244 254
, , 3
[,1] [,2] [,3] [,4] [,5]
[1,] 311 321 331 341 351
[2,] 312 322 332 342 352
[3,] 313 323 333 343 353
[4,] 314 324 334 344 354
答案 2 :(得分:0)
如果您的目标是将函数应用于v1 ... vn的每个组合,则可以使用expand.grid
和apply
:
x1 <- 1:5
x2 <- 1:10
x3 <- 8:10
# get the average
apply(expand.grid(x1,x2,x7), 1, FUN=mean)
# get the sum of squares
apply(expand.grid(x1,x2,x7), 1, FUN=function(i) sum(i^2))
如果您的v1,...,vn等长并且您想将某些功能应用于相应的条目,则可以使用Map
(或mapply
)
x1 <- 1:5
x2 <- 2:6
x3 <- 6:10
# get the sum of squares
Map(f=function(x,y,z) sum(x^2 + y^2 + z^2), x1, x2, x3)
答案 3 :(得分:0)
这是我想出如何使用expand.grid()创建数组的另一种方式,但它的格式与我原来的问题略有不同:(1)我是单独输入向量而不是列表,(2)我使用变量x [1],x [2]等来定义函数,(3)我假设我知道向量的长度。我仍然希望outer()只使用任意数量的数组作为参数,因为该函数完全符合我的要求,但仅适用于两个数组。
array(apply(expand.grid(1:4,4:8,3:5), 1, function(x) 100*x[1]+10*x[2]+x[3]),c(4,5,3))