假设我有一个向量嵌套在一个或两个级别的数据框中。是否有一种快速而肮脏的方式来访问最后一个值,而不使用 length()
函数?一些 ala PERL 的 $#
特殊变量?
所以我想要类似的东西:
dat$vec1$vec2[$#]
代替
dat$vec1$vec2[length(dat$vec1$vec2)]
我使用 tail
函数:
tail(vector, n=1)
tail
的好处是它也适用于数据帧,这与 x[length(x)]
习语不同。
为了不是从美学而是从性能导向的角度来回答这个问题,我已经通过一个基准测试了上述所有建议。确切地说,我已经考虑了这些建议
x[长度(x)]
mylast(x),其中 mylast 是通过 Rcpp 实现的 C++ 函数,
尾(x,n=1)
dplyr::last(x)
x[结束(x)[1]]]
转(x)[1]
并将它们应用于各种大小(10^3、10^4、10^5、10^6 和 10^7)的随机向量。在我们查看这些数字之前,我认为应该清楚的是,任何随着输入大小变得明显变慢的东西(即任何不是 O(1) 的东西)都不是一种选择。这是我使用的代码:
Rcpp::cppFunction('double mylast(NumericVector x) { int n = x.size(); return x[n-1]; }')
options(width=100)
for (n in c(1e3,1e4,1e5,1e6,1e7)) {
x <- runif(n);
print(microbenchmark::microbenchmark(x[length(x)],
mylast(x),
tail(x, n=1),
dplyr::last(x),
x[end(x)[1]],
rev(x)[1]))}
它给了我
Unit: nanoseconds
expr min lq mean median uq max neval
x[length(x)] 171 291.5 388.91 337.5 390.0 3233 100
mylast(x) 1291 1832.0 2329.11 2063.0 2276.0 19053 100
tail(x, n = 1) 7718 9589.5 11236.27 10683.0 12149.0 32711 100
dplyr::last(x) 16341 19049.5 22080.23 21673.0 23485.5 70047 100
x[end(x)[1]] 7688 10434.0 13288.05 11889.5 13166.5 78536 100
rev(x)[1] 7829 8951.5 10995.59 9883.0 10890.0 45763 100
Unit: nanoseconds
expr min lq mean median uq max neval
x[length(x)] 204 323.0 475.76 386.5 459.5 6029 100
mylast(x) 1469 2102.5 2708.50 2462.0 2995.0 9723 100
tail(x, n = 1) 7671 9504.5 12470.82 10986.5 12748.0 62320 100
dplyr::last(x) 15703 19933.5 26352.66 22469.5 25356.5 126314 100
x[end(x)[1]] 13766 18800.5 27137.17 21677.5 26207.5 95982 100
rev(x)[1] 52785 58624.0 78640.93 60213.0 72778.0 851113 100
Unit: nanoseconds
expr min lq mean median uq max neval
x[length(x)] 214 346.0 583.40 529.5 720.0 1512 100
mylast(x) 1393 2126.0 4872.60 4905.5 7338.0 9806 100
tail(x, n = 1) 8343 10384.0 19558.05 18121.0 25417.0 69608 100
dplyr::last(x) 16065 22960.0 36671.13 37212.0 48071.5 75946 100
x[end(x)[1]] 360176 404965.5 432528.84 424798.0 450996.0 710501 100
rev(x)[1] 1060547 1140149.0 1189297.38 1180997.5 1225849.0 1383479 100
Unit: nanoseconds
expr min lq mean median uq max neval
x[length(x)] 327 584.0 1150.75 996.5 1652.5 3974 100
mylast(x) 2060 3128.5 7541.51 8899.0 9958.0 16175 100
tail(x, n = 1) 10484 16936.0 30250.11 34030.0 39355.0 52689 100
dplyr::last(x) 19133 47444.5 55280.09 61205.5 66312.5 105851 100
x[end(x)[1]] 1110956 2298408.0 3670360.45 2334753.0 4475915.0 19235341 100
rev(x)[1] 6536063 7969103.0 11004418.46 9973664.5 12340089.5 28447454 100
Unit: nanoseconds
expr min lq mean median uq max neval
x[length(x)] 327 722.0 1644.16 1133.5 2055.5 13724 100
mylast(x) 1962 3727.5 9578.21 9951.5 12887.5 41773 100
tail(x, n = 1) 9829 21038.0 36623.67 43710.0 48883.0 66289 100
dplyr::last(x) 21832 35269.0 60523.40 63726.0 75539.5 200064 100
x[end(x)[1]] 21008128 23004594.5 37356132.43 30006737.0 47839917.0 105430564 100
rev(x)[1] 74317382 92985054.0 108618154.55 102328667.5 112443834.0 187925942 100
这立即排除了涉及 rev
或 end
的任何内容,因为它们显然不是 O(1)
(并且结果表达式以非惰性方式评估)。 tail
和 dplyr::last
与 O(1)
相差不远,但它们也比 mylast(x)
和 x[length(x)]
慢得多。由于 mylast(x)
比 x[length(x)]
慢并且没有任何好处(相反,它是自定义的并且不能优雅地处理空向量),我认为答案很明确:请使用 x[length(x)]
。
mylastR=function(x) {x[length(x)}
比 Rcpp 中的 mylast
快,但比直接写 x[length(x)]
慢一倍
如果您正在寻找与 Python 的 x[-1] 表示法一样好的东西,我认为您不走运。标准的成语是
x[length(x)]
但是编写一个函数来做到这一点很容易:
last <- function(x) { return( x[length(x)] ) }
R 中这个缺失的功能也让我很恼火!
x[length(x)-0:3]
来获取 x
的最后四个元素。
结合 lindelof's 和 Gregg Lind's 的想法:
last <- function(x) { tail(x, n = 1) }
在提示符下工作,我通常省略 n=
,即 tail(x, 1)
。
与 pastecs
包中的 last
不同,head
和 tail
(来自 utils
)不仅适用于矢量,还适用于数据帧等,并且还可以返回数据“没有第一个/最后一个n 个元素”,例如
but.last <- function(x) { head(x, n = -1) }
(请注意,您必须为此使用 head
,而不是 tail
。)
x[length(x)]
平均慢 30 倍!
dplyr 包包含一个函数 last()
:
last(mtcars$mpg)
# [1] 21.4
x[[length(x)]]
。
last()
并将该函数存储在某处,就像上面几个人所做的那样。您可以提高函数的可读性,它的可移植性来自 CRAN,以便其他人可以运行代码。
mtcars$mpg %>% last
,具体取决于您的偏好。
x[[length(x)]]
慢得多!
我刚刚使用以下代码在具有 663,552 行的数据帧上对这两种方法进行了基准测试:
system.time(
resultsByLevel$subject <- sapply(resultsByLevel$variable, function(x) {
s <- strsplit(x, ".", fixed=TRUE)[[1]]
s[length(s)]
})
)
user system elapsed
3.722 0.000 3.594
和
system.time(
resultsByLevel$subject <- sapply(resultsByLevel$variable, function(x) {
s <- strsplit(x, ".", fixed=TRUE)[[1]]
tail(s, n=1)
})
)
user system elapsed
28.174 0.000 27.662
因此,假设您正在使用向量,访问长度位置的速度要快得多。
tail(strsplit(x,".",fixed=T)[[1]],1)
?对我来说,tail
的主要优点是您可以将它写在一行中。 ;)
另一种方法是取反向向量的第一个元素:
rev(dat$vect1$vec2)[1]
[1]
访问第一个元素,并且 (2) 虽然您可以将 rev
应用于迭代器,但它的行为与预期不同:它只是将迭代器对象视为其成员的列表并将其反转。
我有另一种查找向量中最后一个元素的方法。假设向量是a
。
> a<-c(1:100,555)
> end(a) #Gives indices of last and first positions
[1] 101 1
> a[end(a)[1]] #Gives last element in a vector
[1] 555
给你!
包 data.table
包括 last
功能
library(data.table)
last(c(1:10))
# [1] 10
x[[length(x)]]
。
关于什么
> a <- c(1:100,555)
> a[NROW(a)]
[1] 555
NROW
在许多不同的数据类型上的表现符合您的预期,但它与 OP 希望避免的 a[length(a)]
基本相同。使用 OP 的嵌套向量示例,dat$vec1$vec2[NROW(dat$vec1$vec2)]
仍然很混乱。
nrow
nrow
不同,NROW
将向量视为 1 列矩阵。
xts 包提供 last
函数:
library(xts)
a <- 1:100
last(a)
[1] 100
不定期副业成功案例分享
x[length(x[,1]),]
的建议没有错(注意x
子集中的逗号),但它肯定很尴尬。x[length(x)]
平均慢 30 倍!tail(vector, n=1)-tail(vector, n=2)
从向量中添加内容,则不起作用