Cara tercepat untuk menemukan nilai tertinggi / terendah kedua dalam vektor atau kolom


161

R menawarkan max dan min, tetapi saya tidak melihat cara yang sangat cepat untuk menemukan nilai lain dalam urutan, selain memilah seluruh vektor dan kemudian memilih nilai x dari vektor ini.

Apakah ada cara yang lebih cepat untuk mendapatkan nilai tertinggi kedua, misalnya?


Paket kit pada CRAN memiliki topnfungsi yang lebih cepat daripada sort, orderdan nth. Lihatlah dokumentasinya.
Suresh_Patel

Jawaban:


24

Rfast memiliki fungsi yang disebut nth_element yang melakukan persis apa yang Anda minta dan lebih cepat dari semua implementasi yang dibahas di atas

Juga metode yang dibahas di atas yang didasarkan pada semacam parsial, tidak mendukung menemukan k terkecil nilai-nilai

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

Akan mengembalikan elemen x ke 5 terbesar, sementara

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

Akan mengembalikan elemen terkecil ke-5 x

Tingkatan yang dicapai di bawah ini terhadap sebagian besar jawaban populer.

Untuk 10 ribu angka:

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

Untuk 1 juta angka:

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
Bagus! Biasanya ketika saya melihat pengguna rep yang relatif rendah menambahkan jawaban untuk pertanyaan lama yang populer, kualitasnya cukup rendah. Ini, di sisi lain, merupakan tambahan yang sangat baik. Saya membuat beberapa suntingan yang dapat dibaca, tetapi tampak hebat!
Gregor Thomas

3
Perlu disebutkan bahwa Rfast::nthdapat mengembalikan beberapa elemen (misalnya elemen terbesar ke-8 dan ke-9) serta indeks elemen-elemen tersebut.
Jasha

3
Apa yang saya sukai dari solusi Rfast adalah paket itu juga memiliki solusi yang mudah diimplementasikan untuk melakukan ini untuk setiap baris atau kolom.
Jay

195

Gunakan partialargumen dari sort(). Untuk nilai tertinggi kedua:

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

4
Apa keuntungan dari metode ini yang bertentangan dengan yang sort(x, TRUE)[2]dijelaskan dalam jawaban @ Abrar, selain tidak memuaskan kendala dalam pertanyaan?
Hugh

5
Saya menggunakan metode ini, tetapi mendapatkan kesalahan berikut: Error in sort.int(x, na.last = na.last, decreasing = decreasing, ...) : index 4705 outside bounds Ada yang tahu apa masalahnya? Beberapa perincian: X saya adalah vektor numerik dengan panjang 4706 dengan beberapa NAs dalam data. Saya mencoba untuk mendapatkan nilai tertinggi kedua dalam vektor menggunakan kode yang sama persis seperti yang disarankan oleh @RobHyndman.
sriramn

Mengapa Anda tidak menyortir turun dan mengambil nilai kedua dari hanya dua? Bukankah ini lebih cepat?
jwg

3
Argumen descreasing tidak kompatibel dengan penyortiran parsial.
Rob Hyndman

7
Meskipun decreasingargumennya tidak kompatibel dengan penyortiran parsial, Anda selalu bisa -sort(-x, partial=n-1)[n-1]; ini secara logis adalah hal yang sama dan membutuhkan waktu yang jauh lebih sedikit daripada sort(x, decreasing=TRUE)[n-1].
r2evans

52

Alternatif sedikit lebih lambat, hanya untuk catatan:

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

Tampaknya mengejutkan jika ini lebih cepat daripada menyortir seluruh vektor dan mengambil nilai n-1!
jwg

@ jwg Ini O (n) jadi harus lebih cepat daripada mengurutkan pada dataset besar.
Museful

Berfungsi lebih baik dengan NAS daripada jawaban yang diterima lainnya - cukup gunakan 'na.rm = TRUE' sebagai argumen untuk fungsi 'min'.
Yair Daon

2
Sepertinya saya Anda bisa mendapatkan beberapa peningkatan kecepatan yang cukup dengan modifikasi kecil:max(x[-which.max(x)])
sindri_baldur

31

Saya membungkus jawaban Rob menjadi fungsi yang sedikit lebih umum, yang dapat digunakan untuk menemukan maks 2, 3, 4 (dll):

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
Keren. Penggunaan ini sangat berguna maxN(1:10, 1:3)(saya akan mengatur default N ke 1)
PatrickT

16

Berikut adalah cara mudah untuk menemukan indeks N nilai terkecil / terbesar dalam vektor (Contoh untuk N = 3):

N <- 3

N Terkecil:

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

N Terbesar:

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

Jadi, Anda dapat mengekstrak nilai sebagai:

x[ndx]

Ini berjalan dalam waktu L log L, di mana L adalah panjang x. Saya pikir pengguna berharap untuk metode yang berjalan dalam waktu log L.
arsmath

Ini mungkin cara tercepat kedua jika metode dipesan berdasarkan waktu dan N tercepat diekstraksi. Saya juga suka karena kode ini sangat jelas dibandingkan dengan solusi yang diterima.
Pete

1
Metode teoritis terbaik dan yang diterima (semoga) berjalan dalam waktu O (L), bukan O (log L). Yang ini berjalan di O (L log L).
Valentas

6

Untuk nilai tertinggi ke-n,

sort(x, TRUE)[n]

9
OP sudah mengatakan dalam posnya bahwa ini adalah solusi yang tidak ingin ia gunakan: "selain memilah seluruh vektor dan daripada memilih nilai x dari vektor ini".
Paul Hiemstra

3

Saya menemukan bahwa menghapus elemen max terlebih dahulu dan kemudian menjalankan max lainnya dalam kecepatan yang sebanding:

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

Inilah cara paling sederhana yang saya temukan,

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

Ketika saya baru-baru ini mencari R fungsi mengembalikan indeks atas N max / angka min dalam vektor yang diberikan, saya terkejut tidak ada fungsi seperti itu.

Dan ini adalah sesuatu yang sangat mirip.

Solusi brute force menggunakan fungsi dasar :: order tampaknya yang paling mudah.

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

Tetapi ini bukan yang tercepat jika nilai N Anda relatif kecil dibandingkan dengan panjang vektor x .

Di sisi lain jika N benar-benar kecil, Anda dapat menggunakan basis :: whichMax berfungsi secara iteratif dan di setiap iterasi Anda dapat mengganti nilai yang ditemukan dengan -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
}

Saya percaya Anda melihat masalah - sifat copy-on-memodifikasi R. Jadi ini akan bekerja lebih baik untuk N sangat sangat sangat kecil (1,2,3) tetapi akan cepat melambat untuk nilai N yang lebih besar. Dan Anda mengulangi semua elemen dalam vektor x N kali.

Saya pikir solusi terbaik dalam clean R adalah dengan menggunakan basis parsial :: sort .

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

Kemudian Anda dapat memilih yang terakhir ( N th) item dari hasil fungsi defiend di atas.

Catatan: fungsi yang didefinisikan di atas hanyalah contoh - jika Anda ingin menggunakannya, Anda harus memeriksa / input sanity (mis. N> panjang (x) ).

Saya menulis sebuah artikel kecil tentang sesuatu yang sangat mirip (dapatkan indeks nilai maksimal N / min maksimum vektor) di http://palusga.cz/?p=18 - Anda dapat menemukan di sini beberapa tolok ukur fungsi serupa yang saya tetapkan di atas.



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

fungsi ini akan mengembalikan matriks dengan nilai n atas dan indeksnya. harap ini membantu VDevi-Chou


0

Ini akan menemukan indeks dari N'th nilai terkecil atau terbesar di input numerik vektor x. Setel bawah = BENAR dalam argumen jika Anda ingin N'th dari bawah, atau bawah = FALSE jika Anda ingin N'th dari atas. N = 1 dan bawah = BENAR setara dengan which.min, N = 1 dan bawah = FALSE setara dengan yang.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 memiliki fungsi n, di mana argumen pertama adalah vektor dan yang kedua adalah tempat yang Anda inginkan. Ini berlaku untuk elemen berulang juga. Sebagai contoh:

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

Menemukan nilai terbesar kedua:

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

[1] 17

2
apakah ini cepat ...?
Ben Bolker

2
ini secara internal menggunakan x[[order(order_by)[[n]]]]- jadi ini membutuhkan pengurutan seluruh vektor. Jadi itu tidak akan secepat jawaban yang diterima.
Ben Bolker

5
tetapi digunakan sort dengan argumen parsial = (yang mengubah segalanya)
Ben Bolker

@ BenBolker yang menyiratkan jawaban Paolo atau Rob dapat digunakan untuk meningkatkan dplyr::nth()? bench::mark(max(x[-which.max(x)]), x[[order(-x)[[2]]]] ), nth()sepertinya hampir 10 kali lebih lambat, di mana length(x)3 juta.
sindri_baldur

-1

Anda dapat mengidentifikasi nilai yang lebih tinggi berikutnya dengan cummax(). Jika Anda ingin lokasi masing-masing nilai baru yang lebih tinggi misalnya, Anda dapat meneruskan vektor cummax()nilai Anda ke diff()fungsi untuk mengidentifikasi lokasi di mana cummax()nilai berubah. katakanlah kita memiliki vektor

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

Sekarang, jika Anda ingin menemukan lokasi perubahan, cummax()Anda memiliki banyak opsi yang cenderung saya gunakan sign(diff(cummax(v))). Anda harus menyesuaikan elemen pertama yang hilang karena diff(). Kode lengkap untuk vektor vadalah:

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

Saya pikir Anda salah paham pertanyaannya. Tujuannya adalah untuk menemukan, katakanlah, nilai tertinggi kedua. Bagaimana ini membantu Anda dari v ke 12 ... dan untuk yang ketiga tertinggi ke 8?
Frank

-1

Anda dapat menggunakan sortkata kunci seperti ini:

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

Contoh:

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

akan memberikan 5 angka maks pertama.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.