Найшвидший спосіб знайти друге (третє…) найвище / найменше значення у векторі чи стовпці


161

R пропонує max та min, але я не бачу дійсно швидкого способу знайти інше значення в порядку, крім сортування всього вектора та вибору значення x з цього вектора.

Чи є більш швидкий спосіб отримати, наприклад, друге найвище значення?


У комплект пакета на CRAN має topnфункцію , яка швидше sort, orderі nth. Подивіться на документацію.
Суреш_Патель

Відповіді:


195

Скористайтеся partialаргументом sort(). Для другого найвищого значення:

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

4
Яка перевага цього методу на відміну від sort(x, TRUE)[2]описаного у відповіді @ Абрара, окрім того, що він не задовольняє обмеженню у питанні?
Х'ю,

5
Я використав цей метод, але отримав таку помилку: Error in sort.int(x, na.last = na.last, decreasing = decreasing, ...) : index 4705 outside bounds Будь-яке уявлення про те, що може бути проблемою? Деякі деталі: My x - числовий вектор довжиною 4706 з деякими NAs в даних. Я намагався отримати друге найвище значення у векторі, використовуючи той самий код, що і @RobHyndman.
sriramn

Чому ви не сортуєте низхідний і не приймете друге з лише двох значень? Чи не було б це швидше?
jwg

3
Аргумент зменшення не сумісний з частковим сортуванням.
Роб Хайндман

7
Хоча decreasingаргумент не сумісний з частковим сортуванням, ви завжди могли -sort(-x, partial=n-1)[n-1]; логічно це те саме і займає значно менше часу, ніж sort(x, decreasing=TRUE)[n-1].
r2evans

52

Трохи повільніша альтернатива, лише для записів:

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

Здавалося б, дивно, якби це було швидше, ніж сортування всього вектора та прийняття n-1-го значення!
jwg

@jwg Це O (n), тому це має бути швидшим, ніж сортування на великих наборах даних.
Помірно

Працює краще з NA, ніж інша прийнята відповідь - просто використовуйте 'na.rm = TRUE' як аргумент для функції 'min'.
Yair Daon

2
Мені здається, ви можете досягти значного покращення швидкості з невеликою модифікацією:max(x[-which.max(x)])
sindri_baldur

31

Я переклав відповідь Роба на трохи більш загальну функцію, яку можна використовувати для пошуку 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)

1
Класно. Це використання особливо корисне maxN(1:10, 1:3)(я б встановив N за замовчуванням 1)
PatrickT

23

Rfast має функцію під назвою nth_element, яка робить саме те, що ви запитуєте, і швидше, ніж усі реалізовані вище реалізації

Також описані вище методи, засновані на частковому сортуванні, не підтримують знаходження k найменших значень

Rfast::nth(x, 5, descending = T)

Поверне 5-й за величиною елемент x, в той час

Rfast::nth(x, 5, descending = F)

Повернеться 5-й найменший елемент x

Нижче наведено орієнтири проти найбільш популярних відповідей.

Для 10 тис. Номерів:

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 #evaluates to 1 million
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

8
Приємно! Зазвичай, коли я бачу, що користувач із відносно низьким рівнем відгуку додає відповідь на старе популярне запитання, це досить низька якість. З іншого боку, це відмінне доповнення. Я зробив кілька змін для читання, але це виглядає чудово!
Грегор Томас

3
Тут згадується, що Rfast::nthможе повертатися декілька елементів (наприклад, 8-й та 9-й за величиною елементи), а також індекси цих елементів.
Яша

3
Що мені подобається у рішенні Rfast, це те, що пакет також має легко реалізоване рішення для цього для кожного рядка чи стовпця.
Джей

16

Ось простий спосіб знайти індекси 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. Я думаю, що користувач сподівався на метод, який працює в журналі L час.
армат

Це може бути другим найшвидшим способом, якби методи були впорядковані часом і найшвидше витягнуто N. Мені це також подобається, тому що це дуже чіткий код у порівнянні з прийнятим рішенням.
Піт

1
Теоретичний найкращий і прийнятий метод (сподіваємось) працює в О (L) час, а не О (лог L). Цей працює в O (L log L).
Валентас

6

Для n-го найвищого значення,

sort(x, TRUE)[n]

8
ОП вже говорив у своєму дописі, що це рішення, яке він не хоче використовувати: "крім сортування всього вектора і ніж вибору значення x з цього вектора".
Пол Хіемстра

3

Я виявив, що спочатку витягніть максимальний елемент, а потім виконайте ще один макс-біг із порівнянною швидкістю:

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 

2

Ось найпростіший спосіб, який я знайшов,

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

1

Коли я нещодавно шукав функцію R, що повертає індекси верхніх N max / min чисел у заданому векторі, я був здивований, що такої функції немає.

І це щось дуже схоже.

Рішення з грубою силою за допомогою функції base :: order здається найпростішим.

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

Але це не найшвидший випадок, якщо ваше N значення порівняно невелике порівняно з довжиною вектора x .

З іншого боку, якщо N дійсно малий, ви можете використовувати base :: thatMax ітеративно функціонувати, і в кожній ітерації ви можете замінити знайдене значення на -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]
}

Тоді ви можете вибрати останній ( N- й) елемент з результатів функцій, що захищаються вище.

Примітка: функції, визначені вище, - лише приклади - якщо ви хочете їх використовувати, ви повинні перевірити / вводити дані (наприклад, N> довжина (x) ).

Я написав невелику статтю про щось дуже схоже (отримати індекси верхніх N max / min значень вектора) на http://palusga.cz/?p=18 - ви можете знайти тут деякі орієнтири подібних функцій, які я визначив вище.



0
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


0

Це знайде індекс N-го найменшого або найбільшого значення у вхідному числовому векторі x. Встановіть низ = ІСТИНА в аргументах, якщо ви хочете N'th знизу, або знизу = FALSE, якщо ви хочете N'th зверху. N = 1 і низ = TRUE еквівалентно, який.min, N = 1 і нижній = FALSE еквівалентний, який.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)
}

0

dplyr має функцію nth, де перший аргумент - вектор, а другий - яке місце ви хочете. Це стосується і повторюваних елементів. Наприклад:

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

Пошук другого за величиною значення:

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

[1] 17

2
це швидко ...?
Бен Болкер

2
всередині цього використовується x[[order(order_by)[[n]]]]- тому воно вимагає сортування всього вектора. Тож це буде не так швидко, як прийнята відповідь.
Бен Болкер

5
але він використовує sort аргумент частковий = (який змінює все)
Бен Болкер

@BenBolker, що означає відповідь Паоло чи Роба, може бути використаний для покращення dplyr::nth()? bench::mark(max(x[-which.max(x)]), x[[order(-x)[[2]]]] ), nth()здається майже в 10 разів повільніше, де length(x)3 мільйони.
sindri_baldur

-1

Ви можете ідентифікувати наступне вище значення за допомогою 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?
Франк

-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 максимальних чисел.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.