numpy二维布尔数组索引与沿一个轴的reduce

2024-09-29 00:15:00 发布

您现在位置:Python中文网/ 问答频道 /正文

这个问题与this问题相似。在

我有一个2d布尔数组“beyond”和一个2d浮点数组“angles”。 我想要的是沿着行求和相应索引所属的角度,并使用numpy(即避免python循环)。我不需要存储结果行,这些行的长度不同,如链接问题中所述,需要一个列表。在

所以我尝试的是np.总和(angles[beyond],axis=1),但是angles[beyond]返回一个1d结果,我不能按我的要求减少它。我也试过了np.总和(角度*属于,轴=1)这样就行了。但是我想知道是否可以通过只访问属于真的索引来改进时间安排。归属是正确的大约30%的时间和角度是一个较长的公式的简化涉及角度。在

更新

我喜欢用einsum的解,但是在我的实际计算中,速度是很小的。我用角度来简化问题,实际上这是一个使用角度的公式。我怀疑这个公式是针对所有的角度(不管属于什么)计算的,然后传递给einsum,它将执行计算。在

这就是我所做的:

THRESθ和max_line_length是浮点数。 属于,角度和线条长度都有形状(1653,58) 以及np.count_非零(属于)/属于。大小->;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))

结果是:

^{pr2}$

如果我们看最后两行,对应于einsum的行比使用基方法快30%。但是如果我们看前两行,einsum方法的加速速度更小,大约快了0.1%。在

我不确定这个时机是否可以改进。在


Tags: lambdanplinelengthmax角度linestimeit
3条回答

您可以使用masked arrays来实现这一点,但在我运行的测试中,它比(angles * belong).sum(1)快。在

掩码数组方法如下所示:

sum_ang = np.ma.masked_where(~belong, angles, copy=False).sum(1).data

这里,我们正在创建一个angles的掩码数组,其中值~belong(“不属于”)是掩码的(排除)。我们使用而不是,因为我们要排除belong中的False中的值。然后沿着第.sum(1)行取和。sum将返回另一个掩码数组,因此您可以使用该掩码数组的.data属性获取值。在

我添加了copy=Falsekwarg,这样代码就不会因为创建数组而变慢,但是它仍然比(angles * belong).sum(1)方法慢,所以您应该坚持这样做。在

您可以使用^{}-

np.einsum('ij,ij->i',belong,angles)

您也可以使用^{},如下-

^{pr2}$

样本运行-

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的那一个!在

我发现了一种比einsum方法快3倍的方法,而且我不认为它能更快,所以我用另一种方法回答我自己的问题。在

我所希望的是计算出一个公式,这个公式涉及到,属于的位置是真的。这应该会加快大约3倍,因为归属是真实的大约30%的时间。在

我尝试用第一个公式来计算数组的位置,但这不属于第一个问题np.总和. 解决方案是使用np.add.reduceat。在

reduceat可以在特定切片的列表中减少ufunc(在本例中为add)。所以我只需要创建一个切片列表,这样我就可以减少由角度[归属]产生的一维数组。在

我将展示我的代码和时间安排,这应该是独立的。在

首先,我用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方法:

^{pr2}$

时间安排:

[2.866840408487671, 2.6822349628234874, 2.665520338478774]
[2.3444239421490725, 2.352450520946098, 2.4150879511222794]
[0.6846337313820605, 0.660780839464234, 0.6091473217964847]

因此,还原解决方案的速度大约是其他两种方法的3倍,并给出了相同的结果。 请注意,这些结果是针对比之前稍大的示例,其中: 属于,角度和线条长度有形状:(3400170) 以及np.count_非零(属于)/属于。大小->;0.16765051903114186

更新 因为一个角落的案子np.还原(就像在numpy版本'1.11.0rc1')中它不能正确地处理重复的索引,see,我不得不添加hack to vote_op()函数来处理beyond中的整行都是False的情况。这会导致重复索引和错误的投票结果。我目前的解决方案是修补错误的值,这是可行的,但这是另一步。请参见新投票操作():

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

相关问题 更多 >