15

我希望能够构造一个do.call子集公式,而不必识别输入数组中每个维度的实际范围。我遇到的问题是我无法弄清楚如何模仿直接函数x[,,1:n,],在其他维度中没有条目意味着“抓住所有元素”。

这是一些示例代码,但失败了。据我所知,[或者用索引do.call替换我的NULL列表值。1

x<-array(1:6,c(2,3))
dimlist<-vector('list', length(dim(x)))
shortdim<-2
dimlist[[shortdim]] <- 1: (dim(x)[shortdim] -1)
flipped <- do.call(`[`,c(list(x),dimlist)) 

我想我可以通过将值分配给 的-2*max(dim(x))每个元素来组合解决方案dimlist,但是很糟糕。
(FWIW,我有替代功能可以通过melt/recast或可怕的“构建一个字符串然后”来完成所需的工作eval(parse(mystring)),但我想“做得更好”。)

编辑:顺便说一句,我针对使用的函数运行了这个代码的一个版本(相当于 DWin 的 TRUE 设置)melt & acast;后者慢了好几倍,这不足为奇。

4

5 回答 5

14

经过一番摸索,alist似乎可以解决问题:

x <- matrix(1:6, nrow=3)
x
     [,1] [,2]
[1,]    1    4
[2,]    2    5
[3,]    3    6

# 1st row
do.call(`[`, alist(x, 1, ))
[1] 1 4

# 2nd column
do.call(`[`, alist(x, , 2))
[1] 4 5 6

来自?alist

'alist' 处理它的参数,就好像它们描述了函数参数一样。所以这些值不会被评估,并且没有值的标记参数是允许的,而“列表”只是忽略它们。“alist”最常与“formals”一起使用。


一种动态选择提取哪个维度的方法。要创建alist所需长度的首字母,请参见此处(Hadley,使用bquote)或此处(使用alist)。

m <- array(1:24, c(2,3,4))
ndims <- 3
a <- rep(alist(,)[1], ndims)
for(i in seq_len(ndims))
{
    slice <- a
    slice[[i]] <- 1
    print(do.call(`[`, c(list(m), slice)))
}

     [,1] [,2] [,3] [,4]
[1,]    1    7   13   19
[2,]    3    9   15   21
[3,]    5   11   17   23

     [,1] [,2] [,3] [,4]
[1,]    1    7   13   19
[2,]    2    8   14   20

     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6
于 2013-07-19T16:34:18.683 回答
11

在这种情况下,我一直用作TRUE占位符:

> x
     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6
> do.call("[", list(x, TRUE,1))
[1] 1 2

让我们使用一个更复杂的x例子:x <- array(1:36, c(2,9,2),那么如果希望将向量替换为下标列表,该列表将恢复所有第一维和第二维以及第三维的第二个“切片”:

shortdim <- 3
short.idx <- 2
dlist <- rep(TRUE, length(dim(x)) )
dlist <- as.list(rep(TRUE, length(dim(x)) ))

> dlist
[[1]]
[1] TRUE

[[2]]
[1] TRUE

[[3]]
[1] TRUE

> dlist[shortdim] <- 2
> do.call("[", c(list(x), dlist) )
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
[1,]   19   21   23   25   27   29   31   33   35
[2,]   20   22   24   26   28   30   32   34   36

有时有用的另一点是逻辑索引被回收,因此您可以使用 c(TRUE,FALSE) 来挑选其他所有项目:

(x<-array(1:36, c(2,9,2)))
, , 1

     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
[1,]    1    3    5    7    9   11   13   15   17
[2,]    2    4    6    8   10   12   14   16   18

, , 2

     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
[1,]   19   21   23   25   27   29   31   33   35
[2,]   20   22   24   26   28   30   32   34   36

> x[TRUE,c(TRUE,FALSE), TRUE]
, , 1

     [,1] [,2] [,3] [,4] [,5]
[1,]    1    5    9   13   17
[2,]    2    6   10   14   18

, , 2

     [,1] [,2] [,3] [,4] [,5]
[1,]   19   23   27   31   35
[2,]   20   24   28   32   36

并且每个其他项目的进一步变化是可能的。尝试 c(FALSE, FALSE, TRUE) 以获取从 item-3 开始的每第三个项目。

于 2013-07-19T16:35:48.027 回答
9

不是一个直接的答案,但我将演示asub作为替代方案,因为我很确定这是 OP 最终所追求的。

library(abind)

提取第一行:

asub(x, idx = list(1), dims = 1)

提取第二列和第三列:

asub(x, idx = list(2:3), dims = 2)

shortdim按照 OP 的要求从维度中删除最后一项:

asub(x, idx = list(1:(dim(x)[shortdim]-1)), dims = shortdim)

您也可以使用负索引,这样也可以:

asub(x, idx = list(-dim(x)[shortdim]), dims = shortdim)

最后,我会提到该函数有一个drop选项,就像 dos 一样[

于 2013-07-19T17:23:01.317 回答
1

好的,这是四个版本的代码,后面是microbenchmark. 所有这些的速度似乎几乎相同。我想检查所有答案是否已接受,但由于我不能,这里是使用的 chintzy 标准:DWin 失败,因为您必须为占位符输入“TRUE”。
flodel 输了,因为它需要一个非基础库 我原来的输了,当然,因为eval(parse()). 所以Hong Ooi赢了。他晋级下一轮谁想成为切碎的偶像:-)

flip1<-function(x,flipdim=1) {
    if (flipdim > length(dim(x))) stop("Dimension selected exceeds dim of input")
    a <-"x["
    b<-paste("dim(x)[",flipdim,"]:1",collapse="")
    d <-"]"
    #now the trick: get the right number of commas
    lead<-paste(rep(',',(flipdim-1)),collapse="")
    follow <-paste(rep(',',(length(dim(x))-flipdim)),collapse="")
    thestr<-paste(a,lead,b,follow,d,collapse="")
    flipped<-eval(parse(text=thestr))
    return(invisible(flipped))
    }       

flip2<-function(x,flipdim=1) {
    if (flipdim > length(dim(x))) stop("Dimension selected exceeds dim of input")
    dimlist<-vector('list', length(dim(x))  )  
    dimlist[]<-TRUE  #placeholder to make do.call happy 
    dimlist[[flipdim]] <- dim(x)[flipdim]:1 
    flipped <- do.call(`[`,c(list(x),dimlist) )
    return(invisible(flipped))
    }       

# and another...
flip3 <- function(x,flipdim=1) {
    if (flipdim > length(dim(x))) stop("Dimension selected exceeds dim of input")
    flipped <- asub(x, idx = list(dim(x)[flipdim]:1), dims = flipdim)
    return(invisible(flipped))
}

#and finally, 
flip4 <- function(x,flipdim=1) {
    if (flipdim > length(dim(x))) stop("Dimension selected exceeds dim of input")
    dimlist <- rep(list(bquote()), length(dim(x)))
    dimlist[[flipdim]] <- dim(x)[flipdim]:1
    flipped<- do.call(`[`, c(list(x), dimlist))
    return(invisible(flipped))
}

Rgames> foo<-array(1:1e6,c(100,100,100))
Rgames> microbenchmark(flip1(foo),flip2(foo),flip3(foo),flip4(foo)


   Unit: milliseconds
       expr      min       lq   median       uq      max neval
 flip1(foo) 18.40221 18.47759 18.55974 18.67384 35.65597   100
 flip2(foo) 21.32266 21.53074 21.76426 31.56631 76.87494   100
 flip3(foo) 18.13689 18.18972 18.22697 18.28618 30.21792   100
 flip4(foo) 21.17689 21.57282 21.73175 28.41672 81.60040   100
于 2013-07-19T21:47:35.160 回答
0

您可以使用substitute()来获取空参数。这可以包含在正常列表中。

然后,要以编程方式生成可变数量的空参数,只需rep()

n <- 4
rep(list(substitute()), n)
于 2015-09-27T10:31:41.740 回答