Wie greife ich auf den letzten Wert in einem Vektor zu?

289

Angenommen, ich habe einen Vektor, der in einem Datenrahmen mit einer oder zwei Ebenen verschachtelt ist. Gibt es eine schnelle und schmutzige Möglichkeit, auf den letzten Wert zuzugreifen, ohne die length()Funktion zu verwenden? Etwas ala PERLs $#spezielles Var?

Also hätte ich gerne etwas wie:

dat$vec1$vec2[$#]

anstatt

dat$vec1$vec2[length(dat$vec1$vec2)]
user14008
quelle
1
Ich bin auf keinen Fall ein R-Experte, aber ein schnelles Google hat dies aufgedeckt : < stat.ucl.ac.be/ISdidactique/Rhelp/library/pastecs/html/… > Es scheint eine "letzte" Funktion zu geben.
wohltätige
Siehe auch
krlmlr
1
MATLAB hat die Notation "myvariable (end-k)", wobei k eine ganze Zahl ist, die kleiner ist als die Länge des Vektors, der das (length (myvariable) -k) -te Element zurückgibt. Das wäre schön in R.
EngrStudent

Antworten:

368

Ich benutze die tailFunktion:

tail(vector, n=1)

Das tailSchöne daran ist, dass es im Gegensatz zur x[length(x)]Redewendung auch auf Datenrahmen funktioniert .

Lindelof
quelle
5
x [Länge (x [, 1]),] funktioniert jedoch auf Datenrahmen oder x [dim (x) [1],]
kpierce8
29
Beachten Sie, dass für Datenrahmen Länge (x) == ncol (x) definitiv falsch ist und dim (x) [1] beschreibender nrow (x) geschrieben werden kann.
Hadley
2
@hadley - kpierce8s Vorschlag von x[length(x[,1]),]ist nicht falsch (beachten Sie das Komma in der xTeilmenge), aber es ist sicherlich umständlich.
Jbaums
4
Bitte beachten Sie, dass mein unten stehender Benchmark zeigt, dass dies x[length(x)]bei größeren Vektoren langsamer ist als im Durchschnitt um den Faktor 30!
anonym
1
Funktioniert nicht, wenn Sie Inhalte aus Vektoren hinzufügen möchtentail(vector, n=1)-tail(vector, n=2)
Andreas Storvik Strauman
179

Um dies nicht aus ästhetischer, sondern aus leistungsorientierter Sicht zu beantworten, habe ich alle oben genannten Vorschläge einem Benchmark unterzogen . Um genau zu sein, habe ich die Vorschläge berücksichtigt

  • x[length(x)]
  • mylast(x), wo mylastist eine C ++ - Funktion über Rcpp implementiert,
  • tail(x, n=1)
  • dplyr::last(x)
  • x[end(x)[1]]]
  • rev(x)[1]

und wendete sie auf zufällige Vektoren verschiedener Größen an (10 ^ 3, 10 ^ 4, 10 ^ 5, 10 ^ 6 und 10 ^ 7). Bevor wir uns die Zahlen ansehen, sollte klar sein, dass alles, was mit größerer Eingabegröße merklich langsamer wird (dh alles, was nicht O (1) ist), keine Option ist. Hier ist der Code, den ich verwendet habe:

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

Es gibt mir

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

Dies schließt sofort alles aus, was involviert ist revoder endda dies eindeutig nicht O(1)der Fall ist (und die resultierenden Ausdrücke werden nicht faul ausgewertet). tailund dplyr::lastsind nicht weit davon entfernt zu sein, O(1)aber sie sind auch erheblich langsamer als mylast(x)und x[length(x)]. Da mylast(x)es langsamer ist als x[length(x)]und keine Vorteile bietet (es ist eher benutzerdefiniert und behandelt einen leeren Vektor nicht ordnungsgemäß), denke ich, dass die Antwort klar ist: Bitte verwendenx[length(x)] .

anonym
quelle
11
^ O (1) -Lösungen sollten die einzig akzeptable Antwort in dieser Frage sein.
Kwame
2
Vielen Dank für das Timing all dieser anon +1!
Sam
1
Ich habe versucht, mylastR=function(x) {x[length(x)}es ist schneller als mylastin Rcpp, aber einmal langsamer als x[length(x)]direkt zu schreiben
Endle_Zhenbo
115

Wenn Sie nach etwas suchen, das so schön ist wie Pythons x [-1] -Notation, haben Sie wahrscheinlich kein Glück. Die Standardsprache ist

x[length(x)]  

Es ist jedoch einfach genug, eine Funktion zu schreiben, um dies zu tun:

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

Diese fehlende Funktion in R nervt mich auch!

Gregg Lind
quelle
3
nette Idee, ein Funktionsbeispiel +1
H.Latte
Beachten Sie, dass Sie bei der Anpassung dieser Lösung keine komplexen Aufgaben ausführen müssen, wenn Sie die letzten Elemente eines Vektors und nicht nur das letzte Element verwenden möchten. R Vektorisierung ermöglicht es Ihnen , neet Dinge wie die letzten vier Elemente zu tun , xindem Sie x[length(x)-0:3].
J. Mini
46

Die Ideen von Lindelof und Gregg Lind kombinieren :

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

Wenn ich an der Eingabeaufforderung arbeite, lasse ich normalerweise das n=, dh tail(x, 1).

Anders als lastaus dem pastecsPaket headund tail(aus utils) arbeiten nicht nur Vektoren, sondern auch Datenrahmen usw. und können auch Daten " ohne erste / letzte n Elemente " zurückgeben, z

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

(Beachten Sie, dass Sie headdafür statt verwenden müssen tail.)

Florian Jenn
quelle
7
Bitte beachten Sie, dass mein unten stehender Benchmark zeigt, dass dies x[length(x)]bei größeren Vektoren langsamer ist als im Durchschnitt um den Faktor 30!
anonym
19

Das dplyr- Paket enthält eine Funktion last():

last(mtcars$mpg)
# [1] 21.4
Sam Firke
quelle
4
Das läuft im Grunde x[[length(x)]]wieder darauf hinaus.
Rich Scriven
6
Ähnlich unter der Haube, aber mit dieser Antwort müssen Sie nicht Ihre eigene Funktion schreiben last()und diese Funktion irgendwo speichern, wie es mehrere Leute oben getan haben. Sie erhalten die verbesserte Lesbarkeit einer Funktion, wobei die Portabilität von CRAN stammt, sodass jemand anderes den Code ausführen kann.
Sam Firke
1
Kann mtcars$mpg %>% lastje nach Wunsch auch als schreiben .
Keith Hughitt
1
@RichScriven Leider ist es erheblich langsamer als x[[length(x)]]!
anonym
18

Ich habe diese beiden Ansätze im Datenrahmen mit 663.552 Zeilen mit dem folgenden Code verglichen:

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 

und

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 

Angenommen, Sie arbeiten mit Vektoren, ist der Zugriff auf die Längenposition erheblich schneller.

scuerda
quelle
3
Warum nicht tail(strsplit(x,".",fixed=T)[[1]],1)für den 2. Fall testen ? Für mich ist der Hauptvorteil von, taildass Sie es in einer Zeile schreiben können. ;)
mschilli
13

Eine andere Möglichkeit besteht darin, das erste Element des umgekehrten Vektors zu nehmen:

rev(dat$vect1$vec2)[1]
James
quelle
7
Dies wird jedoch teuer sein !
Felipe Gerard
1
Bitte beachten Sie, dass dies eine Operation ist, deren Rechenaufwand in der Länge der Eingabe linear ist. mit anderen Worten, während O (n) ist, ist es nicht O (1). Siehe auch meinen Benchmark unten für die tatsächlichen Zahlen.
anonym
@anonymous Es sei denn, Sie verwenden einen Iterator
James
@ James Richtig. Aber in diesem Fall würde Ihr Code auch nicht funktionieren, oder? Wenn Sie mit Iterator meinen, was vom Iterator-Paket bereitgestellt wird, dann (1) können Sie nicht [1]auf das erste Element zugreifen und (2) während Sie revauf einen Iterator anwenden können, verhält es sich nicht wie erwartet: Es behandelt nur das Iterator-Objekt als eine Liste seiner Mitglieder und kehrt das um.
anonym
11

Paket data.tableenthält lastFunktion

library(data.table)
last(c(1:10))
# [1] 10
Enrique Pérez Herrero
quelle
2
Das läuft im Grunde x[[length(x)]]wieder darauf hinaus.
Rich Scriven
10

Ich habe eine andere Methode, um das letzte Element in einem Vektor zu finden. Angenommen, der Vektor ist 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

Los geht's!

Akash
quelle
8

Was ist mit

> a <- c(1:100,555)
> a[NROW(a)]
[1] 555
Kurt Ludikovsky
quelle
1
Ich weiß, dass NROWdies genau das tut, was Sie von vielen verschiedenen Datentypen erwarten würden, aber es ist im Wesentlichen dasselbe, was a[length(a)]das OP zu vermeiden hofft. Die Verwendung des OP-Beispiels eines verschachtelten Vektors dat$vec1$vec2[NROW(dat$vec1$vec2)]ist immer noch ziemlich chaotisch.
Gregor Thomas
1
kann geschrieben werden alsnrow
Franck Dernoncourt
2
Hinweis: Im Gegensatz dazu nrowwird NROWein Vektor als 1-Spalten-Matrix behandelt.
PatrickT
3

Das xts-Paket bietet eine lastFunktion:

library(xts)
a <- 1:100
last(a)
[1] 100
smoff
quelle