【问题标题】:Ranking of numpy array with possible duplicates可能重复的numpy数组的排名
【发布时间】:2015-03-16 17:05:22
【问题描述】:

我有一个 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])

现在我想将此方法扩展到具有可能重复项的数组,以便将重复项映射到相同的值。例如,我想要数组 a

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

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

有什么建议吗?

编辑(效率要求)

在最初的描述中,我忘了提及时间要求。我正在寻找 numpy/scipy 函数方面的解决方案,这将避免“纯 python 开销”。为了清楚起见,考虑一下 Richard 提出的解决方案,它实际上解决了问题,但速度很慢:

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

【问题讨论】:

  • 为什么要和a.argsort().argsort()比较?这并没有给你答案。
  • 正确,这并不能在存在重复项的情况下给我答案。但是,我想强调使用 numpy 函数与 numpy + 纯 python 循环时的性能差异。而且差别很大。据我所知,我使用“a.argsort().argsort()”的解决方案比 Richard 提供的更快:)
  • 理查德的回答是正确的,但需要更多的矢量化。请参阅我对他的回答的评论。这应该会在相当合理的时间内给您答案。
  • 查看scipy.stats.rankdata。另外,看看pandas 包(pandas.pydata.org/pandas-docs/stable/…)中的排名函数。

标签: python sorting numpy scipy


【解决方案1】:

您可以使用uniquebincount 做得相当好:

>>> 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 提供要提供的 bin 数量,可以稍微加快速度:

(np.cumsum(np.bincount(v, minlength=u.size)) - 1)[v]

【讨论】:

  • 非常漂亮的解决方案,在大型阵列上工作正常且速度非常快。非常感谢!
  • @Merlin 当然,使用scipy.stats.rankdata 只是“序数”-“分钟”。如果由于某种原因您不能使用 scipy,我的代码也将起作用。已回答您的问题。
  • @ecatmur 我如何将您的代码用于我的问题? scipy 作品,看着你的学习一些东西..
  • @Merlin 当然,只需使用我的(第二个)解决方案替换rankdata(..., "min")argsort().argsort() 技术为rankdata(..., "ordinal")。更新了我的答案。
【解决方案2】:

按照 cmets 中的 @WarrenWeckesser 建议升级到最新版本的 scipy 后,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

【讨论】:

  • 谢谢,您的回答很有用。但是,据我在内部看到的,它在纯 python 中实现了逻辑(与 Richard 提出的非常相似,但处理关系的逻辑要多一点)。因此,它甚至比 Richard 的解决方案还要慢。但无论如何,最好有一个现成的解决方案。
  • @MikhailShevelev -- 速度取决于数组的大小,尝试一些大的东西,它会优于其他两个。
  • 你可以使用scipy.stats.rankdata,比scipy.stats.mstats.rankdata快。
  • @WarrenWeckesser -- 我确实检查过它,但它在大型阵列上的表现似乎(明显)更差,这有点令人困惑......
  • @root:你使用的是什么版本的 scipy?在最近的 scipy 版本中,stats.rankdata 用 cython 重写,因此它会比旧的 python+numpy 版本快得多。
【解决方案3】:

这是一个可以返回你想要的输出的函数(在第一种情况下)

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

【讨论】:

  • 理查德,感谢您的快速回答。你的功能解决了这个问题,但是我正在寻求在执行时间方面更有效的解决方案。我忘了在最初的描述中提到这一点——这是我的错,我很抱歉。请参阅更新后的说明以了解更多详细信息。再次感谢您的回复!
  • 正确的方法,但如果你更好地使用numpy,你可以做得更好。那个函数就是:np.searchsorted(np.sort(a1), a1)
猜你喜欢
  • 2017-10-14
  • 2016-08-23
  • 1970-01-01
  • 2012-12-17
  • 2015-11-20
  • 2014-05-07
  • 1970-01-01
  • 1970-01-01
  • 2020-12-26
相关资源
最近更新 更多