Lấy n phần tử cuối cùng của một vectơ. Có cách nào tốt hơn là sử dụng hàm length () không?


84

Nếu, vì lợi ích của đối số, tôi muốn năm phần tử cuối cùng của vectơ độ dài 10 trong Python, tôi có thể sử dụng toán tử "-" trong chỉ mục phạm vi để:

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

Cách tốt nhất để làm điều này trong R là gì? Có cách nào rõ ràng hơn kỹ thuật hiện tại của tôi là sử dụng hàm length () không?

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

Câu hỏi liên quan đến phân tích chuỗi thời gian btw trong đó nó thường hữu ích khi chỉ làm việc trên dữ liệu gần đây.

Câu trả lời:


120

xem ?tail?headcho một số chức năng thuận tiện:

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

Vì lợi ích của đối số: mọi thứ trừ năm yếu tố cuối cùng sẽ là:

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

Như @Martin Morgan đã nói trong các bình luận, có hai khả năng khác nhanh hơn giải pháp đuôi, trong trường hợp bạn phải thực hiện điều này một triệu lần trên một vectơ có giá trị 100 triệu. Để dễ đọc, tôi sẽ đi với đuôi.

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     

mã điểm chuẩn:

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

Nhưng không nhanh hơn cắt lát - thử nghiệm cho thấy điều này.
Nick Bastin

1
Cảm ơn Nick thú vị. Vâng, cắt lát Python là một tính năng tuyệt vời của ngôn ngữ này.
Thomas Browne

5
@Nick: Thật vậy. Trên vectơ có độ dài 1e6 và 1000 lần lặp lại, nó chậm hơn khoảng 0,3 giây. Hãy tưởng tượng những gì bạn có thể làm với 0,3 giây bạn tiết kiệm được ...
Joris Meys

6
Việc thực hiện utils ::: tail.default x[seq.int(to=length(x), length.out=5)]dường như nhanh hơn khoảng 10 lần so với tail()nhưng không có kiểm tra tỉnh táo; x[length(x)-(4:0)]vẫn nhanh hơn.
Martin Morgan

1
@Joris: Tôi có thể tưởng tượng những gì tôi sẽ làm với chúng sau khi tôi chạy thao tác cụ thể đó trong một vòng lặp bên trong hàng tỷ lần .. :-) Vấn đề là việc cắt lát không ít rõ ràng hơn, nhưng tối ưu hơn, vì vậy nói chung tôi đi theo con đường đó.
Nick Bastin

6

Bạn có thể làm chính xác điều tương tự trong R với hai ký tự nữa:

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

hoặc là

x[-(1:5)]

Điều gì sẽ xảy ra nếu tôi không biết độ dài của Vectơ, nhưng tôi vẫn luôn muốn có 5 phần tử cuối cùng? Phiên bản python vẫn hoạt động nhưng ví dụ R của bạn trả về 15 phần tử cuối cùng và vì vậy sẽ vẫn yêu cầu lệnh gọi length ()?
Thomas Browne

10
Sacha, tôi không nghĩ câu trả lời của bạn chung chung. Những gì ví dụ mã của bạn làm là bỏ 5 kết quả đầu tiên, thay vì giữ 5 kết quả cuối cùng. Trong ví dụ này, nó giống như vậy, nhưng điều sau không hoạt động: x <- 0:20; x[-5:-1]- điều này trả về mười lăm phần tử cuối cùng.
Andrie

Tôi không biết python, nhưng trong OP's x[-5:]: điều này có nghĩa là bỏ qua 5 phần tử đầu tiên hay giữ lại 5 phần tử cuối cùng? Nếu đó là lần đầu tiên, anh ấy đang gián tiếp sử dụng độ dài của bạn, giống như bạn, ở đây (nếu không, làm thế nào để bạn biết những yếu tố nào cần bỏ qua?)
Nick Sabbe

1
toán tử "-" trong Python có nghĩa là đếm ngược. Vì vậy, nó sẽ luôn trả về 5 phần tử cuối cùng trong trường hợp này.
Thomas Browne

2
À đúng rồi, tôi không biết python và cho rằng nó có nghĩa là bỏ qua 5. đầu tiên tail là những gì bạn muốn sau đó.
Sacha Epskamp

6

Sự phản đối tailở đây chỉ dựa trên tốc độ dường như không thực sự nhấn mạnh rằng một phần của tốc độ chậm hơn xuất phát từ thực tế là đuôi an toàn hơn để làm việc, nếu bạn không chắc chắn rằng độ dài của x sẽ vượt quá n, con số trong số các phần tử bạn muốn lấy ra:

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 sẽ chỉ trả về số phần tử tối đa thay vì tạo ra lỗi, vì vậy bạn không cần phải tự mình kiểm tra lỗi. Một lý do tuyệt vời để sử dụng nó. Mã sạch hơn an toàn hơn, nếu micro giây / mili giây thừa không quan trọng nhiều với bạn trong việc sử dụng nó.


3

Làm thế nào về 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

Nhận xét muộn. Thời gian xử lý được thực hiện khi đảo ngược vectơ quá lớn đối với các vectơ dài. Hãy thử thời gian nó khix <- 1:10e6
Chris Njuguna

Điểm tốt @ChrisNjuguna. Các công trình lớn sử dụng một vector có độ dài 10 mặc dù :)
Brian Davis

2

Đây là một chức năng để làm điều đó và có vẻ khá nhanh.

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

SỬ DỤNG:

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

BENCHMARK:

                                                    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

Tôi chỉ thêm vào đây một cái gì đó liên quan. Tôi muốn truy cập một vectơ với các chỉ số phụ trợ, tức là viết một cái gì đó giống như tail(x, i)nhưng để trả về x[length(x) - i + 1]chứ không phải toàn bộ phần đuôi.

Sau các bài bình luận, tôi đã đánh giá hai giải pháp:

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

Vì vậy, có vẻ như trong trường hợp này, ngay cả đối với các vectơ nhỏ, tailrất chậm so với truy cập trực tiếp

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.