Как получить доступ к последнему значению в векторе?


290

Предположим, у меня есть вектор, который вложен в фрейм данных одного или двух уровней. Есть ли быстрый и грязный способ доступа к последнему значению без использования length()функции? Что-то $#особенное вар PERL ?

Поэтому я хотел бы что-то вроде:

dat$vec1$vec2[$#]

вместо того

dat$vec1$vec2[length(dat$vec1$vec2)]

1
Я ни в коем случае не эксперт по R, но быстрый гугл обнаружил это: < stat.ucl.ac.be/ISdidactique/Rhelp/library/pastecs/html/… > Кажется, есть «последняя» функция.
благоприятно


1
MATLAB имеет обозначение «myvariable (end-k)», где k - целое число, меньшее длины вектора, который будет возвращать (length (myvariable) -k) -й элемент. Это было бы хорошо иметь в R.
EngrStudent

Ответы:


369

Я использую tailфункцию:

tail(vector, n=1)

Приятно то, tailчто в отличие от x[length(x)]идиомы , он работает и на данных .


5
однако x [length (x [, 1]),] работает на фреймах данных или x [dim (x) [1],]
kpierce8

29
Обратите внимание, что для фреймов данных length (x) == ncol (x), так что это определенно неверно, и dim (x) [1] можно описать более наглядно nrow (x).
Хэдли

2
@hadley - предложение kpierce8 не x[length(x[,1]),]является неправильным (обратите внимание на запятую в xподмножестве), но это, конечно, неудобно.
Jbaums

4
Обратите внимание, что мой тест ниже показывает, что это медленнее, чем x[length(x)]в среднем в 30 раз для больших векторов!
Аноним

1
Не работает, если вы хотите добавить материал из векторов, хотяtail(vector, n=1)-tail(vector, n=2)
Андреас Storvik Strauman

182

Чтобы ответить на этот вопрос не с эстетической, а с точки зрения производительности, я поставил все вышеперечисленные предложения через эталон . Чтобы быть точным, я рассмотрел предложения

  • 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)] .


11
^ O (1) решения должны быть единственным приемлемым ответом в этом вопросе.
Кваме

2
Спасибо за выбор времени всех тех, кто скоро +1!
Сэм

1
Я попробовал mylastR=function(x) {x[length(x)}это быстрее, чем mylastв Rcpp, но в один раз медленнее, чем писать x[length(x)]напрямую
Endle_Zhenbo

115

Если вы ищете что-то хорошее, чем нотация Python x [-1], я думаю, вам не повезло. Стандартная идиома

x[length(x)]  

но достаточно просто написать функцию для этого:

last <- function(x) { return( x[length(x)] ) }

Эта недостающая особенность в R раздражает меня тоже!


3
хорошая идея предложить пример функции +1
H.Latte

Обратите внимание, что если вам нужны последние несколько элементов вектора, а не только последний элемент, нет необходимости делать что-либо сложное при адаптации этого решения. Векторизации R позволяет вам делать NEET вещи , как получить последние четыре элемента x, делая x[length(x)-0:3].
J. Mini

46

Объединяя идеи Линделофа и Грегга Линда :

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.)


7
Обратите внимание, что мой тест ниже показывает, что это медленнее, чем x[length(x)]в среднем в 30 раз для больших векторов!
Аноним

19

Пакет dplyr включает в себя функцию last():

last(mtcars$mpg)
# [1] 21.4

4
Это в основном сводится к x[[length(x)]]снова.
Rich Scriven

6
Аналогично, но с этим ответом вам не нужно писать свою собственную функцию last()и хранить эту функцию где-нибудь, как это делали несколько человек выше. Вы получаете улучшенную читаемость функции, благодаря переносимости ее из CRAN, так что кто-то другой может запустить код.
Сэм Фирке

1
Также можете написать как mtcars$mpg %>% last, в зависимости от ваших предпочтений.
Кит Хьюджит

1
@RichScriven К сожалению, это значительно медленнее, чем x[[length(x)]]!
анонимно

18

Я только что сравнил эти два подхода для фрейма данных с 663 552 строками, используя следующий код:

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 

Итак, если вы работаете с векторами, доступ к позиции длины будет значительно быстрее.


3
Почему не тестирование tail(strsplit(x,".",fixed=T)[[1]],1)для 2-го случая? Для меня главное преимущество в том tail, что вы можете написать это в одну строку. ;)
Мщилли

13

Другой способ - взять первый элемент обратного вектора:

rev(dat$vect1$vec2)[1]

7
Это будет дорого, хотя!
Фелипе Джерард

1
Обратите внимание, что это операция, вычислительная стоимость которой линейна по длине ввода; другими словами, хотя O (n), это не O (1). См. Также мой тест ниже для фактических чисел.
Аноним

@anonymous Если вы не используете итератор
Джеймс

@ Джеймс Верно. Но в этом случае ваш код также не будет работать, не так ли? Если под итератором вы имеете в виду то, что предоставляется пакетом итераторов, то (1) вы не можете использовать [1]для доступа к первому элементу и (2) в то время как вы можете обратиться revк итератору, он не работает должным образом: он просто обрабатывает объект итератора как список его членов и полностью изменяет это.
анонимный


10

У меня есть другой метод для нахождения последнего элемента в векторе. Скажи, что вектор есть 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

Вот и вы!


8

Что насчет

> a <- c(1:100,555)
> a[NROW(a)]
[1] 555

1
Я ценю, что это NROWделает то, что вы ожидаете от множества различных типов данных, но по сути это то же самое, a[length(a)]что OP надеется избежать. Использование примера OP для вложенного вектора dat$vec1$vec2[NROW(dat$vec1$vec2)]все еще довольно грязно.
Грегор Томас

1
может быть записано какnrow
Франк Дернонкур

2
Примечание. В отличие от этого nrow, NROWвектор обрабатывается как матрица из 1 столбца.
PatrickT

3

Пакет xts предоставляет lastфункцию:

library(xts)
a <- 1:100
last(a)
[1] 100
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.