Как передать "ничего" в качестве аргумента для `[` для подмножества?
Я надеялся построить конструкцию 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)