python-根据列表的Numpy折叠列

在NumPy中,我有一个adxn数组A和一个长度为n的列表L,描述了我希望A的每一列在矩阵B中结束的位置.想法是矩阵B的第i列是A的所有列的总和L中的对应值为i.

我可以使用for循环来做到这一点:

A = np.arange(15).reshape(3,5)
L = [0,1,2,1,1]
n_cols = 3
B = np.zeros((len(A), n_cols)) 
# assume I know the desired number of columns, 
# which is also one more than the maximum value of L
for i, a in enumerate(A.T):
    B[:, L[i]] += a

我想知道是否有一种方法可以通过切片数组A(或以其他方式向量化)来实现?

解决方法:

您正在使用L来选择A列以减少/折叠A列.另外,您将基于L元素的唯一性更新输出数组的列.

因此,您可以将np.add.reduceat用于矢量化解决方案,如下所示-

sidx = L.argsort()
col_idx, grp_start_idx = np.unique(L[sidx],return_index=True)
B_out = np.zeros((len(A), n_cols))
B_out[:,col_idx] = np.add.reduceat(A[:,sidx],grp_start_idx,axis=1)

运行时测试-

In [129]: def org_app(A,n_cols):
     ...:     B = np.zeros((len(A), n_cols)) 
     ...:     for i, a in enumerate(A.T):
     ...:         B[:, L[i]] += a
     ...:     return B
     ...: 
     ...: def vectorized_app(A,n_cols):
     ...:     sidx = L.argsort()
     ...:     col_idx, grp_start_idx = np.unique(L[sidx],return_index=True)
     ...:     B_out = np.zeros((len(A), n_cols))
     ...:     B_out[:,col_idx] = np.add.reduceat(A[:,sidx],grp_start_idx,axis=1)
     ...:     return B_out
     ...: 

In [130]: # Setup inputs with an appreciable no. of cols & lesser rows
     ...: # so as that memory bandwidth to work with huge number of 
     ...: # row elems doesn't become the bottleneck
     ...: d,n_cols = 10,5000
     ...: A = np.random.rand(d,n_cols)
     ...: L = np.random.randint(0,n_cols,(n_cols,))
     ...: 

In [131]: np.allclose(org_app(A,n_cols),vectorized_app(A,n_cols))
Out[131]: True

In [132]: %timeit org_app(A,n_cols)
10 loops, best of 3: 33.3 ms per loop

In [133]: %timeit vectorized_app(A,n_cols)
100 loops, best of 3: 1.87 ms per loop

随着行数与A中cols数可比,矢量化方法对内存带宽的高要求将抵消其明显的提速.

上一篇:如何从9个大小为N的数组快速创建N个3×3矩阵的数组?


下一篇:快速的Python / Numpy频率严重度分布仿真