[英]numpy 2d boolean array indexing with reduce along one axis
这个问题与此类似。
我有一个2d布尔数组“属于”和一个2d浮点数组“角度”。 我想要的是沿着行总和相应索引所属的角度为True,并使用numpy(即避免python循环)。 我不需要存储生成的行,这些行具有不同的长度,并且如链接的问题中所解释的那样需要列表。
所以我尝试的是np.sum(角度[属于],轴= 1),但角度[属于]返回1d结果,我不能按我的意愿减少它。 我也试过np.sum(角度*属于,轴= 1),这是有效的。 但我想知道我是否可以通过只访问属于True的索引来改善时间。 属于真是大约30%的时间和角度是一个涉及角度的较长公式的简化。
UPDATE
我喜欢einsum的解决方案,但在我的实际计算中,速度很快。 我在问题中使用了角度来简化,在实践中它是一个使用角度的公式。 我怀疑这个公式是针对所有角度计算的(无论属于哪个),然后传递给einsum,它将执行计算。
这就是我所做的:
THRES_THETA和max_line_length是浮点数。 属于,角度和lines_lengths_vstacked有形状(1653,58)和np.count_nonzero(属于)/belong.size - > 0.376473287856979
l2 = (lambda angle=angle, belong=belong, THRES_THETA=THRES_THETA, lines_lengths_vstacked=lines_lengths_vstacked, max_line_length=max_line_length:
np.sum(belong*(0.3 * (1-(angle/THRES_THETA)) + 0.7 * (lines_lengths_vstacked/max_line_length)), axis=1)) #base method
t2 = timeit.Timer(l2)
print(t2.repeat(3, 100))
l1 = (lambda angle=angle, belong=belong, THRES_THETA=THRES_THETA, lines_lengths_vstacked=lines_lengths_vstacked, max_line_length=max_line_length:
np.einsum('ij,ij->i', belong, 0.3 * (1-(angle/THRES_THETA)) + 0.7 * (lines_lengths_vstacked/max_line_length)))
t1 = timeit.Timer(l1)
print(t1.repeat(3, 100))
l3 = (lambda angle=angle, belong=belong:
np.sum(angle*belong ,axis=1)) #base method
t3 = timeit.Timer(l3)
print(t3.repeat(3, 100))
l4 = (lambda angle=angle, belong=belong:
np.einsum('ij,ij->i', belong, angle))
t4 = timeit.Timer(l4)
print(t4.repeat(3, 100))
结果如下:
[0.2505458095931187, 0.22666162878242901, 0.23591678551324263]
[0.23295411847036418, 0.21908727226505043, 0.22407296178704272]
[0.03711204915708555, 0.03149960399994978, 0.033403337575027114]
[0.025264803208228992, 0.022590580646423053, 0.024585736455331464]
如果我们查看最后两行,对应于einsum的行比使用基本方法快约30%。 但是如果我们看前两行,那么einsum方法的速度会更快,只会快0.1%左右。
我不确定这个时间是否可以改善。
你可以使用np.einsum
-
np.einsum('ij,ij->i',belong,angles)
你也可以使用np.bincount
,就像这样 -
idx,_ = np.where(belong)
out = np.bincount(idx,angles[belong])
样品运行 -
In [32]: belong
Out[32]:
array([[ True, True, True, False, True],
[False, False, False, True, True],
[False, False, True, True, True],
[False, False, True, False, True]], dtype=bool)
In [33]: angles
Out[33]:
array([[ 0.65429151, 0.36235607, 0.98316406, 0.08236384, 0.5576149 ],
[ 0.37890797, 0.60705112, 0.79411002, 0.6450942 , 0.57750073],
[ 0.6731019 , 0.18608778, 0.83387574, 0.80120389, 0.54971573],
[ 0.18971255, 0.86765132, 0.82994543, 0.62344429, 0.05207639]])
In [34]: np.sum(angles*belong ,axis=1) # This worked for you, so using as baseline
Out[34]: array([ 2.55742654, 1.22259493, 2.18479536, 0.88202183])
In [35]: np.einsum('ij,ij->i',belong,angles)
Out[35]: array([ 2.55742654, 1.22259493, 2.18479536, 0.88202183])
In [36]: idx,_ = np.where(belong)
...: out = np.bincount(idx,angles[belong])
...:
In [37]: out
Out[37]: array([ 2.55742654, 1.22259493, 2.18479536, 0.88202183])
运行时测试 -
In [52]: def sum_based(belong,angles):
...: return np.sum(angles*belong ,axis=1)
...:
...: def einsum_based(belong,angles):
...: return np.einsum('ij,ij->i',belong,angles)
...:
...: def bincount_based(belong,angles):
...: idx,_ = np.where(belong)
...: return np.bincount(idx,angles[belong])
...:
In [53]: # Inputs
...: belong = np.random.rand(4000,5000)>0.7
...: angles = np.random.rand(4000,5000)
...:
In [54]: %timeit sum_based(belong,angles)
...: %timeit einsum_based(belong,angles)
...: %timeit bincount_based(belong,angles)
...:
1 loops, best of 3: 308 ms per loop
10 loops, best of 3: 134 ms per loop
1 loops, best of 3: 554 ms per loop
我会选择np.einsum
!
你可以使用蒙面数组 ,但是在我运行的测试中它并不比(angles * belong).sum(1)
快。
掩码数组方法如下所示:
sum_ang = np.ma.masked_where(~belong, angles, copy=False).sum(1).data
在这里,我们创建一个蒙版的angles
数组,其中值~belong
(“不属于”),被屏蔽 (排除)。 我们接受not,因为我们想要排除belong
False
的值。 然后沿着行.sum(1)
获取总和。 sum
将返回另一个屏蔽数组,因此您可以使用该屏蔽数组的.data
属性获取值。
我添加了copy=False
kwarg,这样代码就不会因为数组创建而变慢,但它仍然比你的(angles * belong).sum(1)
方法慢,所以你应该坚持下去。
我找到了一种比einsum解决方案快3倍的方法,我认为它不会更快,所以我用其他方法回答我自己的问题。
我希望的是计算涉及角度的公式, 仅适用于属于True的位置。 这应该加速大约3倍,因为属于真实的大约30%的时间。
我第一次尝试使用角度[属于]将仅计算属于True的位置的公式,但是问题是结果数组是1d而我无法使用np.sum进行行减少。 解决方案是使用np.add.reduceat 。
reduceat可以在特定切片列表中减少ufunc (在本例中为add)。 所以我只需要创建切片列表,这样我就可以减少角度[属于]产生的1d数组。
我会展示我的代码和时间,而且应该单独说出来。
首先我用reduceat解决方案定义一个函数:
def vote_op(angle, belong, THRES_THETA, lines_lengths_vstacked, max_line_length):
intermediate = (0.3 * (1-(angle[belong]/THRES_THETA)) + 0.7 * (lines_lengths_vstacked[belong]/max_line_length))
b_ind = np.hstack([0, np.cumsum(np.sum(belong, axis=1))])
votes = np.add.reduceat(intermediate, b_ind[:-1])
return votes
然后我比较基本方法和einsum方法:
l1 = (lambda angle=angle, belong=belong, THRES_THETA=THRES_THETA, lines_lengths_vstacked=lines_lengths_vstacked, max_line_length=max_line_length:
np.sum(belong*(0.3 * (1-(angle/THRES_THETA)) + 0.7 * (lines_lengths_vstacked/max_line_length)), axis=1))
t1 = timeit.Timer(l1)
print(t1.repeat(3, 100))
l2 = (lambda angle=angle, belong=belong, THRES_THETA=THRES_THETA, lines_lengths_vstacked=lines_lengths_vstacked, max_line_length=max_line_length:
np.einsum('ij,ij->i', belong, 0.3 * (1-(angle/THRES_THETA)) + 0.7 * (lines_lengths_vstacked/max_line_length)))
t2 = timeit.Timer(l2)
print(t2.repeat(3, 100))
l3 = (lambda angle=angle, belong=belong, THRES_THETA=THRES_THETA, lines_lengths_vstacked=lines_lengths_vstacked, max_line_length=max_line_length:
vote_op(angle, belong, THRES_THETA, lines_lengths_vstacked, max_line_length))
t3 = timeit.Timer(l3)
print(t3.repeat(3, 100))
和时间:
[2.866840408487671, 2.6822349628234874, 2.665520338478774]
[2.3444239421490725, 2.352450520946098, 2.4150879511222794]
[0.6846337313820605, 0.660780839464234, 0.6091473217964847]
因此, reduceat解决方案的速度提高了约3倍,并且与其他两个解决方案的结果相同。 请注意,这些结果是稍微大于以前的示例,其中:belongs,angle和lines_lengths_vstacked具有形状:(3400,170)和np.count_nonzero(属于)/belong.size-> 0.16765051903114186
更新由于np.reduceat中的一个极端情况(如在numpy版本'1.11.0rc1'中),它无法正确处理重复索引, 请参阅 ,我必须为有问题的情况添加一个hack to vote_op()函数属于False的整行。 这导致b_ind中的重复索引和投票中的错误结果。 我目前的解决方案是修补错误的值,这是有效的,但又是另一个步骤。 看到新的vote_op():
def vote_op(angle, belong, THRES_THETA, lines_lengths_vstacked, max_line_length):
intermediate = (0.3 * (1-(angle[belong]/THRES_THETA)) + 0.7 * (lines_lengths_vstacked[belong]/max_line_length))
b_rows = np.sum(belong, axis=1)
b_ind = np.hstack([0, np.cumsum(b_rows)])[:-1]
intermediate = np.hstack([intermediate, 0])
votes = np.add.reduceat(intermediate, b_ind)
votes[b_rows == 0] = 0
return votes
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.