Vektör veya sütundaki ikinci (üçüncü…) en yüksek / en düşük değeri bulmanın en hızlı yolu


161

R, max ve min sunuyor, ancak tüm vektörü sıralamak ve daha sonra bu vektörden x değeri seçmek dışında, sırayla başka bir değer bulmak için gerçekten hızlı bir yol görmüyorum.

Örneğin, en yüksek ikinci değeri almanın daha hızlı bir yolu var mı?


CRAN üzerindeki paket seti vardır topndaha hızlı olduğu işlevi sort, orderve nth. Belgelere bakın.
Suresh_Patel

Yanıtlar:


24

Rfast , nth_element adlı, tam olarak sorduğunuz şeyi yapan ve yukarıda tartışılan tüm uygulamalardan daha hızlı bir işleve sahiptir.

Ayrıca yukarıda tartışılan ve kısmi sıralamaya dayanan yöntemler, en küçük değerlerin bulunmasını desteklemez

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

X'in en büyük 5. öğesini döndürür

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

X'in en küçük 5. öğesini döndürür

En popüler cevaplara karşı aşağıdaki karşılaştırmalar.

10 bin numara için:

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 milyon numara için:

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
Güzel! Normalde nispeten düşük rep kullanıcısı, popüler eski bir soruya cevap eklediğini gördüğümde oldukça düşük kalitedir. Öte yandan, bu mükemmel bir ektir. Birkaç okunabilirlik düzenlemesi yaptım, ama harika görünüyor!
Gregor Thomas

3
Rfast::nthBirden fazla elemanın (örneğin 8. ve 9. en büyük elemanlar) yanı sıra bu elemanların endekslerini döndürebileceğinden bahsetmektedir .
Jasha

3
Rfast çözümü hakkında sevdiğim, paketin her satır veya sütun için bunu yapmak için kolayca uygulanan bir çözüme sahip olmasıdır.
Jay

195

İfadesinin partialargümanını kullanın sort(). İkinci en yüksek değer için:

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

4
Bu yöntemin sort(x, TRUE)[2], sorudaki kısıtlamayı tatmin etmemek dışında, @ Abrar'ın cevabında tarif edilenin avantajı nedir ?
Hugh

5
Bu yöntemi kullandım, ancak aşağıdaki hatayı alın: Error in sort.int(x, na.last = na.last, decreasing = decreasing, ...) : index 4705 outside bounds Sorun ne olabilir? Bazı detaylar: Benim x, 4706 uzunluğunda sayısal bir vektördür ve bazı NAveriler de s'dir. @RobHyndman önerilen tam olarak aynı kodu kullanarak vektör ikinci en yüksek değeri almaya çalıştım.
sriramn

Neden azalanları sıralamıyorsunuz ve sadece iki değerin ikincisini almıyorsunuz? Bu daha hızlı olmaz mıydı?
jwg

3
Azalan argüman kısmi sıralama ile uyumlu değil.
Rob Hyndman

7
Her ne kadar decreasingargüman kısmi sıralama ile uyumlu olmasa da , her zaman -sort(-x, partial=n-1)[n-1]; mantıksal olarak aynı şeydir ve olduğundan çok daha az zaman alır sort(x, decreasing=TRUE)[n-1].
r2evans

52

Sadece kayıtlar için biraz daha yavaş bir alternatif:

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

Bu, tüm vektörü sıralamak ve n-1'inci değeri almaktan daha hızlı olsaydı şaşırtıcı görünüyordu!
jwg

@jwg Bu O (n) olduğundan büyük veri kümelerinde sıralama yapmaktan daha hızlı olmalıdır.
Museful

NA'larla kabul edilen yanıttan daha iyi çalışır - 'min' işlevi için argüman olarak 'na.rm = TRUE' kullanın.
Yair Daon

2
Bana öyle geliyor ki küçük bir değişiklikle önemli bir hız artışı elde edebilirsiniz:max(x[-which.max(x)])
sindri_baldur

31

Rob'un cevabını 2., 3., 4. (vb.) Maks. Bulmak için kullanılabilecek biraz daha genel bir işleve sardım:

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
Güzel. Bu kullanım özellikle kullanışlıdır maxN(1:10, 1:3)(varsayılan N değerini 1 olarak ayarlayabilirdim)
PatrickT

16

Bir vektördeki N en küçük / en büyük değerlerin indekslerini bulmanın kolay bir yolu (N = 3 örneği):

N <- 3

N En Küçük:

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

En Büyük:

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

Böylece değerleri şöyle ayıklayabilirsiniz:

x[ndx]

Bu L log L süresinde çalışır, burada L x uzunluğudur. Kullanıcı günlük L zamanında çalışan bir yöntem umuyordum düşünüyorum.
arsmath

Yöntemler zamana göre sıralanırsa ve en hızlı N çıkarılırsa, bu ikinci en hızlı yol olabilir. Ayrıca hoşuma gitti çünkü kabul edilen çözümle karşılaştırıldığında çok açık bir kod.
Pete

1
Teorik en iyi ve kabul edilen yöntem (umarım) O (log L) yerine O (L) zamanında çalışır. Bu O (L log L) içinde çalışır.
Valentas

6

En yüksek nci değer için,

sort(x, TRUE)[n]

9
OP zaten yazısında bunun kullanmak istemediği bir çözüm olduğunu söyledi: "tüm vektörü sıralamaktan ve bu vektörden x değerini seçmek dışında".
Paul Hiemstra

3

Önce max elemanını kaldırmanın ve daha sonra karşılaştırılabilir hızda başka bir max çalıştırması yaptım:

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

İşte bulduğum en basit yol,

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

Son zamanlarda belirli bir vektörde üst N max / dak sayılarının indekslerini döndüren bir R işlevi ararken , böyle bir işlev olmadığına şaşırdım.

Ve bu çok benzer bir şey.

Base :: order fonksiyonunu kullanan kaba kuvvet çözümü en kolay çözüm gibi görünmektedir.

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

Ancak N değerinizin x vektörünün uzunluğuna kıyasla nispeten küçük olması durumunda en hızlı olanı değildir .

Diğer tarafta N gerçekten küçükse, base :: whichMax işlevini yinelemeli olarak kullanabilirsiniz ve her yinelemede bulunan değeri -Inf ile değiştirebilirsiniz.

# 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
}

Sorunu gördüğünüze inanıyorum - R'nin kopyala-değiştir doğası Bu, çok çok çok küçük N (1,2,3) için daha iyi performans gösterecek, ancak daha büyük N değerleri için hızla yavaşlayacaktır. Ve vektör tüm elemanları yinelemek olan x N defa.

Ben temiz R en iyi çözüm kısmi base :: sort kullanmak olduğunu düşünüyorum .

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

Ardından, yukarıdaki fonksiyon fonksiyonlarının sonucundan son ( N. ) Öğeyi seçebilirsiniz.

Not: yukarıda tanımlanan fonksiyonlar sadece örnektir - bunları kullanmak istiyorsanız, girişleri kontrol etmelisiniz (örn. N> uzunluk (x) ).

Http://palusga.cz/?p=18 adresinde çok benzer bir şey hakkında küçük bir makale yazdım (bir vektörün üst N max / min değerlerinin dizinlerini alın) - burada yukarıda tanımladığım benzer işlevlerin bazı kriterlerini bulabilirsiniz.



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)
}

bu işlev en üst n değerlerine ve indekslerine sahip bir matris döndürür. umarım VDevi-Chou'ya yardımcı olur


0

Bu, x giriş sayısal vektöründeki N'inci en küçük veya en büyük değerin dizinini bulur. En alttan N'th istiyorsanız bağımsız değişkenlerde bottom = TRUE, N'th'i üstten isterseniz bottom = FALSE olarak ayarlayın. N = 1 ve alt = DOĞRU olan ile eşdeğerdir. Min, N = 1 ve alt = YANLIŞ olan ile eşdeğerdir. Maks.

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, ilk argümanın vektör ve ikincisi istediğiniz yer olduğu nth işlevine sahiptir. Bu, elemanları tekrarlamak için de geçerlidir. Örneğin:

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

İkinci en büyük değeri bulmak:

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

[1] 17

2
bu hızlı mı ...?
Ben Bolker

2
dahili olarak bu kullanır x[[order(order_by)[[n]]]]- bu yüzden tüm vektörü sıralamayı gerektirir. Bu yüzden kabul edilen cevap kadar hızlı olmayacak.
Ben Bolker

5
ama sort kısmi = argümanla (her şeyi değiştirir) kullanır
Ben Bolker

@BenBolker, Paolo'nun veya Rob'un cevabını ima eden iyileştirme için kullanılabilir dplyr::nth()mi? bench::mark(max(x[-which.max(x)]), x[[order(-x)[[2]]]] ), nth()yaklaşık 10 kat daha yavaş görünüyor, nerede length(x)3 milyon.
sindri_baldur

-1

Bir sonraki daha yüksek değeri ile tanımlayabilirsiniz cummax(). Örneğin, her yeni daha yüksek değerin konumunu istiyorsanız , değerin değiştiği konumları belirlemek için cummax()değerler vektörünüzü diff()işleve iletebilirsiniz cummax(). vektörümüz olduğunu söyle

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

Şimdi, eğer bir değişikliğin yerini bulmak istiyorsanız, kullanmak istediğim cummax()birçok seçeneğiniz var sign(diff(cummax(v))). Kayıp ilk eleman için ayar yapmanız gerekir diff(). Vektör için tam kod v:

which(sign(diff(cummax(v)))==1)+1

Soruyu yanlış anladığınızı düşünüyorum. Amaç, ikinci en yüksek değeri bulmaktır. Bu sizi v'den 12'ye ve en yüksek üçüncü 8'e çıkarmanıza nasıl yardımcı olur?
Frank

-1

sortAnahtar kelimeyi şu şekilde kullanabilirsiniz :

sort(unique(c))[1:N]

Misal:

c <- c(4,2,44,2,1,45,34,2,4,22,244)
sort(unique(c), decreasing = TRUE)[1:5]

ilk 5 maksimum sayıyı verecektir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.