Come accedere all'ultimo valore in un vettore?


290

Supponiamo di avere un vettore nidificato in un frame di dati di uno o due livelli. Esiste un modo rapido e sporco per accedere all'ultimo valore, senza utilizzare la length()funzione? Qualcosa di $#speciale alla var di PERL ?

Quindi vorrei qualcosa come:

dat$vec1$vec2[$#]

invece di

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

1
Non sono affatto un esperto di R, ma un rapido google ha mostrato questo: < stat.ucl.ac.be/ISdidactique/Rhelp/library/pastecs/html/… > Sembra esserci una funzione "last".
benefico


1
MATLAB ha la notazione "myvariable (end-k)" dove k è un numero intero inferiore alla lunghezza del vettore che restituirà l'elemento th (length (myvariable) -k). Sarebbe bello avere in R.
EngrStudent il

Risposte:


369

Uso la tailfunzione:

tail(vector, n=1)

La cosa bella tailè che funziona anche su frame di dati, a differenza del x[length(x)]linguaggio.


5
tuttavia x [lunghezza (x [, 1]),] funziona su frame di dati o x [dim (x) [1],]
kpierce8

29
Si noti che per i frame di dati, length (x) == ncol (x) è decisamente sbagliato e dim (x) [1] può essere più descrittivamente scritto nrow (x).
Hadley,

2
@hadley - il suggerimento di kpierce8 x[length(x[,1]),]non è sbagliato (notare la virgola nel xsottoinsieme), ma è certamente imbarazzante.
jbaums,

4
Si noti che il mio benchmark di seguito mostra che questo è più lento di x[length(x)]un fattore 30 in media per i vettori più grandi!
anonimo

1
Non funziona se si desidera aggiungere elementi dai vettoritail(vector, n=1)-tail(vector, n=2)
Andreas Storvik Strauman,

182

Per rispondere a questa domanda non dal punto di vista estetico ma orientato alle prestazioni, ho inserito tutti i suggerimenti sopra riportati attraverso un benchmark . Per essere precisi, ho considerato i suggerimenti

  • x[length(x)]
  • mylast(x), dove mylastè implementata una funzione C ++ tramite Rcpp,
  • tail(x, n=1)
  • dplyr::last(x)
  • x[end(x)[1]]]
  • rev(x)[1]

e li applicava a vettori casuali di varie dimensioni (10 ^ 3, 10 ^ 4, 10 ^ 5, 10 ^ 6 e 10 ^ 7). Prima di esaminare i numeri, penso che dovrebbe essere chiaro che tutto ciò che diventa notevolmente più lento con dimensioni di input maggiori (ovvero, qualsiasi cosa che non sia O (1)) non è un'opzione. Ecco il codice che ho usato:

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

Mi dà

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

Ciò esclude immediatamente tutto ciò che riguarda revo endpoiché chiaramente non lo sono O(1)(e le espressioni risultanti vengono valutate in modo non pigro). taile dplyr::lastnon sono lungi dall'essere O(1)ma sono anche considerevolmente più lenti di mylast(x)e x[length(x)]. Poiché mylast(x)è più lento di x[length(x)]e non offre alcun vantaggio (piuttosto, è personalizzato e non gestisce con grazia un vettore vuoto), penso che la risposta sia chiara: si prega di utilizzarex[length(x)] .


11
^ O (1) le soluzioni dovrebbero essere l'unica risposta accettabile in questa domanda.
Kwame,

2
Grazie per il cronometraggio di tutti quei +1 +1!
Sam,

1
Ho provato mylastR=function(x) {x[length(x)}È più veloce che mylastin Rcpp, ma una volta più lentamente della scrittura x[length(x)]diretta
Endle_Zhenbo

115

Se stai cercando qualcosa di bello come la notazione x [-1] di Python, penso che tu sia sfortunato. Il linguaggio standard è

x[length(x)]  

ma è abbastanza facile scrivere una funzione per fare questo:

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

Anche questa caratteristica mancante in R mi dà fastidio!


3
bella idea di offrire un esempio di funzione +1
H.Latte

Nota che se desideri gli ultimi elementi di un vettore anziché solo l'ultimo elemento, non è necessario fare nulla di complesso quando si adatta questa soluzione. La vettorializzazione di R ti permette di fare cose neet come ottenere gli ultimi quattro elementi xfacendo x[length(x)-0:3].
J. Mini

46

Combinando le idee di Lindelof e Gregg Lind :

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

Lavorando al prompt, di solito ometto n=, ad es tail(x, 1).

A differenza lastdel pastecspacchetto, heade tail(from utils) funziona non solo su vettori ma anche su frame di dati ecc. E può anche restituire dati " senza il primo / ultimo n elemento ", ad es.

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

(Nota che devi usare headper questo, invece di tail.)


7
Si noti che il mio benchmark di seguito mostra che questo è più lento di x[length(x)]un fattore di 30 in media per i vettori più grandi!
anonimo

19

Il pacchetto dplyr include una funzione last():

last(mtcars$mpg)
# [1] 21.4

4
Questo sostanzialmente si riduce di x[[length(x)]]nuovo.
Rich Scriven,

6
Simile sotto il cofano, ma con questa risposta non è necessario scrivere la propria funzione last()e memorizzarla da qualche parte, come diverse persone hanno fatto sopra. Ottieni la migliore leggibilità di una funzione, con la sua portabilità proveniente da CRAN in modo che qualcun altro possa eseguire il codice.
Sam Firke,

1
Può anche scrivere come mtcars$mpg %>% last, a seconda delle tue preferenze.
Keith Hughitt,

1
@RichScriven Sfortunatamente, è considerevolmente più lento di x[[length(x)]], però!
anonimo

18

Ho appena confrontato questi due approcci sul frame di dati con 663.552 righe usando il seguente codice:

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 

e

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 

Quindi, supponendo che tu stia lavorando con i vettori, accedere alla posizione della lunghezza è significativamente più veloce.


3
Perché non testare tail(strsplit(x,".",fixed=T)[[1]],1)per il secondo caso? Per me il vantaggio principale di tailè che puoi scriverlo in una riga. ;)
mschilli

13

Un altro modo è quello di prendere il primo elemento del vettore invertito:

rev(dat$vect1$vec2)[1]

7
Questo sarà costoso però!
Felipe Gerard,

1
Si noti che si tratta di un'operazione il cui costo computazionale è lineare nella lunghezza dell'input; in altre parole, mentre O (n), non è O (1). Vedi anche il mio benchmark di seguito per i numeri reali.
anonimo

@anonymous A meno che tu non usi un iteratore
James,

@James Right. Ma in quel caso, anche il tuo codice non funzionerebbe, vero? Se per iteratore intendi ciò che viene fornito dal pacchetto iteratori, allora (1) non puoi utilizzare [1]per accedere al primo elemento e (2) mentre puoi applicarlo reva un iteratore, non si comporta come previsto: tratta semplicemente l'oggetto iteratore come un elenco dei suoi membri e lo annulla.
anonimo


10

Ho un altro metodo per trovare l'ultimo elemento in un vettore. Di 'che il vettore è 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

Ecco qua!


8

Di cosa si tratta

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

1
Apprezzo che NROWfaccia quello che ti aspetteresti da molti tipi di dati diversi, ma è essenzialmente lo stesso a[length(a)]che l'OP spera di evitare. L'uso dell'esempio di OP di un vettore nidificato dat$vec1$vec2[NROW(dat$vec1$vec2)]è ancora piuttosto disordinato.
Gregor Thomas,

1
può essere scritto comenrow
Franck Dernoncourt il

2
Nota: a differenza nrow, NROWconsidera un vettore come matrice a 1 colonna.
PatrickT,

3

Il pacchetto xts fornisce una lastfunzione:

library(xts)
a <- 1:100
last(a)
[1] 100
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.