¿Cómo acceder al último valor en un vector?

289

Supongamos que tengo un vector que está anidado en un marco de datos de uno o dos niveles. ¿Hay alguna forma rápida y sucia de acceder al último valor, sin usar la length()función? ¿Algo especial de ala PERL $#?

Entonces me gustaría algo como:

dat$vec1$vec2[$#]

en vez de

dat$vec1$vec2[length(dat$vec1$vec2)]
usuario14008
fuente
1
De ninguna manera soy un experto en R, pero un google rápido apareció esto: < stat.ucl.ac.be/ISdidactique/Rhelp/library/pastecs/html/… > Parece que hay una "última" función.
benefactual
1
MATLAB tiene la notación "myvariable (end-k)", donde k es un entero menor que la longitud del vector que devolverá el elemento (length (myvariable) -k) th. Eso sería bueno tener en R.
EngrStudent

Respuestas:

368

Yo uso la tailfunción:

tail(vector, n=1)

Lo bueno tailes que también funciona en marcos de datos, a diferencia del x[length(x)]idioma.

lindelof
fuente
55
sin embargo x [length (x [, 1]),] funciona en marcos de datos o x [dim (x) [1],]
kpierce8
29
Tenga en cuenta que para los marcos de datos, length (x) == ncol (x), por lo que definitivamente está mal, y dim (x) [1] puede escribirse más descriptivamente como nrow (x).
hadley
2
@hadley: la sugerencia de kpierce8 x[length(x[,1]),]no es incorrecta (tenga en cuenta la coma en el xsubconjunto), pero ciertamente es incómoda.
jbaums
44
¡Tenga en cuenta que mi punto de referencia a continuación muestra que esto es más lento que x[length(x)]en un factor de 30 en promedio para vectores más grandes!
anónimo
1
Sin embargotail(vector, n=1)-tail(vector, n=2)
Andreas Storvik Strauman
180

Para responder esto no desde un punto de vista estético sino orientado al rendimiento, he puesto todas las sugerencias anteriores a través de un punto de referencia . Para ser precisos, he considerado las sugerencias

  • x[length(x)]
  • mylast(x), donde mylastse implementa una función C ++ a través de Rcpp,
  • tail(x, n=1)
  • dplyr::last(x)
  • x[end(x)[1]]]
  • rev(x)[1]

y los aplicó a vectores aleatorios de varios tamaños (10 ^ 3, 10 ^ 4, 10 ^ 5, 10 ^ 6 y 10 ^ 7). Antes de mirar los números, creo que debería quedar claro que cualquier cosa que se vuelva notablemente más lenta con un mayor tamaño de entrada (es decir, cualquier cosa que no sea O (1)) no es una opción. Aquí está el código que usé:

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

Me da

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

Esto descarta inmediatamente cualquier cosa que implique revo endya que claramente no lo es O(1)(y las expresiones resultantes se evalúan de manera no perezosa). taily dplyr::lastno están lejos de ser, O(1)pero también son considerablemente más lentos que mylast(x)y x[length(x)]. Como mylast(x)es más lento x[length(x)]y no proporciona beneficios (más bien, es personalizado y no maneja un vector vacío con gracia), creo que la respuesta es clara: por favor, usex[length(x)] .

anónimo
fuente
11
^ O (1) las soluciones deberían ser la única respuesta aceptable en esta pregunta.
Kwame
2
¡Gracias por cronometrar todos esos anon +1!
Sam
1
Lo intenté mylastR=function(x) {x[length(x)}Es más rápido que mylasten Rcpp, pero una vez más lento que escribir x[length(x)]directamente
Endle_Zhenbo
115

Si está buscando algo tan bueno como la notación x [-1] de Python, creo que no tiene suerte. El idioma estándar es

x[length(x)]  

pero es bastante fácil escribir una función para hacer esto:

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

¡Esta característica que falta en R también me molesta!

Gregg Lind
fuente
3
buena idea ofrecer un ejemplo de función +1
H.Latte
Tenga en cuenta que si desea los últimos elementos de un vector en lugar de solo el último elemento, no hay necesidad de hacer nada complejo al adaptar esta solución. La vectorización de R le permite hacer cosas nuevas como obtener los últimos cuatro elementos xal hacer x[length(x)-0:3].
J. Mini
46

Combinando las ideas de lindelof y Gregg Lind :

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

Al trabajar en el indicador, generalmente omito el n=, es decir tail(x, 1).

A diferencia lastdel pastecspaquete, heady tail(desde utils) funcionan no solo en vectores sino también en marcos de datos, etc., y también pueden devolver datos " sin el primer / último n elementos ", por ejemplo

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

(Tenga en cuenta que tiene que usar headpara esto, en lugar de tail).

Florian Jenn
fuente
77
¡Tenga en cuenta que mi punto de referencia a continuación muestra que esto es más lento que x[length(x)]en un factor de 30 en promedio para vectores más grandes!
anónimo
19

El paquete dplyr incluye una función last():

last(mtcars$mpg)
# [1] 21.4
Sam Firke
fuente
44
Esto básicamente se reduce a x[[length(x)]]otra vez.
Rich Scriven
66
Similar bajo el capó, pero con esta respuesta no tiene que escribir su propia función last()y almacenar esa función en algún lugar, como lo han hecho varias personas anteriormente. Obtiene la legibilidad mejorada de una función, y la portabilidad proviene de CRAN para que otra persona pueda ejecutar el código.
Sam Firke
1
También puede escribir como mtcars$mpg %>% last, según su preferencia.
Keith Hughitt
1
@RichScriven ¡Desafortunadamente, es considerablemente más lento que x[[length(x)]], sin embargo!
Anónimo
18

Acabo de comparar estos dos enfoques en el marco de datos con 663,552 filas usando el siguiente código:

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 

y

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 

Entonces, suponiendo que esté trabajando con vectores, acceder a la posición de longitud es significativamente más rápido.

scuerda
fuente
3
¿Por qué no hacer pruebas tail(strsplit(x,".",fixed=T)[[1]],1)para el segundo caso? Para mí, la principal ventaja de esto tailes que puedes escribirlo en una línea. ;)
mschilli
13

Otra forma es tomar el primer elemento del vector invertido:

rev(dat$vect1$vec2)[1]
James
fuente
77
¡Sin embargo, esto será costoso !
Felipe Gerard
1
Tenga en cuenta que esta es una operación cuyo costo computacional es lineal en la longitud de la entrada; en otras palabras, mientras O (n), no es O (1). Vea también mi punto de referencia a continuación para conocer los números reales.
anónimo
@anónimo A menos que use un iterador
James
@ James Right. Pero en ese caso, su código tampoco funcionaría, ¿verdad? Si por iterador te refieres a lo que proporciona el paquete de iteradores, entonces (1) no puedes usar [1]para acceder al primer elemento y (2) mientras puedes aplicar reva un iterador, no se comporta como se esperaba: solo trata el objeto iterador como una lista de sus miembros y revierte eso.
anónimo el
11

El paquete data.tableincluye la lastfunción

library(data.table)
last(c(1:10))
# [1] 10
Enrique Pérez Herrero
fuente
2
Esto básicamente se reduce a x[[length(x)]]otra vez.
Rich Scriven
10

Tengo otro método para encontrar el último elemento en un vector. Digamos que el vector es 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

Ahí tienes!

Akash
fuente
8

De que se trata

> a <- c(1:100,555)
> a[NROW(a)]
[1] 555
Kurt Ludikovsky
fuente
1
Aprecio que NROWhaga lo que esperaría en muchos tipos de datos diferentes, pero es esencialmente lo mismo a[length(a)]que OP espera evitar. Usar el ejemplo de OP de un vector anidado dat$vec1$vec2[NROW(dat$vec1$vec2)]sigue siendo bastante complicado.
Gregor Thomas
1
puede escribirse comonrow
Franck Dernoncourt
2
Nota: a diferencia nrow, NROWtrata un vector como una matriz de 1 columna.
PatrickT
3

El paquete xts proporciona una lastfunción:

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