【问题标题】:Classify elements of a numpy array using a second array as reference使用第二个数组作为参考对 numpy 数组的元素进行分类
【发布时间】:2015-06-26 16:41:00
【问题描述】:

假设我有一个具有有限数量唯一值的数组。说

data = array([30, 20, 30, 10, 20, 10, 20, 10, 30, 20, 20, 30, 30, 10, 30])

我还有一个引用数组,其中包含在data 中找到的所有唯一值,没有重复且按特定顺序。说

reference = array([20, 10, 30])

我想创建一个形状与data 相同的数组,其中包含reference 数组中的索引作为值,其中找到了data 数组中的每个元素。

换句话说,有了datareference,我想创建一个数组indexes,以使以下内容成立。

data = reference[indexes]

计算 indexes 的次优方法是使用 for 循环,如下所示

indexes = np.zeros_like(data, dtype=int)
for i in range(data.size):
    indexes[i] = np.where(data[i] == reference)[0]

但令我惊讶的是,没有 numpythonic (因此更快!)方法可以做到这一点......有什么想法吗?

谢谢!

【问题讨论】:

  • 嗯...我想我并不清楚这个问题,抱歉...reference 数组预计比data 小得多,所以主要的事情我需要优化的是循环遍历data 中的所有值...不过,我确实应该更频繁地考虑字典! :)

标签: python arrays performance numpy


【解决方案1】:

我们有datareference 作为-

In [375]: data
Out[375]: array([30, 20, 30, 10, 20, 10, 20, 10, 30, 20, 20, 30, 30, 10, 30])

In [376]: reference
Out[376]: array([20, 10, 30])

让我们考虑一下reference的排序版本-

In [373]: np.sort(reference)
Out[373]: array([10, 20, 30])

现在,我们可以使用np.searchsorted 来找出每个data 元素在这个排序版本中的位置,就像这样 -

In [378]: np.searchsorted(np.sort(reference), data, side='left')
Out[378]: array([2, 1, 2, 0, 1, 0, 1, 0, 2, 1, 1, 2, 2, 0, 2], dtype=int64)

如果我们运行原始代码,预期的输出结果是 -

In [379]: indexes
Out[379]: array([2, 0, 2, 1, 0, 1, 0, 1, 2, 0, 0, 2, 2, 1, 2])

可以看出,searchsorted 输出很好,除了其中的0's 必须是1s1's 必须更改为0's。现在,我们已经开始计算,reference 的排序版本。因此,要进行0's1's 的更改,反之亦然,我们需要引入用于排序reference 的索引,即np.argsort(reference)。这基本上就是矢量化无循环或无字典的方法!所以,最终的实现看起来像这样 -

# Get sorting indices for reference
sort_idx = np.argsort(reference)

# Sort reference and get searchsorted indices for data in reference
pos = np.searchsorted(reference[sort_idx], data, side='left')

# Change pos indices based on sorted indices for reference
out = np.argsort(reference)[pos]

运行时测试 -

In [396]: data = np.random.randint(0,30000,150000)
     ...: reference = np.unique(data)
     ...: reference = reference[np.random.permutation(reference.size)]
     ...: 
     ...: 
     ...: def org_approach(data,reference):
     ...:     indexes = np.zeros_like(data, dtype=int)
     ...:     for i in range(data.size):
     ...:         indexes[i] = np.where(data[i] == reference)[0]
     ...:     return indexes
     ...: 
     ...: def vect_approach(data,reference):
     ...:     sort_idx = np.argsort(reference)
     ...:     pos = np.searchsorted(reference[sort_idx], data, side='left')       
     ...:     return sort_idx[pos]
     ...: 

In [397]: %timeit org_approach(data,reference)
1 loops, best of 3: 9.86 s per loop

In [398]: %timeit vect_approach(data,reference)
10 loops, best of 3: 32.4 ms per loop

验证结果 -

In [399]: np.array_equal(org_approach(data,reference),vect_approach(data,reference))
Out[399]: True

【讨论】:

  • 是的!好又快的方法!谢谢!
【解决方案2】:

您必须遍历数据一次才能将数据值映射到索引上。最快的方法是在字典中查找值索引。所以你需要先创建一个从值到索引的字典。

这是一个完整的例子:

import numpy

data = numpy.array([30, 20, 30, 10, 20, 10, 20, 10, 30, 20, 20, 30, 30, 10, 30])
reference = numpy.array([20, 10, 30])
reference_index = dict((value, index) for index, value in enumerate(reference))
indexes = [reference_index[value] for value in data]
assert numpy.all(data == reference[indexes])

这将比 numpy.where 方法更快,因为 numpy.where 将执行线性 O(n) 搜索,而字典方法使用哈希表在 O(1) 时间内查找索引。

【讨论】:

  • 不错!当reference 数组不比data 数组小很多时,这很有意义...在我的情况下,reference 小得多,data 很大...确实,我的 for 循环示例没有'不要考虑到这一点......如果data.size >>> reference.size,像for i in range(reference.size): indexes[data==reference[i]] = i这样的东西会更快。
【解决方案3】:
import numpy as np

data = np.array([30, 20, 30, 10, 20, 10, 20, 10, 30, 20, 20, 30, 30, 10, 30])
reference = {20:0, 10:1, 30:2}
indexes = np.zeros_like(data, dtype=int)

for i in xrange(data.size):
    indexes[i] = reference[data[i]]

字典查找速度明显更快。使用xrange 也有一定帮助。

使用timeit:

原文:4.01297836938

此版本:1.30972428591

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-01-01
    • 2013-04-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-07
    • 2019-11-25
    • 1970-01-01
    相关资源
    最近更新 更多