在矢量或列中找到第二(第三...)最高/最低值的最快方法

R 提供了 max 和 min,但是我没有看到一个真正快速的方法来找到顺序中的另一个值,除了对整个向量进行排序,然后从这个向量中挑选一个值 x。

例如,有没有更快的方法获得第二高的值?

247935 次浏览

使用 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)] )

第 N 高的价值,

sort(x, TRUE)[n]

这里有一个简单的方法可以找到向量中 N 个最小/最大值的索引(例如 N = 3) :

N <- 3

N 最小:

ndx <- order(x)[1:N]

N 最大:

ndx <- order(x, decreasing = T)[1:N]

因此可以提取如下值:

x[ndx]

我发现,先去掉 max 元素,然后再以相同的速度运行另一个 max:

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

我把 Rob 的答案包装成一个稍微通用一点的函数,它可以用来找到第二、第三、第四(等等) max:

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)

当我最近在寻找一个返回给定向量上 N 个最大/最小数的索引的 R函数时,我很惊讶没有这样一个函数。

这是非常相似的东西。

使用 基础: : 秩序函数的蛮力解决方案似乎是最简单的一个。

topMaxUsingFullSort <- function(x, N) {
sort(x, decreasing = TRUE)[1:min(N, length(x))]
}

但是它并不是最快的,因为你的 N值相对于矢量 X的长度来说是相对较小的。

另一方面,如果 N非常小,则可以迭代地使用 麦克斯函数,并且在每次迭代中可以用 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中最好的解决方案是使用部分 基础: : 排序

topMaxUsingPartialSort <- function(x, N) {
N <- min(N, length(x))
x[x >= -sort(-x, partial=N)[N]][1:N]
}

然后您可以从上面的函数调用结果中选择最后一项(Nth)。

注意: 上面定义的函数只是一些例子-如果你想使用它们,你必须检查/理智的输入(例如。N > 长度(x)).

我在 http://palusga.cz/?p=18上写过一篇类似的文章(获取向量的最大 N/min 值的索引)——你可以在这里找到我上面定义的类似函数的一些基准。

head(sort(x),..)tail(sort(x),...)应该可以

您可以使用 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
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 个值及其索引的矩阵。 希望能有所帮助 周德维

您可以像下面这样使用 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个最大号码。

这将在输入数值向量 x 中找到 N 次最小或最大值的索引。如果你想从底部得到 N 次方,那么在参数中设置 bottom = TRUE; 如果你想从顶部得到 N 次方,那么在参数中设置 bottom = FALSE。N = 1,bottom = TRUE 等于 which. min,N = 1,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)
}

Dplyr 有函数 nth,其中第一个参数是向量,第二个参数是您想要的位置。这也适用于重复元素。 例如:

x = c(1,2, 8, 16, 17, 20, 1, 20)

找到第二大价值:

 nth(unique(x),length(unique(x))-1)


[1] 17

Rfast 有一个名为 nth _ element 的函数,该函数完全满足您的要求。

此外,上面讨论的基于部分排序的方法不支持查找 k最小的

更新(28/FEB/21) 软件包工具包提供了一个更快的实现(topn)参见 https://stackoverflow.com/a/66367996/4729755,< a href = “ https://stackoverflow. com/a/53146559/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个最小元素

下面是针对最流行答案的基准。

一万个号码:

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

1个 百万号码:

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

这是我找到的最简单的方法,

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

给你... Kit 显然是赢家!

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::topnhasna选项... 让我们做另一个运行。

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