Предположим, у меня есть вектор, вложенный в один или два уровня фрейма данных. Есть ли быстрый и грязный способ получить доступ к последнему значению без использования функции length()
? Что-то аля PERL's $#
special var?
Итак, я бы хотел что-то вроде:
dat$vec1$vec2[$#]
вместо
dat$vec1$vec2[length(dat$vec1$vec2)]
MATLAB имеет обозначение «myvariable (end-k)», где k - целое число, меньшее, чем длина вектора, который вернет элемент (length (myvariable) -k) th. Было бы неплохо, если бы в R.
Я ни в коем случае не эксперт по R, но быстрый гугл обнаружил следующее: <stat.ucl.ac.be/ISdidactique/Rhelp/library/pastecs/html/…> Кажется, есть "последняя" функция.
Если вы ищете что-то столь же красивое, как нотация x [-1] Python, я думаю, вам не повезло. Стандартная идиома
x[length(x)]
но достаточно просто написать функцию для этого:
last <- function(x) { return( x[length(x)] ) }
Эта отсутствующая функция в R меня тоже раздражает!
хорошая идея предложить пример функции +1
Обратите внимание: если вам нужны последние несколько элементов вектора, а не только последний элемент, нет необходимости делать что-либо сложное при адаптации этого решения. Векторизация R позволяет вам делать новые вещи, например, получить последние четыре элемента x
, выполнив x[length(x)-0:3]
.
Использую функцию tail
:
tail(vector, n=1)
Приятная вещь с tail
заключается в том, что он также работает с фреймами данных, в отличие от идиомы x[length(x)]
.
однако x [length (x [, 1]),] работает с фреймами данных или x [dim (x) [1],]
Обратите внимание, что для фреймов данных length (x) == ncol (x), что определенно неверно, а dim (x) [1] более описательно можно записать как nrow (x).
@hadley - предложение kpierce8 о x[length(x[,1]),]
не является неправильным (обратите внимание на запятую в подмножестве x
), но это определенно неудобно.
Обратите внимание, что мой тест ниже показывает, что это медленнее, чем x[length(x)]
, в среднем в 30 раз для больших векторов!
Не работает, если вы хотите добавить что-то из векторов, хотя tail(vector, n=1)-tail(vector, n=2)
Объединение идей Линделофа и Грегга Линда:
last <- function(x) { tail(x, n = 1) }
Работая в командной строке, я обычно опускаю n=
, то есть tail(x, 1)
.
В отличие от last
из пакета pastecs
, head
и tail
(из utils
) работают не только с векторами, но и с кадрами данных и т. д., А также могут возвращать данные "без первых / последних n элементов", например
but.last <- function(x) { head(x, n = -1) }
(Обратите внимание, что для этого необходимо использовать head
вместо tail
.)
Обратите внимание, что мой тест ниже показывает, что это медленнее, чем x[length(x)]
, в среднем в 30 раз для больших векторов!
Другой способ - взять первый элемент перевернутого вектора:
rev(dat$vect1$vec2)[1]
Хотя это будет дорогой!
Обратите внимание, что это операция, вычислительные затраты которой линейны по длине ввода; другими словами, пока O (n), это не O (1). См. Также мой тест ниже для реальных цифр.
@anonymous Если вы не используете итератор
@ Джеймс Верно. Но в этом случае ваш код тоже не будет работать, не так ли? Если под итератором вы имеете в виду то, что предоставляется пакетом итераторов, то (1) вы не можете использовать [1]
для доступа к первому элементу и (2), хотя вы можете применить rev
к итератору, он ведет себя не так, как ожидалось: он просто обрабатывает итератор как список его членов, и меняет его местами.
Я только что протестировал эти два подхода на фрейме данных с 663552 строками, используя следующий код:
system.time(
resultsByLevel$subject <- sapply(resultsByLevel$variable, function(x) {
s <- strsplit(x, ".", fixed=TRUE)[[1]]
s[length(s)]
})
)
user system elapsed
3.722 0.000 3.594
и
system.time(
resultsByLevel$subject <- sapply(resultsByLevel$variable, function(x) {
s <- strsplit(x, ".", fixed=TRUE)[[1]]
tail(s, n=1)
})
)
user system elapsed
28.174 0.000 27.662
Итак, если вы работаете с векторами, доступ к позиции длины происходит значительно быстрее.
Почему бы не протестировать tail(strsplit(x,".",fixed=T)[[1]],1)
для второго случая? Для меня главное преимущество tail
в том, что вы можете написать его в одну строку. ;)
У меня есть другой способ найти последний элемент в векторе.
Скажем, вектор - a
.
> a<-c(1:100,555)
> end(a) #Gives indices of last and first positions
[1] 101 1
> a[end(a)[1]] #Gives last element in a vector
[1] 555
Вот так!
Как насчет
> a <- c(1:100,555)
> a[NROW(a)]
[1] 555
Я ценю, что NROW
делает то, что вы ожидаете от множества различных типов данных, но по сути это то же самое, что и a[length(a)]
, которого OP надеется избежать. Используя пример вложенного вектора OP, dat$vec1$vec2[NROW(dat$vec1$vec2)]
все еще довольно запутан.
можно записать как nrow
Примечание. В отличие от nrow
, NROW
обрабатывает вектор как матрицу с одним столбцом.
Чтобы ответить на этот вопрос не с эстетической, а с точки зрения производительности, я поместил все вышеперечисленные предложения через ориентир. Если быть точным, я учел предложения
x[length(x)]
mylast(x)
, где mylast
- это функция C++, реализованная через Rcpp,tail(x, n=1)
dplyr::last(x)
x[end(x)[1]]]
rev(x)[1]
и применил их к случайным векторам разного размера (10 ^ 3, 10 ^ 4, 10 ^ 5, 10 ^ 6 и 10 ^ 7). Прежде чем мы посмотрим на цифры, я думаю, должно быть ясно, что все, что становится заметно медленнее при увеличении размера ввода (то есть все, что не O (1)), недопустимо. Вот код, который я использовал:
Rcpp::cppFunction('double mylast(NumericVector x) { int n = x.size(); return x[n-1]; }')
options(width=100)
for (n in c(1e3,1e4,1e5,1e6,1e7)) {
x <- runif (n);
print(microbenchmark::microbenchmark(x[length(x)],
mylast(x),
tail(x, n=1),
dplyr::last(x),
x[end(x)[1]],
rev(x)[1]))}
Это дает мне
Unit: nanoseconds
expr min lq mean median uq max neval
x[length(x)] 171 291.5 388.91 337.5 390.0 3233 100
mylast(x) 1291 1832.0 2329.11 2063.0 2276.0 19053 100
tail(x, n = 1) 7718 9589.5 11236.27 10683.0 12149.0 32711 100
dplyr::last(x) 16341 19049.5 22080.23 21673.0 23485.5 70047 100
x[end(x)[1]] 7688 10434.0 13288.05 11889.5 13166.5 78536 100
rev(x)[1] 7829 8951.5 10995.59 9883.0 10890.0 45763 100
Unit: nanoseconds
expr min lq mean median uq max neval
x[length(x)] 204 323.0 475.76 386.5 459.5 6029 100
mylast(x) 1469 2102.5 2708.50 2462.0 2995.0 9723 100
tail(x, n = 1) 7671 9504.5 12470.82 10986.5 12748.0 62320 100
dplyr::last(x) 15703 19933.5 26352.66 22469.5 25356.5 126314 100
x[end(x)[1]] 13766 18800.5 27137.17 21677.5 26207.5 95982 100
rev(x)[1] 52785 58624.0 78640.93 60213.0 72778.0 851113 100
Unit: nanoseconds
expr min lq mean median uq max neval
x[length(x)] 214 346.0 583.40 529.5 720.0 1512 100
mylast(x) 1393 2126.0 4872.60 4905.5 7338.0 9806 100
tail(x, n = 1) 8343 10384.0 19558.05 18121.0 25417.0 69608 100
dplyr::last(x) 16065 22960.0 36671.13 37212.0 48071.5 75946 100
x[end(x)[1]] 360176 404965.5 432528.84 424798.0 450996.0 710501 100
rev(x)[1] 1060547 1140149.0 1189297.38 1180997.5 1225849.0 1383479 100
Unit: nanoseconds
expr min lq mean median uq max neval
x[length(x)] 327 584.0 1150.75 996.5 1652.5 3974 100
mylast(x) 2060 3128.5 7541.51 8899.0 9958.0 16175 100
tail(x, n = 1) 10484 16936.0 30250.11 34030.0 39355.0 52689 100
dplyr::last(x) 19133 47444.5 55280.09 61205.5 66312.5 105851 100
x[end(x)[1]] 1110956 2298408.0 3670360.45 2334753.0 4475915.0 19235341 100
rev(x)[1] 6536063 7969103.0 11004418.46 9973664.5 12340089.5 28447454 100
Unit: nanoseconds
expr min lq mean median uq max neval
x[length(x)] 327 722.0 1644.16 1133.5 2055.5 13724 100
mylast(x) 1962 3727.5 9578.21 9951.5 12887.5 41773 100
tail(x, n = 1) 9829 21038.0 36623.67 43710.0 48883.0 66289 100
dplyr::last(x) 21832 35269.0 60523.40 63726.0 75539.5 200064 100
x[end(x)[1]] 21008128 23004594.5 37356132.43 30006737.0 47839917.0 105430564 100
rev(x)[1] 74317382 92985054.0 108618154.55 102328667.5 112443834.0 187925942 100
Это немедленно исключает все, что связано с rev
или end
, поскольку они явно не O(1)
(и полученные выражения вычисляются неленивым образом). tail
и dplyr::last
не далеки от O(1)
, но они также значительно медленнее, чем mylast(x)
и x[length(x)]
. Поскольку mylast(x)
медленнее x[length(x)]
и не дает никаких преимуществ (скорее, он настраивается и не обрабатывает пустой вектор изящно), я думаю, что ответ очевиден: Пожалуйста, используйте x[length(x)]
.
^ O (1) решения должны быть единственным приемлемым ответом на этот вопрос.
Спасибо, что выбрали время для анонса +1!
Я пробовал mylastR=function(x) {x[length(x)}
. Он быстрее, чем mylast
в Rcpp, но один раз медленнее, чем напрямую писать x[length(x)]
.
Пакет data.table
включает функцию last
library(data.table)
last(c(1:10))
# [1] 10
В основном это снова сводится к x[[length(x)]]
.
Пакет dplyr включает функцию last()
:
last(mtcars$mpg)
# [1] 21.4
В основном это снова сводится к x[[length(x)]]
.
Аналогично под капотом, но с этим ответом вам не нужно писать свою собственную функцию last()
и где-то хранить эту функцию, как это сделали несколько человек выше. Вы получаете улучшенную читаемость функции с переносимостью, исходящей от CRAN, так что кто-то другой может запустить код.
Также можно писать как mtcars$mpg %>% last
, в зависимости от ваших предпочтений.
@RichScriven К сожалению, он значительно медленнее, чем x[[length(x)]]
!
Пакет xts предоставляет функцию last
:
library(xts)
a <- 1:100
last(a)
[1] 100
Связанный: stackoverflow.com/q/6136613/946850