我有一个具有8000x64元素的2D数组(C),一个具有8000x1元素的1D数组和另一个具有1x64元素的1D数组(d)。 索引i的每一行(其中s [i]为True)应与向量d相加。 效果很好:
Opel AG
现在,我在C,s和d上添加了一个维,并且以上逻辑将应用于附加维的每个元素。
下面的代码可以实现我想要的,但是非常慢。
C[s == True] += d
在没有for循环的情况下,是否有一种麻木的方法来做到这一点?
答案 0 :(得分:2)
在开始时添加额外的尺寸会更容易:
In [376]: C = np.zeros((4,2,3),int)
In [377]: s = np.array([[0,0],[0,1],[1,0],[1,1]],bool)
In [378]: d = np.arange(1,13).reshape(4,3)
In [379]: C.shape, s.shape, d.shape
Out[379]: ((4, 2, 3), (4, 2), (4, 3))
In [380]: I,J = np.nonzero(s)
In [381]: I,J
Out[381]: (array([1, 2, 3, 3]), array([1, 0, 0, 1]))
In [383]: C[I,J]=d[I]
In [384]: C
Out[384]:
array([[[ 0, 0, 0],
[ 0, 0, 0]],
[[ 0, 0, 0],
[ 4, 5, 6]],
[[ 7, 8, 9],
[ 0, 0, 0]],
[[10, 11, 12],
[10, 11, 12]]])
您的方式:
In [385]: C = np.zeros((4,2,3),int)
In [386]: for i in range(4):
...: C[i,:,:][s[i,:]] += d[i,:]
...:
In [387]: C
Out[387]:
array([[[ 0, 0, 0],
[ 0, 0, 0]],
[[ 0, 0, 0],
[ 4, 5, 6]],
[[ 7, 8, 9],
[ 0, 0, 0]],
[[10, 11, 12],
[10, 11, 12]]])
答案 1 :(得分:1)
由于numpy索引的工作方式,在第一个示例中,s
选择了C
的相关行。要在3D情况下执行相同的操作,必须将C
重塑为(8000*3, 64)
,并将s
重塑为(8000*3, 1)
。现在唯一的问题是让d
来说明每个三维维度中不同的行数,这可以通过np.repeat
完成。
第一部分是
C2 = np.swapaxes(C, -1, 1).reshape(-1, 64)
这效率极低,因为它会复制整个阵列。如果C
首先具有形状(3, 8000, 64)
,则是更好的安排。然后,您只需要拉开前两个轴即可获得正确的形状和内存布局,而无需复制数据。
repeats = np.count_nonzero(s, axis=0)
C.reshape(-1, 64)[s.ravel()] += np.repeat(d, repeats, axis=0)
由于在这种情况下,整形操作会返回一个视图,因此索引应该可以正常工作以就地增加。我认为这种方法不一定很好,因为它在新维度的相应元素中将d
的每一行复制为s
非零的次数。
答案 2 :(得分:0)
这是我建议的@hpaulj方法的实现。请注意,我不想从他那里得到荣誉,因此请标记他的回答,而不是我的正确。只想分享我的所作所为。
import numpy as np
import numpy.random as npr
C = np.zeros((100, 8000, 64), dtype=int)
s = np.zeros((100, 8000), dtype=bool)
d = np.zeros((100, 64), dtype=int)
C[:,:,:] = npr.randint(50, size=C.shape)
s[:,:] = npr.randint(3, size=s.shape)
d[:,:] = npr.randint(10, size=d.shape)
I, J = np.nonzero(s)
C[I, J] += d[I]
然后我分析了我制作的程序,该程序在我的机器上运行不到450毫秒(最后两行不到300毫秒)。请注意,对“ randint”的调用只是用于设置数组值,因此这些行不适用于您的用例。