ChatGPT解决这个技术问题 Extra ChatGPT

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

R 提供了最大值和最小值,但除了对整个向量进行排序然后从该向量中选择一个值 x 之外,我没有看到一种真正快速的方法来按顺序找到另一个值。

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

CRAN 上的 package kit 有一个 topn 函数,它比 sortordernth 更快。查看文档。
@Suresh_Patel 您能否提供针对 Rfast::nth 提供的示例对其进行基准测试的示例?如果与 Rfast::nth 相比它确实更快,那么它应该是公认的答案
@Stefanos,我在下面发布了基准...基于您的基准
我刚刚使用 kit::topn(hasna=F) 进行了第二次运行...我相信我现在提供了最佳答案,不是吗?

R
Rob Hyndman

使用 sort()partial 参数。对于第二高值:

n <- length(x)
sort(x,partial=n-1)[n-1]

除了不满足问题中的约束之外,这种方法与@Abrar 的回答中描述的 sort(x, TRUE)[2] 相比有什么优势?
我使用了这种方法,但收到以下错误:Error in sort.int(x, na.last = na.last, decreasing = decreasing, ...) : index 4705 outside bounds 知道可能是什么问题吗?一些细节:我的 x 是一个长度为 4706 的数字向量,数据中有一些 NA。我尝试使用与@RobHyndman 建议的完全相同的代码来获得向量中的第二高值。
递减参数与部分排序不兼容。
@sriramn 我知道你在 3.5 年前问过这个问题,但是这个解决方案不适用于缺失值,因为 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] 花费的时间要少得多。
P
Paolo

稍慢的替代方案,仅用于记录:

x <- c(12.45,34,4,0,-234,45.6,4)
max( x[x!=max(x)] )
min( x[x!=min(x)] )

如果这比对整个向量进行排序并取第 n-1 个值更快,这似乎令人惊讶!
@jwg 这是 O(n) 所以它必须比在大型数据集上排序更快。
在我看来,您只需稍作修改即可获得相当大的速度提升:max(x[-which.max(x)])
如果所有值都相同,则此答案会产生错误,除非您使用 @sindri_baldur 的答案(当然,至少有 2 个项目)
S
Stefanos

Rfast 有一个名为 nth_element 的函数,它完全按照您的要求执行。

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

更新 (28/FEB/21) 软件包套件提供更快的实施(topn)请参阅 https://stackoverflow.com/a/66367996/4729755https://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 大元素)以及这些元素的索引。
我喜欢 Rfast 解决方案的地方在于,该软件包还有一个易于实现的解决方案,可以为每一行或每一列执行此操作。
nth 中存在整数值错误。我知道,我会修复它以供将来更新包。现在您可以只使用 Rfast::nth(as.numeric(1:10), 2)。虽然,我真的不认为 Rfast::nth(1:10, 2) 是一个很好的例子。如果您有一个排序数组,为什么要使用 nth?检查它是否已排序然后提取值甚至更好地提取值本身要快得多。
Z
Zach

我将 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)
为什么不将 fx 中的主线设为 sort(x, reduction=T, partial=N)[N]?
D
Davit Sargsyan

这是在向量中查找 N 个最小/最大值索引的简单方法(N = 3 的示例):

N <- 3

N 最小:

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

N 最大:

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

因此,您可以将值提取为:

x[ndx]

这在 L log L 时间内运行,其中 L 是 x 的长度。我认为用户希望有一种在 log L 时间内运行的方法。
如果方法按时间排序并提取最快的 N,这可能是第二快的方法。我也喜欢它,因为与公认的解决方案相比,它的代码非常清晰。
理论上最好的和公认的方法(希望)在 O(L) 时间内运行,而不是 O(log L)。这个运行在 O(L log L) 中。
D
DaveShaw

对于第 n 个最高值,

sort(x, TRUE)[n]

OP 在他的帖子中已经说过,这是他不想使用的解决方案:“除了对整个向量进行排序以及从该向量中选择值 x ”。
方便,因为一个人可以轻松抓住三个(四个,无论如何)最高排序(x,TRUE)[1:3]
S
Suresh_Patel

给你...套件是明显的赢家!

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

V
Vin

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

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

J
John Jiang

我发现首先删除最大元素,然后以相当的速度进行另一个最大运行:

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 

N
Noale

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= 参数 (这会改变一切)
@BenBolker 暗示 Paolo 或 Rob 的答案可用于改进 dplyr::nth()bench::mark(max(x[-which.max(x)]), x[[order(-x)[[2]]]] )nth() 似乎慢了将近 10 倍,其中 length(x) 是 300 万。
D
Donarus

当我最近在寻找一个返回给定向量中前 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 个最大/最小值的索引) - 您可以在此处找到我在上面定义的类似函数的一些基准。


R
Robert

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


R
Ralph

这将找到输入数值向量 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)
}

v
vdc320
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 有所帮助


p
perror

您可以使用 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

我想你误解了这个问题。目标是找到第二高的值。这如何帮助您从 v 到 12... 并从第三高到 8?
a
alko989

您可以像这样使用 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 个最大数字。