Как передать "ничего" в качестве аргумента для `[` для подмножества?

Я надеялся построить конструкцию 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, но yuck.
(FWIW, у меня есть альтернативные функции, которые выполняют желаемую работу либо через melt/recast, либо страшно "строят строку, а затем eval(parse(mystring)), но я хотел сделать это" лучше. ")

Изменить: как в сторону, я запускал версию этого кода (с эквивалентом установки DWin TRUE) против функции, которая использовала melt & acast; последний был в несколько раз медленнее, чем настоящий сюрприз.

Ответы

Ответ 1

После того, как кто-то трясет, 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 чаще всего используется в сочетании с формами.


Способ динамического выбора размера. Чтобы создать начальную alist нужной длины, см. здесь (Хэдли, используя 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

Ответ 2

Я всегда использовал 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.

Ответ 3

Не прямой ответ, но я продемонстрирую 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, как это делает [.

Ответ 4

Итак, вот код для четырех версий, за которым следует microbenchmark. Скорость, похоже, почти одинакова для всех этих. Я хотел бы проверить все ответы как принятые, но так как я не могу, вот ключевые критерии:  DWin проигрывает, потому что вам нужно ввести "TRUE" для заполнителей.
flodel проигрывает, потому что для него требуется не-базовая библиотека Мои оригиналы проигрывают, конечно, из-за eval(parse()). Так побеждает Хонг Оой. Он продвигается к следующему раунду "Кто хочет быть изрубленным идолом: -)

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

Ответ 5

Вы можете использовать substitute() для получения пустого аргумента. Это может быть включено в обычный список.

Затем, чтобы программно сгенерировать переменное число пустых аргументов, просто rep() it:

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