Получение последних n элементов вектора. Есть ли лучший способ, чем использование функции length()?

Если для аргумента я хочу использовать последние пять элементов вектора длиной 10 в Python, я могу использовать оператор "-" в индексе диапазона так:

>>> x = range(10)
>>> x
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> x[-5:]
[5, 6, 7, 8, 9]
>>>

Каков наилучший способ сделать это в R? Есть ли более чистый способ, чем моя текущая техника, которая заключается в использовании функции length()?

> x <- 0:9
> x
 [1] 0 1 2 3 4 5 6 7 8 9
> x[(length(x) - 4):length(x)]
[1] 5 6 7 8 9
> 

Вопрос связан с анализом временных рядов, где часто полезно работать только с последними данными.

Ответ 1

см. ?tail и ?head для некоторых удобных функций:

> x <- 1:10
> tail(x,5)
[1]  6  7  8  9 10

Для аргумента: все, кроме последних пяти элементов, будет:

> head(x,n=-5)
[1] 1 2 3 4 5

Как замечает @Martin Morgan в комментариях, есть еще две возможности, которые быстрее, чем решение хвоста, в случае, если вам придется переносить это миллион раз на вектор из 100 миллионов значений. Для читаемости я бы пошел с хвостом.

test                                        elapsed    relative 
tail(x, 5)                                    38.70     5.724852     
x[length(x) - (4:0)]                           6.76     1.000000     
x[seq.int(to = length(x), length.out = 5)]     7.53     1.113905     

код сравнения:

require(rbenchmark)
x <- 1:1e8
do.call(
  benchmark,
  c(list(
    expression(tail(x,5)),
    expression(x[seq.int(to=length(x), length.out=5)]),
    expression(x[length(x)-(4:0)])
  ),  replications=1e6)
)

Ответ 2

Вы можете сделать то же самое в R с двумя другими символами:

x <- 0:9
x[-5:-1]
[1] 5 6 7 8 9

или

x[-(1:5)]

Ответ 3

Отклонение tail здесь, основываясь только на скорости, на самом деле не подчеркивает, что часть более медленной скорости исходит из того, что хвост более безопасен для работы, если вы не уверены, что длина x будет превышать n, количество элементов, которые вы хотите подмножить:

x <- 1:10
tail(x, 20)
# [1]  1  2  3  4  5  6  7  8  9 10
x[length(x) - (0:19)]
#Error in x[length(x) - (0:19)] : 
#  only 0 may be mixed with negative subscripts

Хвост просто вернет максимальное количество элементов вместо генерации ошибки, поэтому вам не нужно делать какие-либо проверки ошибок самостоятельно. Отличная причина для его использования. Более безопасный код, если дополнительные микросекунды/миллисекунды не имеют большого значения для вас при его использовании.

Ответ 4

Вот функция, чтобы сделать это и кажется достаточно быстрым.

endv<-function(vec,val) 
{
if(val>length(vec))
{
stop("Length of value greater than length of vector")
}else
{
vec[((length(vec)-val)+1):length(vec)]
}
}

ПРИМЕНЕНИЕ:

test<-c(0,1,1,0,0,1,1,NA,1,1)
endv(test,5)
endv(LETTERS,5)

ЭТАЛОН:

                                                    test replications elapsed relative
1                                 expression(tail(x, 5))       100000    5.24    6.469
2 expression(x[seq.int(to = length(x), length.out = 5)])       100000    0.98    1.210
3                       expression(x[length(x) - (4:0)])       100000    0.81    1.000
4                                 expression(endv(x, 5))       100000    1.37    1.691

Ответ 5

Я просто добавляю здесь что-то связанное. Мне нужно было получить доступ к вектору с индексами backend, то есть написать что-то вроде tail(x, i), но вернуть x[length(x) - i + 1], а не весь хвост.

После комментариев я сравнил два решения:

accessRevTail <- function(x, n) {
    tail(x,n)[1]
}

accessRevLen <- function(x, n) {
  x[length(x) - n + 1]
}

microbenchmark::microbenchmark(accessRevLen(1:100, 87), accessRevTail(1:100, 87))
Unit: microseconds
                     expr    min      lq     mean median      uq     max neval
  accessRevLen(1:100, 87)  1.860  2.3775  2.84976  2.803  3.2740   6.755   100
 accessRevTail(1:100, 87) 22.214 23.5295 28.54027 25.112 28.4705 110.833   100

Таким образом, в этом случае оказывается, что даже для малых векторов tail очень медленный по сравнению с прямым доступом

Ответ 6

Как насчет rev(x)[1:5]?

x<-1:10
system.time(replicate(10e6,tail(x,5)))
 user  system elapsed 
 138.85    0.26  139.28 

system.time(replicate(10e6,rev(x)[1:5]))
 user  system elapsed 
 61.97    0.25   62.23