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

Posted

技术标签:

【中文标题】获取向量的最后 n 个元素。有没有比使用 length() 函数更好的方法?【英文标题】:Getting the last n elements of a vector. Is there a better way than using the length() function? 【发布时间】:2011-09-02 10:58:54 【问题描述】:

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

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

这个问题与时间序列分析有关,顺便说一句,仅处理最近的数据通常很有用。

【问题讨论】:

【参考方案1】:

查看?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 在 cmets 中所说,还有两种比尾部解决方案更快的可能性,以防您必须在 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)
)

【讨论】:

但并不比切片快——测试证明了这一点。 谢谢尼克有趣。是的,Python 切片是该语言的一个不错的特性。 @Nick:确实。在长度为 1e6 和 1000 次复制的向量上,它慢了大约 0.3 秒。想象一下您可以用节省的 0.3 秒做什么...... utils:::tail.default 的实现是x[seq.int(to=length(x), length.out=5)],它似乎比tail() 快10 倍,但没有进行完整性检查; x[length(x)-(4:0)] 更快。 @Joris:我可以想象在我在内循环中运行该特定操作十亿次之后我会用它们做什么.. :-) 关键是切片并没有那么清晰,但更理想,所以总的来说我会走那条路。【参考方案2】:

你可以在 R 中用另外两个字符做同样的事情:

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

x[-(1:5)]

【讨论】:

如果我不知道 Vector 的长度,但我总是想要最后 5 个元素怎么办? python 版本仍然有效,但您的 R 示例返回最后 15 个元素,因此仍需要调用 length()? Sacha,我认为您的回答并不笼统。您的代码示例所做的是删除前 5 个结果,而不是保留最后五个结果。在此示例中,它是相同的,但以下内容不起作用:x &lt;- 0:20; x[-5:-1] - 这将返回最后 15 个元素。 我不知道python,但是在OP的x[-5:]:这意味着跳过前5个元素,还是保留最后5个?如果是第一个,他就间接地使用了你的长度,就像你一样,在这里(否则,你怎么知道要跳过哪些元素?) Python 中的“-”运算符表示倒数。所以在这种情况下它总是返回最后 5 个元素。 啊,对了,我不知道 python 并假设它意味着跳过前 5 个。tail 就是你想要的。【参考方案3】:

这是一个功能,看起来相当快。

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

【讨论】:

【参考方案4】:

我只是在这里添加一些相关的东西。我想访问带有后端索引的向量,即写类似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 与直接访问相比也非常慢

【讨论】:

【参考方案5】:

这里仅基于速度对tail 的不赞成似乎并没有真正强调速度较慢的部分原因是 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 将简单地返回元素的最大数量而不是生成错误,因此您不需要自己进行任何错误检查。使用它的一个很好的理由。更安全、更简洁的代码,如果额外的微秒/毫秒对您的使用无关紧要。

【讨论】:

【参考方案6】:

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 &lt;- 1:10e6 时计时 好点@ChrisNjuguna。使用长度为 10 的向量效果很好 :)

以上是关于获取向量的最后 n 个元素。有没有比使用 length() 函数更好的方法?的主要内容,如果未能解决你的问题,请参考以下文章

对已经排序了 n 个第一个元素的向量进行排序?

为啥 std::nth_element 返回 N < 33 个元素的输入向量的排序向量?

python十进制转二进制数组求解,急

从字符串中获取最后 10 个单词,应用于字符串向量

向量的所有元素都替换为最后一个对象-java

比 O(n) 更快地获取数组元素的索引