ベクトルまたは列の2番目(3番目…)の最高/最低値を見つける最も速い方法


160

Rは最大値と最小値を提供しますが、ベクトル全体をソートすることと、このベクトルから値xを選択することを除いて、順序で別の値を見つける本当に速い方法はありません。

2番目に高い値(たとえば)を取得するより速い方法はありますか?

ありがとう


CRANのパッケージキットが持っているtopnより高速な関数をsortordernth。ドキュメントを見てください。
Suresh_Patel

回答:


195

partial引数を使用しますsort()。2番目に高い値の場合:

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

4
sort(x, TRUE)[2]質問の制約を満たさないことを除いて、@ Abrarの回答で説明されているのとは対照的に、この方法の利点は何ですか?
ヒュー

5
この方法を使用しましたが、次のエラーが発生しましたError in sort.int(x, na.last = na.last, decreasing = decreasing, ...) : index 4705 outside bounds 。いくつかの詳細:My xはNA、データにいくつかのs を含む長さ4706の数値ベクトルです。@RobHyndmanが提案したのとまったく同じコードを使用して、ベクトルの2番目に高い値を取得しようとしました。
スリラム2013年

降順で並べ替えて、2つだけの値の2番目を取得しませんか?これはもっと速くないでしょうか?
jwg 2015

3
減少する引数は部分ソートと互換性がありません。
Rob Hyndman、2015

7
けれどもdecreasing引数は、部分的ソートと互換性がありません、あなたはいつもできました-sort(-x, partial=n-1)[n-1]。論理的には同じことで、に比べてかなり時間がかかりませんsort(x, decreasing=TRUE)[n-1]
r2evans 2017年

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 2015

@jwgこれはO(n)なので、大規模なデータセットでのソートよりも高速でなければなりません。
ミューズフル2016

受け入れられている他の回答よりもNAでうまく機能します。「min」関数の引数として「na.rm = TRUE」を使用するだけです。
Yair Daon 2017

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)

xの5番目に大きい要素を返しますが、

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

xの5番目に小さい要素を返します

最も一般的な回答に対する以下のベンチマーク。

1万個の場合:

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番目に大きい要素)とそれらの要素のインデックスを返すことができることについて言及します。
Jasha 2018

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時間で実行されるメソッドを望んでいたと思います。
arsmath 2013年

メソッドが時間順に並べられ、最速のNが抽出された場合、これは2番目に速い方法かもしれません。また、受け入れられたソリューションと比較して非常に明確なコードであるため、私も気に入っています。
ピート

1
理論的に最良で、受け入れられている方法は、(うまくいけば)O(L)時間で実行され、O(log L)では実行されません。これはO(L log L)で実行されます。
Valentas

6

n番目に高い値の場合、

sort(x, TRUE)[n]

8
OPは、これが彼が使用したくないソリューションであると彼の投稿ですでに述べています:「ベクトル全体をソートすることとは別に、このベクトルから値xを選択すること」。
Paul Hiemstra

3

最初にmax要素を削除してから、同等の速度で別の最大実行を実行することがわかりました。

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

最近、特定のベクトルで上位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値が大きい場合は急速に遅くなります。そして、あなたはベクトルのすべての要素をxN回繰り返しています。

クリーンRでの最善の解決策は、部分的なbase :: sortを使用することだと思います

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

次に、上記の関数定義の結果から最後(N番目)の項目を選択できます。

注:上記で定義された関数は単なる例です-それらを使用する場合は、入力をチェック/サニティする必要があります(例:N> length(x))。

私はhttp://palusga.cz/?p=18で非常に類似したものについての小さな記事(ベクトルの上位N max / min値のインデックスを取得)を書きました-ここで、上で定義した類似の関数のいくつかのベンチマークを見つけることができます。



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

これは、入力数値ベクトルxのN番目の最小値または最大値のインデックスを見つけます。下からN番目が必要な場合は引数にbottom = TRUEを設定し、上からN番目が必要な場合はbottom = FALSEを設定します。N = 1およびbottom = TRUEはwhich.minと同等で、N = 1および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)
}

0

dplyrには関数nthがあり、最初の引数はベクトルで、2番目は必要な場所です。これは繰り返し要素にも当てはまります。例えば:

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

2番目に大きい値を見つける:

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

[1] 17

2
これは速いですか...?
ベンボルカー2018

2
内部的にはこれを使用しx[[order(order_by)[[n]]]]ているため、ベクトル全体をソートする必要があります。だから、受け入れられた答えほど速くはありません。
ベンボルカー2018

5
しかしsort 、partial =引数(すべてを変更します)と共に使用ます
ベンボルカー2018

パオロまたはロブの答えを暗示する@BenBolkerは、改善に使用できますdplyr::nth()か?bench::mark(max(x[-which.max(x)]), x[[order(-x)[[2]]]] )nth()約10倍遅いようです。300 length(x)万です。
sindri_baldur 2018

-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

あなたはその質問を誤解していると思います。目標は、たとえば2番目に高い値を見つけることです。これはどのようにしてvから12へ、そして3番目に高いものから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つの最大数が表示されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.