计算没有循环的“更大”事件

问题描述

让我们假设如果形状为A的{​​{1}}和形状为(100,)的{​​{1}},则我们有一个数组B。两者都包含[0,1]中的值。

如何使(10,)中的元素数大于A中的每个值?我期望形状为B,其中第一个元素是“ (10,)中有多少大于A”,第二个元素是“ B[0]中有多少大于A”,等等...

不使用循环。

我尝试了以下操作,但没有成功:

B[1]

Python不会将我的函数用作import numpy as np import numpy.random as rdm A = rdm.rand(100) B = np.linspace(0,1,10) def occ(z: float) ->float: return np.count_nonzero(A > z) occ(B) 上的标量函数,这就是为什么我得到:

B

我也尝试过使用operands Could not be broadcast together with shapes (10,) (100,) ,但是我遇到了同样的问题...

解决方法

缓慢但简单

如果您不理解该错误消息,则它是神秘的,但它告诉您该怎么做。数组维是broadcast在一起,方法是从右边缘开始将它们对齐。如果将操作分为两部分,这将特别有用:

  1. 创建一个(100,10)遮罩,以显示A的哪些元素大于B的哪些元素:

     mask = A[:,None] > B
    
  2. 沿与A对应的轴上一次操作的结果求和:

     result = np.count_nonzero(mask,axis=0)
    

    OR

     result = np.sum(mask,axis=0)
    

这可以写成单线:

(A[:,None] > B).sum(0)

OR

np.count_nonzero(A[:,None] > B,axis=0)

您可以切换尺寸并将B放置在第一个轴上以获得相同的结果:

(A > B[:,None]).sum(1)

快速而优雅

采用完全不同(但可能效率更高)的方法,您可以使用np.searchsorted

A.sort()
result = A.size - np.searchsorted(A,B)

默认情况下,searchsorted返回将B的每个元素插入到A处的左索引。这几乎可以立即告诉您A有多少个元素大于该数量。

基准

这里,这些算法的标签如下:

  • B0(A[:,None] > B).sum(0)
  • B1(A > B[:,None]).sum(1)
  • HHnp.cumsum(np.histogram(A,bins=B)[0][::-1])[::-1]
  • SSA.sort(); A.size - np.searchsorted(A,B)
+--------+--------+----------------------------------------+
| A.size | B.size |        Time (B0 / B1 / HH / SS)        |
+--------+--------+----------------------------------------+
|    100 |     10 |  20.9 µs / 15.7 µs / 68.3 µs / 8.87 µs |
+--------+--------+----------------------------------------+
|   1000 |     10 |   118 µs / 57.2 µs /  139 µs / 17.8 µs |
+--------+--------+----------------------------------------+
|  10000 |     10 |   987 µs /  288 µs / 1.23 ms /  131 µs |
+--------+--------+----------------------------------------+
| 100000 |     10 |  9.48 ms / 2.77 ms / 13.4 ms / 1.42 ms |
+--------+--------+----------------------------------------+
|    100 |    100 |  70.7 µs / 63.8 µs /   71 µs / 11.4 µs |
+--------+--------+----------------------------------------+
|   1000 |    100 |   518 µs /  388 µs /  148 µs / 21.6 µs |
+--------+--------+----------------------------------------+
|  10000 |    100 |  4.91 ms / 2.67 ms / 1.22 ms /  137 µs |
+--------+--------+----------------------------------------+
| 100000 |    100 |  57.4 ms / 35.6 ms / 13.5 ms / 1.42 ms |
+--------+--------+----------------------------------------+

内存布局很重要。 B1总是比B0快。发生这种情况是因为求和连续的(缓存的)元素(沿C顺序的最后一个轴)总是比跳过所有行来获取下一个元素要快。对于B的较小值,广播效果很好。请记住,B0B1的时间和空间复杂度均为O(A.size * B.size)。两种直方图解决方案的复杂度应该约为O(A.size * log(A.size)),但是SS的实现效率要比HH高得多,因为它可以承担关于数据的更多事情。

,

我认为您可以使用np.histogram来完成这项工作

A = rdm.rand(100)
B = np.linspace(0,1,10)
np.histogram(A,bins=B)[0]

提供输出

array([10,9,8,11,14,10,12,17])

B[9]将始终为空,因为没有值> 1。

然后向后计算总和

np.cumsum(np.histogram(A,bins=B)[0][::-1])[::-1]

输出

array([100,90,81,73,62,53,39,29,17])
,
np.sum(A>B.reshape((-1,1)),axis=1)

说明

需要了解broadcasting并为此重塑。通过将B重塑为形状(len(B),1),可以将其与A一起广播,以生成具有形状(len(B),len(A))的数组,其中包含所有比较。然后,您沿轴1(沿A)求和。

换句话说,A < B不起作用,因为A有100个条目,而B有10个条目。如果您阅读broadcasting rules,则会看到numpy将以最后一个开始尺寸,如果尺寸相同,则可以一对一比较。如果两者之一是1,则该维度将被拉伸或“复制”以匹配另一个维度。如果它们不相等并且没有一个等于1,则失败。

举一个简短的例子:

A = np.array([0.5112744,0.21696187,0.14710105,0.98581087,0.50053359,0.54954654,0.81217522,0.50009166,0.42990167,0.56078499])
B = array([0.25,0.5,0.75])

(A>B.reshape((-1,1)))的转置(出于可读性)

np.array([[ True,True,False],[False,False,[ True,True],False]])

np.sum(A>B.reshape((-1,axis=1)

array([8,7,2])

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...