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 のループとリストの使用です。