如何将“nothing”作为参数传递给`[`for subsetting?

时间:2013-07-19 16:19:20

标签: r subset do.call

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

这是一些失败的示例代码。据我所知,[do.callNULL列表值替换为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))完成所需的工作,但我想做得更好”。)

编辑:顺便说一句,我对使用melt & acast的函数运行了此代码的一个版本(相当于DWin的TRUE设置);后者慢了好几倍,没有真正意外。

5 个答案:

答案 0 :(得分: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'处理其参数,就好像它们描述了函数一样    参数。因此,不评估值和标记参数    允许没有值,而'list'只是忽略它们。    'alist'最常用于'formals'。

<小时/> 一种动态选择提取哪个维度的方法。要创建所需长度的初始alist,请参阅here(Hadley,使用bquote)或here(使用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

答案 1 :(得分: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)来获取以第3项开头的每个第三项。

答案 2 :(得分: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选项,就像[一样。

答案 3 :(得分: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

答案 4 :(得分:0)

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

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

n <- 4
rep(list(substitute()), n)