Como acessar o último valor em um vetor?


289

Suponha que eu tenha um vetor aninhado em um dataframe em um ou dois níveis. Existe uma maneira rápida e suja de acessar o último valor, sem usar a length()função? Algo ala PERL$# var especial ?

Então, eu gostaria de algo como:

dat$vec1$vec2[$#]

ao invés de

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

1
Não sou especialista em R, mas um rápido google descobriu isso: < stat.ucl.ac.be/ISdidactique/Rhelp/library/pastecs/html/… > Parece haver uma "última" função.
benefactual


1
MATLAB possui a notação "myvariable (end-k)", onde k é um número inteiro menor que o comprimento do vetor que retornará o (length (myvariable) -k) th elemento. Isso seria bom ter em R.
EngrStudent

Respostas:


368

Eu uso a tailfunção:

tail(vector, n=1)

O bom tailé que ele também funciona em quadros de dados, ao contrário do x[length(x)]idioma.


5
no entanto x [comprimento (x [, 1]),] funciona em dataframes ou x [dim (x) [1],]
kpierce8

29
Observe que, para quadros de dados, length (x) == ncol (x) está definitivamente errado, e dim (x) [1] pode ser mais descritivamente escrito nrow (x).
Hadley

2
@hadley - a sugestão de kpierce8 de x[length(x[,1]),]não está errada (observe a vírgula no xsubconjunto), mas certamente é incômoda.
jbaums

4
Observe que minha referência abaixo mostra que isso é mais lento do que x[length(x)]um fator de 30 em média para vetores maiores!
anônimo

1
Não funciona se você quiser adicionar coisas de vetorestail(vector, n=1)-tail(vector, n=2)
Andreas Storvik Strauman

179

Para responder a isso não do ponto de vista estético, mas orientado para o desempenho, coloquei todas as sugestões acima através de uma referência . Para ser mais preciso, considerei as sugestões

  • x[length(x)]
  • mylast(x), onde mylasté uma função C ++ implementada por meio do Rcpp,
  • tail(x, n=1)
  • dplyr::last(x)
  • x[end(x)[1]]]
  • rev(x)[1]

e os aplicou a vetores aleatórios de vários tamanhos (10 ^ 3, 10 ^ 4, 10 ^ 5, 10 ^ 6 e 10 ^ 7). Antes de olharmos para os números, acho que deve ficar claro que qualquer coisa que se torne visivelmente mais lenta com maior tamanho de entrada (ou seja, qualquer coisa que não seja O (1)) não é uma opção. Aqui está o código que eu usei:

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 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

Isso exclui imediatamente qualquer coisa que envolva revou enduma vez que claramente não é O(1)(e as expressões resultantes são avaliadas de maneira não preguiçosa). taile dplyr::lastnão estão longe de existir, O(1)mas também são consideravelmente mais lentos que mylast(x)e x[length(x)]. Como mylast(x)é mais lento x[length(x)]e não oferece benefícios (é personalizado e não lida com um vetor vazio normalmente), acho que a resposta é clara: usex[length(x)] .


11
^ O (1) soluções devem ser a única resposta aceitável nesta questão.
Kwame

2
Obrigado por cronometrar todos aqueles anon +1!
Sam

1
Tentei mylastR=function(x) {x[length(x)}É mais rápido do que mylastem Rcpp, mas houve um tempo mais lento do que escrever x[length(x)]diretamente
Endle_Zhenbo

115

Se você está procurando algo tão bom quanto a notação x [-1] do Python, acho que você está sem sorte. O idioma padrão é

x[length(x)]  

mas é fácil o suficiente escrever uma função para fazer isso:

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

Esse recurso que falta no R também me incomoda!


3
boa idéia para oferecer um exemplo de função +1
H.Latte 31/01/19

Observe que, se você deseja os últimos elementos de um vetor, e não apenas o último, não há necessidade de fazer nada complexo ao adaptar esta solução. A vetorização de R permite que você faça coisas novas como obter os últimos quatro elementos xfazendo x[length(x)-0:3].
J. Mini

46

Combinando as idéias de Lindind e Gregg Lind :

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

Trabalhando no prompt, geralmente omito o n=, ou seja,tail(x, 1) .

Diferente lastdo pastecspacote, heade tail(de utils) trabalham não apenas em vetores, mas também em quadros de dados etc., e também podem retornar dados " sem o primeiro / último n elementos ", por exemplo

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

(Observe que você precisa usar headisso em vez de tail.)


7
Observe que minha referência abaixo mostra que isso é mais lento do que x[length(x)]um fator de 30 em média para vetores maiores!
anônimo

19

O pacote dplyr inclui uma função last():

last(mtcars$mpg)
# [1] 21.4

4
Isso basicamente se resume x[[length(x)]]novamente.
Rich Scriven

6
Semelhante por baixo do capô, mas com esta resposta, você não precisa escrever sua própria função last()e armazenar essa função em algum lugar, como várias pessoas fizeram acima. Você obtém a legibilidade aprimorada de uma função, com a portabilidade dela vindo do CRAN para que outra pessoa possa executar o código.
Sam Firke 07/06

1
Também pode escrever como mtcars$mpg %>% last, dependendo da sua preferência.
Keith Hughitt

1
@ RichScriven Infelizmente, é consideravelmente mais lento do x[[length(x)]]que!
anónimo

18

Acabei de comparar essas duas abordagens no quadro de dados com 663.552 linhas usando o seguinte 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 

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 

Portanto, supondo que você esteja trabalhando com vetores, acessar a posição do comprimento é significativamente mais rápido.


3
Por que não testar tail(strsplit(x,".",fixed=T)[[1]],1)para o segundo caso? Para mim, a principal vantagem do tailé que você pode escrevê-lo em uma linha. ;)
mschilli 7/07/2014

13

Outra maneira é pegar o primeiro elemento do vetor invertido:

rev(dat$vect1$vec2)[1]

7
Isso vai ser caro !
Felipe Gerard

1
Observe que esta é uma operação cujo custo computacional é linear no comprimento da entrada; em outras palavras, enquanto O (n), não é O (1). Veja também minha referência abaixo para obter números reais.
anônimo

@anonymous menos que você use um iterador
James

@ James Right. Mas, nesse caso, seu código também não funcionaria, funcionaria? Se por iterador você quer dizer o que é fornecido pelo pacote de iteradores, (1) você não pode usar [1]para acessar o primeiro elemento e (2) enquanto pode aplicar reva um iterador, ele não se comporta como o esperado: trata apenas o objeto do iterador como uma lista de seus membros e reverte isso.
anônimo


10

Eu tenho outro método para encontrar o último elemento em um vetor. Digamos que o vetor seja 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

Ai está!


8

Sobre o quê

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

1
Compreendo que NROWfaça o que você esperaria em vários tipos de dados diferentes, mas é essencialmente o mesmo a[length(a)]que o OP espera evitar. Usando o exemplo do OP de um vetor aninhado, dat$vec1$vec2[NROW(dat$vec1$vec2)]ainda é bastante confuso.
Gregor Thomas

1
pode ser escrito comonrow
Franck Dernoncourt

2
Nota: Ao contrário nrow, NROWtrata um vetor como matriz de 1 coluna.
PatrickT

3

O pacote xts fornece uma lastfunção:

library(xts)
a <- 1:100
last(a)
[1] 100
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.