如何向`[`函数传递“nothing”作为子集参数?

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)) 

我想我可以通过将dimlist的每个元素赋值为-2*max(dim(x))来将一个解决方案捏合在一起,但哎呀。
(顺便说一下,我有其他函数可以通过melt/recast或可怕的“构建一个字符串然后eval(parse(mystring))”来完成所需的工作,但我希望做得“更好”)

编辑:顺带一提,我对一个使用了melt&acast的函数(相当于DWin的TRUE设置)运行了这段代码的版本,后者比前者慢了几倍,这并不奇怪。


1
需要定义flipdim。在创建后,dimList是一个空列表,因为我的工作区中没有x对象。现在有了,不清楚您是否真的希望dimlist有6个项目长度。 - IRTFM
懒惰求值警告:(dimlist<-vector('list', length(dim(x))) 会抛出一个错误。 - IRTFM
我想我知道怎么做。我正在等待一个有意义的示例来进行工作。 - IRTFM
@DWin 对我起作用了。但是我很抱歉没有按正确的顺序放置我的语句。现在已经修复了。 - Carl Witthoft
@JoshuaUlrich 感谢您找到那个链接。所以我想答案是无论如何都不能将“nothing”放入调用中。 - Carl Witthoft
显示剩余3条评论
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’处理其参数,就好像它们描述函数参数一样。因此,值不会被评估,并且带有没有值的标记参数是允许的,而‘list’则只是忽略它们。‘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

@flodel,我不是很理解动态子集维度的部分(尤其是问题中的代码无法运行),但您可以在调用do.call时使用alist代替list。因此,如果OP有关模拟[,,n]的评论相关,则应该也适用于该方法。 - Hong Ooi
@flodel 我认为这个答案回答了问题。重点是生成一个对 '[' 的调用,而且 OP 希望有空参数。 - Gavin Simpson
@GavinSimpson 好的,我会做的。 - Hong Ooi
@flodel 如果可以形成一个动态长度的alist,那么它就有可能实现。一旦列表被形成(由空参数加上x),您可以像任何其他列表一样对其进行索引,并将alist[[shortdim]]设置为您想要的值。目前似乎很难创建一个动态的alist,但我已经提出了一个问题,正是关于这个的。 - Gavin Simpson
@flodel,您也可以使用alist来完成此操作。问题实际上是您必须使用eval(parse)创建初始列表。 - Hong Ooi
显示剩余6条评论

12

我一直将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)获取从第三项开始的每三个项目。


在我看来,这将解决OP的问题,但您应该通过使用shortdim更清楚地表达。 - flodel
1
如果可以的话,我会这么做。我无法理解shortdim的含义,并要求对我认为有误的示例进行澄清。 - IRTFM
据我理解,shortdim是OP想要子集的维度。因此,除了那里,您的列表应该在其他地方都为TRUE - flodel
1
由于似乎没有一种简单的方法来创建一个可变长度的alist(),因此使用带有elements TRUElist()似乎是响应动态shortdim的最简单的方法。+1 - Gavin Simpson
@flodel。感谢您的澄清。希望代码很清晰。 - IRTFM
@flodel是正确的。我希望我在原始帖子中捕捉到了所有错误。使用TRUE至少比使用“-2 * max(dim(x))”作为默认占位符更令人愉悦。 - Carl Witthoft

9

虽然不是直接的答案,但我会演示asub作为另一种选择,因为我相信这最终是OP想要的。

library(abind)

提取第一行:

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

提取第二列和第三列:

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

从维度 shortdim 中删除最后一个项,如所需:
asub(x, idx = list(1:(dim(x)[shortdim]-1)), dims = shortdim)

您也可以使用负索引,因此这也可以工作:
asub(x, idx = list(-dim(x)[shortdim]), dims = shortdim)

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

有趣。我想我应该坦白,我的最初目标是创建一个“翻转”函数,可以沿着指定的维度反转数组。看起来asub可以很好地完成这个任务。 - Carl Witthoft
那么也许你的意思是 idx = list(rev(seq_len(dim(x)[shortdim]))) - flodel
是的,这正是我要做的。但我很感谢您的解决方案,因为它适用于各种类型的数组操作。如果最终我选择DWin的解决方案,那是因为它解决了我的"[ ]"问题,尽管您的解决方案对我的根本问题也是一个很好的解决方案。也许我会通过一次时间试验来选择优胜者 :-) - Carl Witthoft
如果 asub 更慢,这并不让我感到惊讶,因为它会执行各种检查并返回有意义的错误消息(请参见 getAnywhere("asub.default"))。将 asub 视为您尝试以编程方式构建的 [ 调用的稳健、经过测试和广泛批准的实现。 - flodel
然而,从代码来看,似乎asub正在构建一个带有缺失参数的[调用,如x [,2,],这应该比x [TRUE,2,TRUE]对于大型输入数组要快得多。 - flodel

1

好的,这是四个版本的代码,后面跟着 microbenchmark。所有版本的速度似乎都差不多。我想将所有答案都标记为已接受,但由于无法实现,因此这里使用了一些简单的标准: 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

0

你可以使用 substitute() 来获取一个空参数,然后将其包含在普通列表中。

接着,为了以编程方式生成可变数量的空参数,只需对其应用 rep():

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

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