此问题类似于this一个。
我有一个2d布尔数组"属于"和一个2d浮点阵列"角度"。 我想要的是沿着行总和相应索引所属的角度为True,并使用numpy(即避免python循环)。我不需要存储结果行,这些行具有不同的长度,并且如链接的问题中所解释的那样需要列表。
所以我尝试的是np.sum(角度[属于],轴= 1),但角度[属于]返回1d结果,我无法按照我的意愿减少它。我也试过np.sum(角度*属于,轴= 1),这是有效的。但我想知道我是否可以通过只访问属于True的索引来改善时间。属于真是大约30%的时间和角度是一个涉及角度的较长公式的简化。
更新
我喜欢使用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%左右。
我不确定这个时间是否可以改善。
答案 0 :(得分:2)
您可以使用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
一个!
答案 1 :(得分:1)
您可以使用masked arrays,但在我运行的测试中,不比(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)
方法慢,所以你应该坚持下去。
答案 2 :(得分:0)
我找到了一种比einsum解决方案快3倍的方法,而且我认为它不会更快,所以我用其他方法回答了我自己的问题。
我所希望的是计算涉及属于真的位置的角度只的公式。这应该加速大约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倍,并且与其他两个解决方案的结果相同。 请注意,这些结果仅用于比之前更大的示例: 属于,角度和lines_lengths_vstacked有形状:(3400,170) 和np.count_nonzero(所属)/belong.size->0.16765051903114186
<强>更新强> 由于np.reduceat中的一个极端情况(如numpy版本&#39; 1.11.0rc1&#39;),它无法正确处理重复索引,see,我不得不添加一个黑客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