8

float/int の numpy 配列があり、その要素をランクにマップしたいと考えています。

配列に重複がない場合、問題は次のコードで解決できます

In [49]: a1
Out[49]: array([ 0.1,  5.1,  2.1,  3.1,  4.1,  1.1,  6.1,  8.1,  7.1,  9.1])

In [50]: a1.argsort().argsort()
Out[50]: array([0, 5, 2, 3, 4, 1, 6, 8, 7, 9])

ここで、このメソッドを重複の可能性がある配列に拡張して、重複が同じ値にマップされるようにします。たとえば、配列が必要です

a2 = np.array([0.1, 1.1, 2.1, 3.1, 4.1, 1.1, 6.1, 7.1, 7.1, 1.1])

どちらかにマッピングされます

0 1 4 5 6 1 7 8 8 1

または

0 3 4 5 6 3 7 9 9 3

または

0 2 4 5 6 2 7 8.5 8.5 2

最初/2 番目のケースでは、a2.argsort().argsort() を適用するだけで、重複をそれらの間の最小/最大ランクにマップします。3 番目のケースは、最初の 2 つのケースの平均です。

助言がありますか?

編集 (効率要件)

最初の説明で、所要時間について言及するのを忘れていました。「純粋な python オーバーヘッド」を回避できる numpy/scipy 関数に関する解決策を探しています。明確にするために、リチャードが提案した解決策を考えてみましょう。これは実際には問題を解決しますが、非常に遅いです。

def argsortdup(a1):
  sorted = np.sort(a1)
  ranked = []
  for item in a1:
    ranked.append(sorted.searchsorted(item))
  return np.array(ranked)

In [86]: a2 = np.array([ 0.1,  1.1,  2.1,  3.1,  4.1,  1.1,  6.1,  7.1,  7.1,  1.1])

In [87]: %timeit a2.argsort().argsort()
1000000 loops, best of 3: 1.55 us per loop

In [88]: %timeit argsortdup(a2)
10000 loops, best of 3: 25.6 us per loop

In [89]: a = np.arange(0.1, 1000.1)

In [90]: %timeit a.argsort().argsort()
10000 loops, best of 3: 24.5 us per loop

In [91]: %timeit argsortdup(a)
1000 loops, best of 3: 1.14 ms per loop

In [92]: a = np.arange(0.1, 10000.1)

In [93]: %timeit a.argsort().argsort()
1000 loops, best of 3: 303 us per loop

In [94]: %timeit argsortdup(a)
100 loops, best of 3: 11.9 ms per loop

上記の分析から、argsortdup は a.argsort().argsort() よりも 30 倍から 50 倍遅いことが明らかです。主な理由は、Python のループとリストの使用です。

4

3 に答える 3

4

uniqueand を使用すると、かなりうまくいくことができますbincount

>>> u, v = np.unique(a2, return_inverse=True)
>>> (np.cumsum(np.bincount(v)) - 1)[v]
array([0, 3, 4, 5, 6, 3, 7, 9, 9, 3])

または、最低ランクの場合:

>>> (np.cumsum(np.concatenate(([0], np.bincount(v)))))[v]
array([0, 1, 4, 5, 6, 1, 7, 8, 8, 1])

bincount提供するビンの数を指定することで、わずかなスピードアップがあります。

(np.cumsum(np.bincount(v, minlength=u.size)) - 1)[v]
于 2013-02-03T13:08:30.773 に答える
3

コメントで提案されてscipyいる@WarrenWeckesserの最新バージョンにアップグレードした後scipy.stats.rankdata、両方よりも高速であり、より大きな配列でそれを行うための最速の方法であるようです。scipy.stats.mstats.rankdatanp.searchsorted

In [1]: import numpy as np

In [2]: from scipy.stats import rankdata as rd
   ...: from scipy.stats.mstats import rankdata as rd2
   ...: 

In [3]: array = np.arange(0.1, 1000000.1)

In [4]: %timeit np.searchsorted(np.sort(array), array)
1 loops, best of 3: 385 ms per loop

In [5]: %timeit rd(array)
10 loops, best of 3: 109 ms per loop

In [6]: %timeit rd2(array)
1 loops, best of 3: 205 ms per loop
于 2013-02-03T10:45:11.763 に答える
2

これは、あなたが望む出力を返すことができる関数です(最初のケース)

def argsortdup(a1):
  sorted = sort(a1)
  ranked = []
  for item in a1:
    ranked.append(sorted.searchsorted(item))
  return array(ranked)

基本的に、それを並べ替えてから、アイテムが存在するインデックスを検索します。重複があると仮定すると、最初のインスタンスのインデックスが返されます。私はあなたのa2の例でそれをテストし、次のようなことをしました

a3 = argsortdup(a2)

収量

array([0, 1, 4, 5, 6, 1, 7, 8, 8, 1])

"a2 でテスト":

>>> a2
array([ 0.1,  1.1,  2.1,  3.1,  4.1,  1.1,  6.1,  7.1,  7.1,  1.1])
>>> def argsortdup(a1):
...   sorted = sort(a1)
...   ranked = []
...   for item in a1:
...     ranked.append(sorted.searchsorted(item))
...   return array(ranked)
...
>>> a3 = argsortdup(a2)
>>> a2
array([ 0.1,  1.1,  2.1,  3.1,  4.1,  1.1,  6.1,  7.1,  7.1,  1.1])
>>> a3
array([0, 1, 4, 5, 6, 1, 7, 8, 8, 1])
>>>
于 2013-02-03T09:57:51.600 に答える