Получение последних 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