Ottenere gli ultimi n elementi di un vettore. Esiste un modo migliore rispetto all'utilizzo della funzione length ()?


86

Se, per amor di discussione, voglio gli ultimi cinque elementi di un vettore di 10 lunghezze in Python, posso usare l'operatore "-" nell'indice di intervallo così:

>>> x = range(10)
>>> x
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> x[-5:]
[5, 6, 7, 8, 9]
>>>

Qual è il modo migliore per farlo in R? Esiste un modo più pulito della mia tecnica attuale che consiste nell'usare la funzione length ()?

> x <- 0:9
> x
 [1] 0 1 2 3 4 5 6 7 8 9
> x[(length(x) - 4):length(x)]
[1] 5 6 7 8 9
> 

La domanda è relativa all'analisi delle serie temporali dove spesso è utile lavorare solo su dati recenti.

Risposte:


123

vedi ?taile ?headper alcune comode funzioni:

> x <- 1:10
> tail(x,5)
[1]  6  7  8  9 10

Per il bene dell'argomento: tutto tranne gli ultimi cinque elementi sarebbe:

> head(x,n=-5)
[1] 1 2 3 4 5

Come dice @Martin Morgan nei commenti, ci sono altre due possibilità che sono più veloci della soluzione di coda, nel caso in cui sia necessario eseguirla un milione di volte su un vettore di 100 milioni di valori. Per la leggibilità, sceglierei la coda.

test                                        elapsed    relative 
tail(x, 5)                                    38.70     5.724852     
x[length(x) - (4:0)]                           6.76     1.000000     
x[seq.int(to = length(x), length.out = 5)]     7.53     1.113905     

codice di benchmarking:

require(rbenchmark)
x <- 1:1e8
do.call(
  benchmark,
  c(list(
    expression(tail(x,5)),
    expression(x[seq.int(to=length(x), length.out=5)]),
    expression(x[length(x)-(4:0)])
  ),  replications=1e6)
)

Ma non più veloce dell'affettatura: i test lo confermano.
Nick Bastin

1
Grazie Nick interessante. Sì, l'affettatura di Python è una bella caratteristica del linguaggio.
Thomas Browne

5
@ Nick: infatti. Su un vettore di lunghezza 1e6 e 1000 repliche, è circa 0,3 secondi più lento. Immagina cosa puoi fare con gli 0,3 secondi che hai risparmiato ...
Joris Meys

6
L'implementazione di utils ::: tail.default è x[seq.int(to=length(x), length.out=5)]che sembra essere circa 10 volte più veloce tail()ma senza i controlli di integrità; x[length(x)-(4:0)]è ancora più veloce.
Martin Morgan,

1
@Joris: posso immaginare cosa ne farei dopo aver eseguito quella particolare operazione in un ciclo interno un miliardo di volte .. :-) Il punto è che l'affettatura non è meno chiara, ma più ottimale, quindi in generale io farei quella strada.
Nick Bastin

6

Puoi fare esattamente la stessa cosa in R con altri due personaggi:

x <- 0:9
x[-5:-1]
[1] 5 6 7 8 9

o

x[-(1:5)]

E se non conosco la lunghezza del vettore, ma voglio sempre gli ultimi 5 elementi? La versione python funziona ancora ma il tuo esempio R restituisce gli ultimi 15 elementi e quindi richiederebbe comunque una chiamata a length ()?
Thomas Browne

10
Sacha, non credo che la tua risposta sia generalizzata. Quello che fa il tuo esempio di codice è eliminare i primi 5 risultati, invece di mantenere gli ultimi cinque. In questo esempio è la stessa cosa, ma quanto segue non funziona: x <- 0:20; x[-5:-1]- restituisce gli ultimi quindici elementi.
Andrie

Non conosco Python, ma negli OP x[-5:]: significa saltare i primi 5 elementi o mantenere gli ultimi 5? Se è il primo, sta usando indirettamente la tua lunghezza, come te, qui (altrimenti, come fai a sapere quali elementi saltare?)
Nick Sabbe

1
l'operatore "-" in Python significa contare all'indietro. Quindi in questo caso restituirà sempre gli ultimi 5 elementi.
Thomas Browne

2
Ah, giusto, non conosco Python e presumo che significhi saltare il primo 5. tail è quello che vuoi allora.
Sacha Epskamp

6

La disapprovazione tailqui in base alla velocità da sola non sembra davvero di sottolineare che una parte della velocità più lenta deriva dal fatto che la coda è più sicuro con cui lavorare, se non per certo che la lunghezza di x supererà n, il numero di elementi che vuoi sottoinsieme:

x <- 1:10
tail(x, 20)
# [1]  1  2  3  4  5  6  7  8  9 10
x[length(x) - (0:19)]
#Error in x[length(x) - (0:19)] : 
#  only 0's may be mixed with negative subscripts

Tail restituirà semplicemente il numero massimo di elementi invece di generare un errore, quindi non è necessario eseguire da soli il controllo degli errori. Un ottimo motivo per usarlo. Codice più pulito più sicuro, se microsecondi / millisecondi extra non ti interessano molto nel suo utilizzo.


3

Che ne dici rev(x)[1:5]?

x<-1:10
system.time(replicate(10e6,tail(x,5)))
 user  system elapsed 
 138.85    0.26  139.28 

system.time(replicate(10e6,rev(x)[1:5]))
 user  system elapsed 
 61.97    0.25   62.23

Commento in ritardo. Il tempo di elaborazione necessario per invertire il vettore è troppo grande per vettori lunghi. Prova a cronometrare quandox <- 1:10e6
Chris Njuguna

Buon punto @ChrisNjuguna. Funziona alla grande usando un vettore di lunghezza 10 però :)
Brian Davis

2

Ecco una funzione per farlo e sembra ragionevolmente veloce.

endv<-function(vec,val) 
{
if(val>length(vec))
{
stop("Length of value greater than length of vector")
}else
{
vec[((length(vec)-val)+1):length(vec)]
}
}

UTILIZZO:

test<-c(0,1,1,0,0,1,1,NA,1,1)
endv(test,5)
endv(LETTERS,5)

PROVA DELLE PRESTAZIONI:

                                                    test replications elapsed relative
1                                 expression(tail(x, 5))       100000    5.24    6.469
2 expression(x[seq.int(to = length(x), length.out = 5)])       100000    0.98    1.210
3                       expression(x[length(x) - (4:0)])       100000    0.81    1.000
4                                 expression(endv(x, 5))       100000    1.37    1.691

2

Aggiungo solo qui qualcosa di correlato. Volevo accedere a un vettore con indici di backend, cioè tail(x, i)scrivere qualcosa come ma per tornare x[length(x) - i + 1]e non l'intera coda.

Seguendo i commenti ho confrontato due soluzioni:

accessRevTail <- function(x, n) {
    tail(x,n)[1]
}

accessRevLen <- function(x, n) {
  x[length(x) - n + 1]
}

microbenchmark::microbenchmark(accessRevLen(1:100, 87), accessRevTail(1:100, 87))
Unit: microseconds
                     expr    min      lq     mean median      uq     max neval
  accessRevLen(1:100, 87)  1.860  2.3775  2.84976  2.803  3.2740   6.755   100
 accessRevTail(1:100, 87) 22.214 23.5295 28.54027 25.112 28.4705 110.833   100

Quindi sembra in questo caso che anche per piccoli vettori, il tailconfronto con l'accesso diretto sia molto lento

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.