用于比较两个NumPy数组的元素广播?

时间:2018-08-06 16:03:19

标签: python arrays numpy vectorization numpy-broadcasting

假设我有一个像这样的数组:

import numpy as np

base_array = np.array([-13, -9, -11, -3, -3, -4,   2,  2,
                         2,  5,   7,  7,  8,  7,  12, 11])

假设我想知道:“ base_array中有多少个元素大于4?”这可以通过利用广播来简单地完成:

np.sum(4 < base_array)

答案为7。现在,假设我不想对单个值进行比较,而是想对一个数组进行比较。换句话说,对于c中的每个值comparison_array,找出base_array的多少个元素大于c。如果我以这种天真的方式进行操作,它显然会失败,因为它不知道如何正确广播它:

comparison_array = np.arange(-13, 13)
comparison_result = np.sum(comparison_array < base_array)

输出:

Traceback (most recent call last):
  File "<pyshell#87>", line 1, in <module>
    np.sum(comparison_array < base_array)
ValueError: operands could not be broadcast together with shapes (26,) (16,) 

如果我能以某种方式让comparison_array的每个元素广播到base_array的形状,那就可以解决这个问题。但是我不知道如何进行这种“逐元素广播”。

现在,我确实知道我如何使用列表理解来实现这两种情况:

first = sum([4 < i for i in base_array])
second = [sum([c < i for i in base_array])
          for c in comparison_array]
print(first)
print(second)

输出:

7
[15, 15, 14, 14, 13, 13, 13, 13, 13, 12, 10, 10, 10, 10, 10, 7, 7, 7, 6, 6, 3, 2, 2, 2, 1, 0]

但是,众所周知,这将比在大型数组上正确矢量化的numpy实现要慢几个数量级。因此,我应该如何在numpy中做到这一点,以便快速?理想情况下,此解决方案应该扩展到广播工作的任何类型的操作,而不仅仅是在此示例中大于或小于该操作。

3 个答案:

答案 0 :(得分:3)

您可以简单地向比较数组添加一个维度,以便在新维度的所有值之间“拉伸”比较。

>>> np.sum(comparison_array[:, None] < base_array)
228

这是broadcasting的基本原理,适用于各种操作。

如果需要沿某个轴求和,只需在比较后指定要沿其求和的轴即可。

>>> np.sum(comparison_array[:, None] < base_array, axis=1)
array([15, 15, 14, 14, 13, 13, 13, 13, 13, 12, 10, 10, 10, 10, 10,  7,  7,
        7,  6,  6,  3,  2,  2,  2,  1,  0])

答案 1 :(得分:1)

您将需要转置其中一个阵列以进行广播以使其正常工作。当您一起广播两个阵列时,尺寸会对齐,并且任何单位尺寸都会有效地扩展到它们匹配的非单位尺寸。因此,大小为(16, 1)(原始数组)和(1, 26)(比较数组)的两个数组将广播到(16, 26)

别忘了对尺寸16的尺寸求和。

(base_array[:, None] > comparison_array).sum(axis=1)
切片中的

None等效于np.newaxis:这是在指定索引处插入新单位尺寸的多种方法之一。您无需执行comparison_array[None, :]的原因是,广播会排列最大的尺寸,并自动用最小的尺寸填充。

答案 2 :(得分:1)

这里是np.searchsorted的一个,重点是内存效率和性能-

@(Html.Kendo().Grid(Model.MyModel)
.
.
.
.DataSource(ds => ds
     .Ajax()
     .Read(r => r.Action("GetReport", "MyController", Model))

时间-

def get_comparative_sum(base_array, comparison_array):
    n = len(base_array)
    base_array_sorted = np.sort(base_array)
    idx = np.searchsorted(base_array_sorted, comparison_array, 'right')
    idx[idx==n] = n-1
    return n - idx - (base_array_sorted[idx] == comparison_array)