R 提供了最大值和最小值,但除了对整个向量进行排序然后从该向量中选择一个值 x 之外,我没有看到一种真正快速的方法来按顺序找到另一个值。
例如,有没有更快的方法来获得第二高的价值?
topn
函数,它比 sort
、order
和 nth
更快。查看文档。
kit::topn(hasna=F)
进行了第二次运行...我相信我现在提供了最佳答案,不是吗?
使用 sort()
的 partial
参数。对于第二高值:
n <- length(x)
sort(x,partial=n-1)[n-1]
稍慢的替代方案,仅用于记录:
x <- c(12.45,34,4,0,-234,45.6,4)
max( x[x!=max(x)] )
min( x[x!=min(x)] )
max(x[-which.max(x)])
Rfast 有一个名为 nth_element 的函数,它完全按照您的要求执行。
此外,上面讨论的基于部分排序的方法不支持查找 k 个最小值
更新 (28/FEB/21) 软件包套件提供更快的实施(topn)请参阅 https://stackoverflow.com/a/66367996/4729755、https://stackoverflow.com/a/53146559/4729755
免责声明:处理整数时似乎会出现一个问题,可以通过使用 as.numeric 绕过(例如 Rfast::nth(as.numeric(1:10), 2)),并将在 Rfast 的下一次更新中解决.
Rfast::nth(x, 5, descending = T)
将返回 x 的第 5 大元素,而
Rfast::nth(x, 5, descending = F)
将返回 x 的第 5 个最小元素
下面针对最受欢迎的答案进行基准测试。
对于 10,000 个数字:
N = 10000
x = rnorm(N)
maxN <- function(x, N=2){
len <- length(x)
if(N>len){
warning('N greater than length(x). Setting N=length(x)')
N <- length(x)
}
sort(x,partial=len-N+1)[len-N+1]
}
microbenchmark::microbenchmark(
Rfast = Rfast::nth(x,5,descending = T),
maxn = maxN(x,5),
order = x[order(x, decreasing = T)[5]])
Unit: microseconds
expr min lq mean median uq max neval
Rfast 160.364 179.607 202.8024 194.575 210.1830 351.517 100
maxN 396.419 423.360 559.2707 446.452 487.0775 4949.452 100
order 1288.466 1343.417 1746.7627 1433.221 1500.7865 13768.148 100
对于 100 万个数字:
N = 1e6
x = rnorm(N)
microbenchmark::microbenchmark(
Rfast = Rfast::nth(x,5,descending = T),
maxN = maxN(x,5),
order = x[order(x, decreasing = T)[5]])
Unit: milliseconds
expr min lq mean median uq max neval
Rfast 89.7722 93.63674 114.9893 104.6325 120.5767 204.8839 100
maxN 150.2822 207.03922 235.3037 241.7604 259.7476 336.7051 100
order 930.8924 968.54785 1005.5487 991.7995 1031.0290 1164.9129 100
Rfast::nth
可以返回多个元素(例如,第 8 和第 9 大元素)以及这些元素的索引。
nth
中存在整数值错误。我知道,我会修复它以供将来更新包。现在您可以只使用 Rfast::nth(as.numeric(1:10), 2)
。虽然,我真的不认为 Rfast::nth(1:10, 2)
是一个很好的例子。如果您有一个排序数组,为什么要使用 nth
?检查它是否已排序然后提取值甚至更好地提取值本身要快得多。
我将 Rob 的答案包装成一个更通用的函数,可用于查找第 2、第 3、第 4(等)最大值:
maxN <- function(x, N=2){
len <- length(x)
if(N>len){
warning('N greater than length(x). Setting N=length(x)')
N <- length(x)
}
sort(x,partial=len-N+1)[len-N+1]
}
maxN(1:10)
maxN(1:10, 1:3)
(我会将默认 N 设置为 1)
这是在向量中查找 N 个最小/最大值索引的简单方法(N = 3 的示例):
N <- 3
N 最小:
ndx <- order(x)[1:N]
N 最大:
ndx <- order(x, decreasing = T)[1:N]
因此,您可以将值提取为:
x[ndx]
对于第 n 个最高值,
sort(x, TRUE)[n]
给你...套件是明显的赢家!
N = 1e6
x = rnorm(N)
maxN <- function(x, N=2){
len <- length(x)
if(N>len){
warning('N greater than length(x). Setting N=length(x)')
N <- length(x)
}
sort(x,partial=len-N+1)[len-N+1]
}
microbenchmark::microbenchmark(
Rfast = Rfast::nth(x,5,descending = T),
maxN = maxN(x,5),
order = x[order(x, decreasing = T)[5]],
kit = x[kit::topn(x, 5L,decreasing = T)[5L]]
)
# Unit: milliseconds
# expr min lq mean median uq max neval
# Rfast 12.311168 12.473771 16.36982 12.702134 16.110779 102.749873 100
# maxN 12.922118 13.124358 17.49628 18.977537 20.053139 28.928694 100
# order 50.443100 50.926975 52.54067 51.270163 52.323116 66.561606 100
# kit 1.177202 1.216371 1.29542 1.240228 1.297286 2.771715 100
编辑:我忘记了 kit::topn
有 hasna
选项...让我们再运行一次。
microbenchmark::microbenchmark(
Rfast = Rfast::nth(x,5,descending = T),
maxN = maxN(x,5),
order = x[order(x, decreasing = T)[5]],
kit = x[kit::topn(x, 5L,decreasing = T)[5L]],
kit2 = x[kit::topn(x, 5L,decreasing = T,hasna = F)[5L]],
unit = "ms"
)
# Unit: milliseconds
# expr min lq mean median uq max neval
# Rfast 13.194314 13.358787 14.7227116 13.4560340 14.551194 24.524105 100
# maxN 7.378960 7.527661 10.0747803 7.7119715 12.217756 67.409526 100
# order 50.088927 50.488832 52.4714347 50.7415680 52.267003 70.062662 100
# kit 1.180698 1.217237 1.2975441 1.2429790 1.278243 3.263202 100
# kit2 0.842354 0.876329 0.9398055 0.9109095 0.944407 2.135903 100
这是我找到的最简单的方法,
num <- c(5665,1615,5154,65564,69895646)
num <- sort(num, decreasing = F)
tail(num, 1) # Highest number
head(tail(num, 2),1) # Second Highest number
head(tail(num, 3),1) # Third Highest number
head(tail(num, n),1) # Generl equation for finding nth Highest number
我发现首先删除最大元素,然后以相当的速度进行另一个最大运行:
system.time({a=runif(1000000);m=max(a);i=which.max(a);b=a[-i];max(b)})
user system elapsed
0.092 0.000 0.659
system.time({a=runif(1000000);n=length(a);sort(a,partial=n-1)[n-1]})
user system elapsed
0.096 0.000 0.653
dplyr 具有函数 nth,其中第一个参数是向量,第二个参数是您想要的位置。这也适用于重复元素。例如:
x = c(1,2, 8, 16, 17, 20, 1, 20)
求第二大值:
nth(unique(x),length(unique(x))-1)
[1] 17
x[[order(order_by)[[n]]]]
- 所以它需要对整个向量进行排序。所以它不会像接受的答案那么快。
sort
与 partial= 参数 (这会改变一切)
dplyr::nth()
? bench::mark(max(x[-which.max(x)]), x[[order(-x)[[2]]]] )
,nth()
似乎慢了将近 10 倍,其中 length(x)
是 300 万。
当我最近在寻找一个返回给定向量中前 N 个最大/最小数字的索引的 R 函数时,我很惊讶没有这样的函数。
这是非常相似的事情。
使用 base::order 函数的蛮力解决方案似乎是最简单的解决方案。
topMaxUsingFullSort <- function(x, N) {
sort(x, decreasing = TRUE)[1:min(N, length(x))]
}
但如果您的 N 值与向量 x 的长度相比相对较小,它并不是最快的。
另一方面,如果 N 真的很小,您可以迭代地使用 base::whichMax 函数,并且在每次迭代中,您可以用 -Inf 替换找到的值
# the input vector 'x' must not contain -Inf value
topMaxUsingWhichMax <- function(x, N) {
vals <- c()
for(i in 1:min(N, length(x))) {
idx <- which.max(x)
vals <- c(vals, x[idx]) # copy-on-modify (this is not an issue because idxs is relative small vector)
x[idx] <- -Inf # copy-on-modify (this is the issue because data vector could be huge)
}
vals
}
我相信您看到了问题 - R 的修改时复制性质。因此,对于非常非常小的 N (1,2,3),这将表现得更好,但对于较大的 N 值,它会迅速减慢。您正在迭代向量 x N 中的所有元素。
我认为干净 R 中的最佳解决方案是使用部分 base::sort。
topMaxUsingPartialSort <- function(x, N) {
N <- min(N, length(x))
x[x >= -sort(-x, partial=N)[N]][1:N]
}
然后你可以从上面defiend函数的结果中选择最后一个(第N个)项。
注意:上面定义的函数只是示例——如果你想使用它们,你必须检查/健全输入(例如 N > length(x))。
我在 http://palusga.cz/?p=18 写了一篇关于非常相似的小文章(获取向量的前 N 个最大/最小值的索引) - 您可以在此处找到我在上面定义的类似函数的一些基准。
head(sort(x),..)
或 tail(sort(x),...)
应该可以工作
这将找到输入数值向量 x 中第 N 个最小值或最大值的索引。如果您想要从底部开始的第 N 个,则在参数中设置 bottom=TRUE,如果您想要从顶部开始的第 N 个,则设置 bottom=FALSE。 N=1 and bottom=TRUE 等价于 which.min,N=1 and bottom=FALSE 等价于 which.max。
FindIndicesBottomTopN <- function(x=c(4,-2,5,-77,99),N=1,bottom=FALSE)
{
k1 <- rank(x)
if(bottom==TRUE){
Nindex <- which(k1==N)
Nindex <- Nindex[1]
}
if(bottom==FALSE){
Nindex <- which(k1==(length(x)+1-N))
Nindex <- Nindex[1]
}
return(Nindex)
}
topn = function(vector, n){
maxs=c()
ind=c()
for (i in 1:n){
biggest=match(max(vector), vector)
ind[i]=biggest
maxs[i]=max(vector)
vector=vector[-biggest]
}
mat=cbind(maxs, ind)
return(mat)
}
此函数将返回一个包含前 n 个值及其索引的矩阵。希望对 VDevi-Chou 有所帮助
您可以使用 cummax()
确定下一个更高的值。例如,如果您想要每个新的较高值的位置,您可以将 cummax()
值的向量传递给 diff()
函数,以识别 cummax()
值发生变化的位置。说我们有向量
v <- c(4,6,3,2,-5,6,8,12,16)
cummax(v) will give us the vector
4 6 6 6 6 6 8 12 16
现在,如果您想在 cummax()
中找到更改的位置,您有很多我倾向于使用 sign(diff(cummax(v)))
的选项。由于 diff()
,您必须调整丢失的第一个元素。向量 v
的完整代码为:
which(sign(diff(cummax(v)))==1)+1
您可以像这样使用 sort
关键字:
sort(unique(c))[1:N]
例子:
c <- c(4,2,44,2,1,45,34,2,4,22,244)
sort(unique(c), decreasing = TRUE)[1:5]
将给出前 5 个最大数字。
不定期副业成功案例分享
sort(x, TRUE)[2]
相比有什么优势?Error in sort.int(x, na.last = na.last, decreasing = decreasing, ...) : index 4705 outside bounds
知道可能是什么问题吗?一些细节:我的 x 是一个长度为 4706 的数字向量,数据中有一些NA
。我尝试使用与@RobHyndman 建议的完全相同的代码来获得向量中的第二高值。sort
删除了缺失值。一种解决方法是n <- sum(!is.na(x)); sort(x,partial=n-1)[n-1]
decreasing
参数与部分排序不兼容,但您始终可以-sort(-x, partial=n-1)[n-1]
;它在逻辑上是相同的,并且比sort(x, decreasing=TRUE)[n-1]
花费的时间要少得多。