纯 numpy/scipy 中具有可能重复项的 numpy 数组的排名

问题描述 投票:0回答:3

我有一个浮点数/整数的 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       # minimum rank

或: 0 3 4 5 6 3 7 9 9 3 # 最大排名 或者: 0 2 4 5 6 2 7 8.5 8.5 2 # 平均排名

在第一种/第二种情况下,如果我们只应用

a2.argsort().argsort()
,我们会将重复项映射到其中的最小/最大排名。 第三种情况只是前两种情况的平均值。

有什么建议吗?

编辑(效率要求)

在最初的描述中,我忘记提及我的速度要求。我想要一个纯 numpy/scipy 函数的解决方案,这样可以避免原生 Python 的开销。示例:考虑理查德提出的解决方案,它实际上解决了问题,但速度相当慢:

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循环和列表的使用。

python numpy scipy ranking
3个回答
4
投票

您可以使用

unique
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]

3
投票

按照评论中@WarrenWeckesser的建议升级到最新版本的

scipy
后,
scipy.stats.rankdata
似乎比
scipy.stats.mstats.rankdata
np.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

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])
>>>
© www.soinside.com 2019 - 2024. All rights reserved.