Comment accéder à la dernière valeur d'un vecteur?


290

Supposons que j'ai un vecteur imbriqué dans une trame de données à un ou deux niveaux. Existe-t-il un moyen rapide et sale d'accéder à la dernière valeur, sans utiliser la length()fonction? Quelque chose d'ala $#spécial PERL var?

Je voudrais donc quelque chose comme:

dat$vec1$vec2[$#]

au lieu de

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

1
Je ne suis en aucun cas un expert en R, mais un rapide Google a révélé ceci: < stat.ucl.ac.be/ISdidactique/Rhelp/library/pastecs/html/… > Il semble y avoir une "dernière" fonction.
benefactual


1
MATLAB a la notation "myvariable (end-k)" où k est un entier inférieur à la longueur du vecteur qui renverra le (length (myvariable) -k) ème élément. Ce serait bien d'avoir dans R.
EngrStudent

Réponses:


368

J'utilise la tailfonction:

tail(vector, n=1)

La bonne chose avec, tailc'est qu'il fonctionne aussi sur les cadres de données, contrairement à l' x[length(x)]idiome.


5
cependant x [longueur (x [, 1]),] fonctionne sur les trames de données ou x [dim (x) [1],]
kpierce8

29
Notez que pour les trames de données, la longueur (x) == ncol (x) est donc tout à fait erronée, et dim (x) [1] peut être plus descriptivement écrit nrow (x).
hadley

2
@hadley - la suggestion de kpierce8 x[length(x[,1]),]n'est pas fausse (notez la virgule dans le xsous - ensemble), mais c'est certainement gênant.
jbaums

4
Veuillez noter que mon indice de référence ci-dessous montre que cela est plus lent que x[length(x)]d'un facteur 30 en moyenne pour les vecteurs plus gros!
anonyme le

1
Cela ne fonctionne pas si vous souhaitez ajouter des éléments à partir de vecteurstail(vector, n=1)-tail(vector, n=2)
Andreas Storvik Strauman

180

Pour répondre à cette question non pas d'un point de vue esthétique mais axé sur les performances, j'ai mis toutes les suggestions ci-dessus à travers une référence . Pour être précis, j'ai considéré les suggestions

  • x[length(x)]
  • mylast(x), où se mylasttrouve une fonction C ++ implémentée via Rcpp,
  • tail(x, n=1)
  • dplyr::last(x)
  • x[end(x)[1]]]
  • rev(x)[1]

et les a appliqués à des vecteurs aléatoires de différentes tailles (10 ^ 3, 10 ^ 4, 10 ^ 5, 10 ^ 6 et 10 ^ 7). Avant d'examiner les chiffres, je pense qu'il devrait être clair que tout ce qui devient sensiblement plus lent avec une plus grande taille d'entrée (c'est-à-dire tout ce qui n'est pas O (1)) n'est pas une option. Voici le code que j'ai utilisé:

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

Ça me donne

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

Cela exclut immédiatement tout ce qui implique revou endcomme ils ne le sont clairement pas O(1)(et les expressions résultantes sont évaluées de manière non paresseuse). tailet dplyr::lastne sont pas loin d'être, O(1)mais ils sont aussi considérablement plus lents que mylast(x)et x[length(x)]. Puisqu'il mylast(x)est plus lent x[length(x)]et ne fournit aucun avantage (il est plutôt personnalisé et ne gère pas un vecteur vide avec élégance), je pense que la réponse est claire: veuillez utiliserx[length(x)] .


11
^ Les solutions O (1) devraient être la seule réponse acceptable à cette question.
Kwame

2
Merci d'avoir chronométré tous ces anons +1!
Sam

1
J'ai essayé mylastR=function(x) {x[length(x)}C'est plus rapide que mylastdans Rcpp, mais une fois plus lent que d'écrire x[length(x)]directement
Endle_Zhenbo

115

Si vous cherchez quelque chose d'aussi agréable que la notation x [-1] de Python, je pense que vous n'avez pas de chance. L'idiome standard est

x[length(x)]  

mais il est assez facile d'écrire une fonction pour ce faire:

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

Cette fonctionnalité manquante dans R m'énerve aussi!


3
belle idée d'offrir un exemple de fonction +1
H.Latte

Notez que si vous voulez les derniers éléments d'un vecteur plutôt que juste le dernier élément, il n'est pas nécessaire de faire quelque chose de complexe lors de l'adaptation de cette solution. La vectorisation de R vous permet de faire des choses comme faire les quatre derniers éléments xen faisant x[length(x)-0:3].
J. Mini

46

Combiner les idées de Lindelof et de Gregg Lind :

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

En travaillant à l'invite, j'omets généralement le n=, c.-à-d.tail(x, 1) .

Contrairement lastau pastecspackage, headet tail(à partir de utils) fonctionne non seulement sur les vecteurs mais également sur les trames de données, etc., et peut également renvoyer des données " sans premier / dernier n éléments ", par exemple

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

(Notez que vous devez utiliser headpour cela, au lieu de tail.)


7
Veuillez noter que mon indice de référence ci-dessous montre que cela est plus lent que x[length(x)]d'un facteur 30 en moyenne pour les vecteurs plus gros!
anonyme le

19

Le package dplyr comprend une fonction last():

last(mtcars$mpg)
# [1] 21.4

4
Cela se résume essentiellement à x[[length(x)]]nouveau.
Rich Scriven

6
Similaire sous le capot, mais avec cette réponse, vous n'avez pas à écrire votre propre fonction last()et à la stocker quelque part, comme plusieurs personnes l'ont fait ci-dessus. Vous obtenez la lisibilité améliorée d'une fonction, avec la portabilité de celle-ci provenant de CRAN afin que quelqu'un d'autre puisse exécuter le code.
Sam Firke

1
Peut également écrire en tant que mtcars$mpg %>% last, selon votre préférence.
Keith Hughitt

1
@RichScriven Malheureusement, c'est beaucoup plus lent que x[[length(x)]], cependant!
anonyme

18

Je viens de comparer ces deux approches sur la trame de données avec 663 552 lignes en utilisant le code suivant:

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 

et

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 

Donc, en supposant que vous travaillez avec des vecteurs, l'accès à la position de longueur est beaucoup plus rapide.


3
Pourquoi ne pas tester tail(strsplit(x,".",fixed=T)[[1]],1)le 2ème cas? Pour moi, le principal avantage de la tailest que vous pouvez l'écrire sur une seule ligne. ;)
mschilli

13

Une autre façon est de prendre le premier élément du vecteur inversé:

rev(dat$vect1$vec2)[1]

7
Ce sera cher cependant!
Felipe Gerard

1
Veuillez noter qu'il s'agit d'une opération dont le coût de calcul est linéaire dans la longueur de l'entrée; en d'autres termes, alors que O (n), ce n'est pas O (1). Voir aussi mon benchmark ci-dessous pour les chiffres réels.
anonyme le

@anonymous À moins que vous n'utilisiez un itérateur
James

@James à droite. Mais dans ce cas, votre code ne fonctionnerait pas non plus, n'est-ce pas? Si par itérateur vous voulez dire ce qui est fourni par le package d'itérateurs, (1) vous ne pouvez pas utiliser [1]pour accéder au premier élément et (2) pendant que vous pouvez appliquer revà un itérateur, il ne se comporte pas comme prévu: il traite simplement l'objet itérateur comme une liste de ses membres et l'inverse.
anonyme


10

J'ai une autre méthode pour trouver le dernier élément d'un vecteur. Disons que le vecteur l'est 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

Voilà!


8

De quoi s'agit-il

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

1
J'apprécie que cela NROWfasse ce que vous attendez sur beaucoup de types de données différents, mais c'est essentiellement la même chose a[length(a)]que OP espère éviter. L'utilisation de l'exemple OP d'un vecteur imbriqué dat$vec1$vec2[NROW(dat$vec1$vec2)]est toujours assez compliquée.
Gregor Thomas

1
peut s'écrire nrow
Franck Dernoncourt

2
Remarque: contrairement à nrow, NROW traite un vecteur comme une matrice à 1 colonne.
PatrickT

3

Le package xts fournit une lastfonction:

library(xts)
a <- 1:100
last(a)
[1] 100
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.