获取向量的最后n个元素。有没有比使用length()函数更好的方法?


84

如果出于参数考虑,我想要Python中10个长度的向量的最后五个元素,则可以在范围索引中使用“-”运算符,因此:

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

在R中执行此操作的最佳方法是什么?有没有一种比我当前使用长度()函数的方法更简洁的方法?

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

这个问题与时间序列分析有关,通常仅对最新数据有用。

Answers:


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在评论中所说,还有另外两种可能性比尾部解决方案要快,以防万一您必须对1亿个值的向量执行一百万次。为了便于阅读,我会选择尾巴。

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秒时间可以做什么...
Joris Meys

6
utils ::: tail.default的实现x[seq.int(to=length(x), length.out=5)]似乎比tail()没有健全性检查的速度快约10倍;x[length(x)-(4:0)]还是更快。
马丁·摩根

1
@Joris:我可以想像我在十亿次的内循环中运行特定的操作之后将如何处理它们。:-)关键是切片并没有那么清晰,而是更优化,所以总的来说会走那条路线。
尼克·巴斯汀,

6

您可以在R中使用另外两个字符来执行完全相同的操作:

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

要么

x[-(1:5)]

如果我不知道Vector的长度怎么办,但是我仍然总是想要最后5个元素怎么办?python版本仍然有效,但是您的R示例返回了最后15个元素,因此仍然需要调用length()吗?
托马斯·布朗

10
萨莎(Sacha),我认为您的回答不那么笼统。您的代码示例所做的是删除前5个结果,而不是保留后5个结果。在此示例中,是同一件事,但是以下操作无效: x <- 0:20; x[-5:-1]-这将返回最后的15个元素。
Andrie

我不了解python,但是在OP中x[-5:]:这是否意味着跳过前5个元素,或保留后5个元素?如果是第一个,那么他会间接使用您的长度,就像您在这里一样(否则,您如何知道要跳过哪些元素?)
Nick Sabbe 2011年

1
Python中的“-”运算符意味着倒数。因此,在这种情况下,它将始终返回最后5个元素。
Thomas Browne

2
嗯,对,我不了解python,并认为这意味着跳过前5个tail
2011年

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

尾巴将仅返回最大元素数,而不是生成错误,因此您无需自己进行任何错误检查。使用它的重要理由。如果使用额外的微秒/毫秒对您来说无关紧要,则代码更安全,更干净。


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的向量效果很好:)
Brian Davis

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与直接访问相比也非常慢

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.