Jak uzyskać dostęp do ostatniej wartości w wektorze?


290

Załóżmy, że mam wektor zagnieżdżony w ramce danych na jednym lub dwóch poziomach. Czy istnieje szybki i brudny sposób uzyskania dostępu do ostatniej wartości bez korzystania zlength() funkcji? Coś innego niż $#specjalny wariant PERL ?

Chciałbym więc coś takiego:

dat$vec1$vec2[$#]

zamiast

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

1
W żadnym wypadku nie jestem ekspertem od języka R, ale pojawiło się szybkie google: < stat.ucl.ac.be/ISdidactique/Rhelp/library/pastecs/html/… > Wygląda na to, że istnieje „ostatnia” funkcja.
korzystne


1
MATLAB ma zapis „moja zmienna (end-k)”, gdzie k jest liczbą całkowitą mniejszą niż długość wektora, który zwróci element (długość (moja zmienna) -k). Byłoby miło mieć w R.
EngrStudent

Odpowiedzi:


368

Korzystam z tailfunkcji:

tail(vector, n=1)

Zaletą ze tailjest to, że działa na dataframes też, w przeciwieństwie do x[length(x)]idiomu.


5
jednak x [długość (x [, 1]),] działa na ramkach danych lub x [dim (x) [1],]
kpierce8

29
Zauważ, że w przypadku ramek danych długość (x) == ncol (x) jest więc zdecydowanie błędna, a dim (x) [1] można bardziej opisowo zapisać nrow (x).
hadley,

2
@hadley - sugestia kpierce8 x[length(x[,1]),]nie jest zła (zwróć uwagę na przecinek w xpodzbiorze), ale z pewnością jest niezręczna.
jbaums

4
Pamiętaj, że mój test porównawczy poniżej pokazuje, że jest to wolniejsze niż x[length(x)]średnio 30 razy dla większych wektorów!
anonimowy

1
Nie działa, jeśli chcesz dodać elementy z wektorówtail(vector, n=1)-tail(vector, n=2)
Andreas Storvik Strauman

180

Aby odpowiedzieć na to nie z estetycznego, ale zorientowanego na wydajność punktu widzenia, przedstawiłem wszystkie powyższe sugestie jako punkt odniesienia . Mówiąc dokładniej, rozważałem sugestie

  • x[length(x)]
  • mylast(x), gdzie mylastjest funkcja C ++ zaimplementowana przez Rcpp,
  • tail(x, n=1)
  • dplyr::last(x)
  • x[end(x)[1]]]
  • rev(x)[1]

i zastosował je do losowych wektorów o różnych rozmiarach (10 ^ 3, 10 ^ 4, 10 ^ 5, 10 ^ 6 i 10 ^ 7). Zanim spojrzymy na liczby, myślę, że powinno być jasne, że wszystko, co staje się zauważalnie wolniejsze przy większym rozmiarze wejściowym (tj. Wszystko, co nie jest O (1)), nie jest opcją. Oto kod, którego użyłem:

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]))}

To daje mi

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

To natychmiast wyklucza wszystko, co wiąże się z tym, revlub endponieważ najwyraźniej tak nie jest O(1)(a wyrażenia wynikowe są oceniane w sposób nie leniwy). taili dplyr::lastnie są daleko od bycia, O(1)ale są również znacznie wolniejsze niż mylast(x)i x[length(x)]. Ponieważ mylast(x)jest wolniejszy niż x[length(x)]i nie daje żadnych korzyści (raczej jest niestandardowy i nie obsługuje z wdziękiem pustego wektora), myślę, że odpowiedź jest jasna: Proszę użyćx[length(x)] .


11
Rozwiązania ^ O (1) powinny być jedyną akceptowalną odpowiedzią na to pytanie.
Kwame,

2
Dzięki za pomiar wszystkich tych anonów +1!
Sam

1
Próbowałem mylastR=function(x) {x[length(x)}Jest to szybsze niż mylastw Rcpp, ale raz wolniejsze niż pisanie x[length(x)]bezpośrednio
Endle_Zhenbo 18.04.19

115

Jeśli szukasz czegoś tak ładnego jak notacja x [-1] Pythona, myślę, że nie masz szczęścia. Standardowy idiom to

x[length(x)]  

ale wystarczy napisać funkcję, aby to zrobić:

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

Irytuje mnie ta brakująca funkcja w R!


3
fajny pomysł, aby zaoferować przykład funkcji +1
H.Latte

Zauważ, że jeśli chcesz ostatnich kilku elementów wektora, a nie tylko ostatniego elementu, nie musisz robić nic skomplikowanego, dostosowując to rozwiązanie. Wektoryzacja R pozwala na zrobić rzeczy NEET podoba się ostatnie cztery elementy xrobiąc x[length(x)-0:3].
J. Mini

46

Łączenie pomysłów Lindelof i Gregga Linda :

last <- function(x) { tail(x, n = 1) }

Pracując w linii poleceń, zwykle pomijam n=, tj tail(x, 1).

W przeciwieństwie lastdo pastecspakietu headi tail(z utils) działa nie tylko na wektorach, ale także na ramkach danych itp., A także może zwracać dane „ bez pierwszych / ostatnich n elementów ”, np.

but.last <- function(x) { head(x, n = -1) }

(Pamiętaj, że musisz do tego użyć headzamiast tail.)


7
Pamiętaj, że mój test porównawczy poniżej pokazuje, że jest to wolniejsze niż x[length(x)]średnio 30 razy dla większych wektorów!
anonimowy

19

Dplyr pakiet zawiera funkcję last():

last(mtcars$mpg)
# [1] 21.4

4
Zasadniczo sprowadza się to x[[length(x)]]ponownie.
Rich Scriven

6
Podobnie pod maską, ale z tą odpowiedzią nie musisz pisać własnej funkcji last()i przechowywać tej funkcji gdzieś, jak zrobiło to kilka osób powyżej. Uzyskujesz lepszą czytelność funkcji, a jej przenośność pochodzi z CRAN, aby ktoś inny mógł uruchomić kod.
Sam Firke,

1
Można również pisać jako mtcars$mpg %>% last, w zależności od preferencji.
Keith Hughitt

1
@RichScriven Niestety, jest znacznie wolniejszy niż x[[length(x)]]!
anonimowy

18

Właśnie przetestowałem te dwa podejścia w ramce danych z 663552 wierszami, używając następującego kodu:

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 

i

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 

Zakładając, że pracujesz z wektorami, dostęp do pozycji długości jest znacznie szybszy.


3
Dlaczego nie przetestować tail(strsplit(x,".",fixed=T)[[1]],1)w drugim przypadku? Dla mnie główną zaletą tailjest to, że możesz napisać to w jednym wierszu. ;)
mschilli

13

Innym sposobem jest pobranie pierwszego elementu wektora odwróconego:

rev(dat$vect1$vec2)[1]

7
To będzie drogie !
Felipe Gerard

1
Należy pamiętać, że jest to operacja, której koszt obliczeniowy jest liniowy względem długości danych wejściowych; innymi słowy, podczas gdy O (n), nie jest to O (1). Zobacz także mój punkt odniesienia dla rzeczywistych liczb.
anonimowy

@ anonimowy Chyba że użyjesz iteratora
James

@James Right. Ale w takim przypadku Twój kod również nie działałby, prawda? Jeśli przez iterator masz na myśli to, co zapewnia pakiet iteratorów, to (1) nie możesz użyć, [1]aby uzyskać dostęp do pierwszego elementu i (2), podczas gdy możesz zastosować revdo iteratora, nie działa on zgodnie z oczekiwaniami: po prostu traktuje obiekt iteratora jako lista jego członków i odwraca to.
anonimowy


10

Mam inną metodę znajdowania ostatniego elementu w wektorze. Powiedz, że wektor jest 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

Proszę bardzo!


8

O co chodzi

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

1
Rozumiem, że NROWrobi to, czego można oczekiwać na wielu różnych typach danych, ale jest to w zasadzie to samo, co a[length(a)]ten OP chce uniknąć. Korzystając z przykładu zagnieżdżonego wektora OP, dat$vec1$vec2[NROW(dat$vec1$vec2)]nadal jest dość niechlujny.
Gregor Thomas,

1
można zapisać jakonrow
Franck Dernoncourt

2
Uwaga: W przeciwieństwie nrow, NROWtraktuje wektora jako matrycy 1 kolumny.
PatrickT

3

Pakiet xts zapewnia lastfunkcję:

library(xts)
a <- 1:100
last(a)
[1] 100
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.