Як отримати доступ до останнього значення у векторі?


290

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

Тож я хотів би щось подібне:

dat$vec1$vec2[$#]

замість

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

1
Я аж ніяк не експерт R, але швидкий google виявив це: < stat.ucl.ac.be/ISdidactique/Rhelp/library/pastecs/html/… > Здається, є "остання" функція.
благодійний


1
MATLAB має позначення "мінливий (кінець-k)", де k - ціле число, менше довжини вектора, яке поверне (довжину (мінливий) -k) -й елемент. Це було б добре в Р.
EngrStudent

Відповіді:


368

Я використовую tailфункцію:

tail(vector, n=1)

Приємно, tailщо він працює і на фреймах даних, на відміну від x[length(x)]ідіоми.


5
проте x [length (x [, 1]),] працює на фреймах даних або x [dim (x) [1],]
kpierce8

29
Зауважте, що для кадрів даних довжина (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)
Андреас Сторвік Штрауман

180

Щоб відповісти на це не з естетичної, а орієнтованої на ефективність точки зору, я поставив усі вищезазначені пропозиції через орієнтир . Якщо бути точним, я розглянув пропозиції

  • 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
^ О (1) рішення повинні бути єдиною прийнятною відповіддю у цьому питанні.
Kwame

2
Дякуємо, що позначили всі ці анонси +1!
Сем

1
Я спробував mylastR=function(x) {x[length(x)}це швидше, ніж mylastу Rcpp, але в один раз повільніше, ніж писати x[length(x)]безпосередньо
Endle_Zhenbo

115

Якщо ви шукаєте щось таке приємне, як позначення x [-1] Python, я думаю, вам не пощастило. Стандартна ідіома така

x[length(x)]  

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

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

Ця відсутність функції в Р дратує мене теж!


3
приємна ідея запропонувати приклад функції +1
H.Latte

Зауважте, що якщо вам потрібно останні кілька елементів вектора, а не лише останній елемент, при адаптуванні цього рішення не потрібно робити нічого складного. Векторизації R дозволяє вам робити NEET речі , як отримати останні чотири елементи x, роблячи x[length(x)-0:3].
Дж. Міні

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є те, що ви можете написати це в одному рядку. ;)
mschilli

13

Ще один спосіб - взяти перший елемент зворотного вектора:

rev(dat$vect1$vec2)[1]

7
Це буде дорого, хоча!
Феліпе Джерард

1
Зверніть увагу, що це операція, обчислювальна вартість якої лінійна за довжиною вводу; іншими словами, хоча O (n), це не O (1). Дивіться також мій орієнтир нижче щодо фактичних цифр.
анонімний

@anonymous Якщо ви не використовуєте ітератор
Джеймс

@James Right. Але в цьому випадку ваш код також не працюватиме, чи не так? Якщо під ітератором ви маєте на увазі те, що передбачено пакетом ітераторів, то (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)]що ОП сподівається уникнути. Використовуючи приклад ОП вкладеного вектора, dat$vec1$vec2[NROW(dat$vec1$vec2)]все ще досить безладно.
Грегор Томас

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

2
Примітка. На відміну від цього nrow, NROWтрактується вектор як матриця з 1 стовпцем.
PatrickT

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.