Отримання останніх n елементів вектора. Чи є кращий спосіб, ніж використання функції length ()?


84

Якщо для аргументу я хочу останні п'ять елементів 10-довжинного вектора в Python, я можу використовувати оператор "-" в індексі діапазону так:

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

Який найкращий спосіб зробити це в R? Чи є чистіший спосіб, ніж мій поточний прийом, який полягає у використанні функції 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
> 

Питання пов’язане з аналізом часових рядів, де часто корисно працювати лише над останніми даними.

Відповіді:


120

див. ?tailі ?headдеякі зручні функції:

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

Для аргументу: все, крім останніх п’яти елементів, буде:

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

Як зазначає @Martin Morgan у коментарях, є дві інші можливості, які швидші за хвостове рішення, на випадок, якщо вам доведеться виконати це мільйон разів на векторі 100 мільйонів значень. Для читабельності я пішов би з хвостом.

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     

код порівняльного тесту:

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

Але не швидше нарізки - тестування це підтверджує.
Нік Бастін,

1
Спасибі Ніку цікаво. Так, нарізка Python - приємна особливість мови.
Томас Браун,

5
@ Нік: Дійсно. На векторі довжиною 1e6 та 1000 реплікацій це приблизно на 0,3 секунди повільніше. Уявіть, що ви можете зробити із збереженими 0,3 секунди ...
Джоріс Мейс,

6
Реалізація utils ::: tail.default - x[seq.int(to=length(x), length.out=5)]це, здається, приблизно в 10 разів швидше, ніж tail()без перевірки осудності; x[length(x)-(4:0)]ще швидше.
Мартін Морган,

1
@Joris: Я можу уявити, що б я робив з ними після того, як цю операцію провів у внутрішньому циклі мільярд разів .. :-) Справа в тому, що нарізка не менш чітка, але більш оптимальна, тому загалом пішов би цим шляхом.
Нік Бастін,

6

Ви можете зробити абсолютно те саме в R із ще двома символами:

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

або

x[-(1:5)]

Що робити, якщо я не знаю довжину вектора, але я все одно хочу останні 5 елементів? Версія python все ще працює, але ваш приклад R повертає останні 15 елементів і тому все одно вимагатиме виклику length ()?
Томас Браун,

10
Саша, я не думаю, що ваша відповідь узагальнена. Що робить ваш приклад коду, це скидання перших 5 результатів, а не збереження останніх п’яти. У цьому прикладі це те саме, але наступне не працює: x <- 0:20; x[-5:-1]- це повертає останні п’ятнадцять елементів.
Андрі

Я не знаю python, але в OP x[-5:]: чи означає це пропустити перші 5 елементів, або зберегти останні 5? Якщо це перший, він побічно використовує вашу довжину, як і ви, тут (інакше, як ви знаєте, які елементи пропустити?)
Нік Саббе,

1
оператор "-" у Python означає зворотний відлік. Тож у цьому випадку він завжди повертає останні 5 елементів.
Томас Браун

2
Ах, правильно, я не знаю python і припускав, що це означає пропустити перші 5. tail - це те, що ви тоді хочете.
Саша Епскамп,

6

Неодобрення tailтут лише на основі швидкості насправді, здається, не підкреслює, що частина повільнішої швидкості походить від того факту, що з хвостом безпечніше працювати, якщо ви не впевнені, що довжина x перевищить n, число елементів, які потрібно підгрупувати:

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 просто поверне максимальну кількість елементів, замість того, щоб генерувати помилку, тому вам не потрібно самостійно перевіряти помилки. Чудовий привід для його використання. Безпечніший чистий код, якщо додаткові мікросекунди / мілісекунди для вас не мають великого значення при його використанні.


3

Як щодо 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

Пізній коментар. Час обробки, необхідний для реверсування вектора, занадто великий для довгих векторів. Спробуйте визначити час, колиx <- 1:10e6
Кріс Ньюгуна

Хороший момент @ChrisNjuguna. Чудово працює, використовуючи вектор довжиною 10, хоча :)
Брайан Девіс

2

Ось функція, яка робить це, і здається досить швидко.

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

ВИКОРИСТАННЯ:

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

Орієнтир:

                                                    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

Я просто додаю сюди щось пов’язане. Мені хотіли отримати доступ до вектора із серверними індексами, тобто написати щось на зразок, tail(x, i)але повернути, x[length(x) - i + 1]а не весь хвіст.

У наступних коментарях я визначив два рішення:

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

Отже, у цьому випадку виявляється, що навіть для малих векторів tailдуже повільний порівняно з прямим доступом

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.