Самый быстрый способ найти * индекс * второго (третьего...) наивысшего/наименьшего значения в векторе или столбце

Самый быстрый способ найти индекс второго (третьего...) наивысшего/наименьшего значения в векторе или столбце?

то есть. то, что

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

имеет значение

max()

но для

which.max()

Бест,

Самый быстрый способ найти второе (третье...) наивысшее/наименьшее значение в векторе или столбце

Ответ 1

В библиотеке Rfast реализована функция n-го элемента с опцией return index, которая работает быстрее, чем все другие обсуждаемые реализации.

x <- runif(1e+6)

ind <- 2

which_nth_highest_richie <- function(x, n)
{
  for(i in seq_len(n - 1L)) x[x == max(x)] <- -Inf
  which(x == max(x))
}

which_nth_highest_joris <- function(x, n)
{
  ux <- unique(x)
  nux <- length(ux)
  which(x == sort(ux, partial = nux - n + 1)[nux - n + 1])
} 

microbenchmark::microbenchmark(
        Rfast = Rfast::nth(x,ind,descending = T,index.return = T),
        order = order(x, decreasing = TRUE)[ind],
        richie = which_nth_highest_richie(x,ind),
        joris = which_nth_highest_joris(x,ind))

Unit: milliseconds
          expr       min        lq      mean    median        uq      max   neval
         Rfast  22.89945  26.03551  31.61163  26.70668  32.07650 105.0016   100
         order 113.54317 116.49898 122.97939 119.44496 124.63646 170.4589   100
        richie  26.69556  27.93143  38.74055  36.16341  44.10246 116.7192   100
         joris 126.52276 138.60153 151.49343 146.55747 155.60709 324.8605   100 

Ответ 2

Один из возможных путей - использовать аргумент index.return для sort. Я не уверен, что это быстрее.

set.seed(21)
x <- rnorm(10)
ind <- 2
sapply(sort(x, index.return=TRUE), `[`, length(x)-ind+1)
#        x       ix 
# 1.746222 3.000000

Ответ 3

ИЗМЕНИТЬ 2:

Как отметил Джошуа, ни одно из данных решений на самом деле не работает правильно, если у вас есть связь с максимумами, поэтому:

X <- c(11:19,19)

n <- length(unique(X))
which(X == sort(unique(X),partial=n-1)[n-1])

самый быстрый способ сделать это правильно. Я удалил способ заказа, так как он не работает и работает намного медленнее, поэтому не очень хороший ответ в соответствии с OP.

Чтобы указать на проблему, мы столкнулись с ней:

> X <- c(11:19,19)    
> n <- length(X)
> which(X == sort(X,partial=n-1)[n-1])
[1]  9 10 #which is the indices of the double maximum 19

> n <- length(unique(X))
> which(X == sort(unique(X),partial=n-1)[n-1])
[1] 8 # which is the correct index of 18

Время правильных решений:

> x <- runif(1000000)

> ind <- 2

> n <- length(unique(x))

> system.time(which(x == sort(unique(x),partial=n-ind+1)[n-ind+1]))
   user  system elapsed 
   0.11    0.00    0.11 

> system.time(sapply(sort(unique(x), index.return=TRUE), `[`, n-ind+1))
   user  system elapsed 
   0.69    0.00    0.69 

Ответ 4

Метод: установите все максимальные значения -Inf, затем найдите индексы макс. Не требуется сортировка.

X <- runif(1e7)
system.time(
{
  X[X == max(X)] <- -Inf
  which(X == max(X))
})

Работает со связками и очень быстро.

Если вы не можете гарантировать связь, тогда еще более быстрая версия

system.time(
{
  X[which.max(X)] <- -Inf
  which.max(X)
})

EDIT: Как отметил Йорис, этот метод не масштабирует эту скважину для поиска третьих, четвертых и т.д. самых высоких значений.

which_nth_highest_richie <- function(x, n)
{
  for(i in seq_len(n - 1L)) x[x == max(x)] <- -Inf
  which(x == max(x))
}

which_nth_highest_joris <- function(x, n)
{
  ux <- unique(x)
  nux <- length(ux)
  which(x == sort(ux, partial = nux - n + 1)[nux - n + 1])
}

Используя x <- runif(1e7) и n = 2, Ричи выигрывает

system.time(which_nth_highest_richie(x, 2))   #about half a second
system.time(which_nth_highest_joris(x, 2))    #about 2 seconds

Для n = 100 Джорис побеждает

system.time(which_nth_highest_richie(x, 100)) #about 20 seconds, ouch! 
system.time(which_nth_highest_joris(x, 100))  #still about 2 seconds

Точка баланса, где они занимают один и тот же отрезок времени, составляет n = 10.

Ответ 5

Нет связей which(), вероятно, ваш друг здесь. Объедините вывод из решения sort() с which(), чтобы найти индекс, который соответствует выходу с шага sort().

> set.seed(1)
> x <- sample(1000, 250)
> sort(x,partial=n-1)[n-1]
[1] 992
> which(x == sort(x,partial=n-1)[n-1])
[1] 145

Обработка связей. Решение выше не работает должным образом (и не предназначалось), если существуют связи, а связи - это значения, которые являются i-м большим или большим значением. Нам нужно взять уникальные значения вектора перед сортировкой этих значений, а затем работает следующее решение:

> set.seed(1)
> x <- sample(1000, 1000, replace = TRUE)
> length(unique(x))
[1] 639
> n <- length(x)
> i <- which(x == sort(x,partial=n-1)[n-1])
> sum(x > x[i])
[1] 0
> x.uni <- unique(x)
> n.uni <- length(x.uni)
> i <- which(x == sort(x.uni, partial = n.uni-1)[n.uni-1])
> sum(x > x[i])
[1] 2
> tail(sort(x))
[1]  994  996  997  997 1000 1000

order() также очень полезен здесь:

> head(ord <- order(x, decreasing = TRUE))
[1] 220 145 209 202 211 163

Итак, решение здесь ord[2] для индекса второго наивысшего/наибольшего элемента x.

Некоторые тайминги:

> set.seed(1)
> X <- sample(1e7, 1e7)
> system.time({n <- length(X); which(X == sort(X, partial = n-1)[n-1])})
   user  system elapsed 
  0.319   0.058   0.378 
> system.time({ord <- order(X, decreasing = TRUE); ord[2]})
   user  system elapsed 
 14.578   0.084  14.708 
> system.time({order(X, decreasing = TRUE)[2]})
   user  system elapsed 
 14.647   0.084  14.779

Но по мере того, как связанный пост проходил, и показанные выше тайминги, order() работает намного медленнее, но оба дают одинаковые результаты:

> all.equal(which(X == sort(X, partial = n-1)[n-1]), 
+           order(X, decreasing = TRUE)[2])
[1] TRUE

И для версии обработки связей:

foo <- function(x, i) {
    X <- unique(x)
    N <- length(X)
    i <- i-1
    which(x == sort(X, partial = N-i)[N-i])
}

> system.time(foo(X, 2))
   user  system elapsed 
  1.249   0.176   1.454

Таким образом, дополнительные шаги немного замедляют это решение, но он по-прежнему очень конкурентоспособен с order().

Ответ 6

Используйте функцию maxN, заданную Заком, чтобы найти следующее максимальное значение и используйте which() с arr.ind = TRUE.

который (x == maxN (x, 4), arr.ind = TRUE)

Использование arr.ind также вернет позицию индекса в любом из вышеперечисленных решений и упростит код.

Ответ 7

Это мое решение для поиска индекса верхнего N наивысшего значения в векторе (не совсем то, что хотел OP, но это могло бы помочь другим людям)

index.top.N = function(xs, N=10){
    if(length(xs) > 0) {
    o = order(xs, na.last=FALSE)
    o.length = length(o)
    if (N > o.length) N = o.length
    o[((o.length-N+1):o.length)]
  }
  else {
    0
  }
}