我有一个二进制稀疏的CSR数组。我想通过组合这个原始数组中的列来创建一个新数组。也就是说,我有一个"列组列表":[[1,10,3],[5,54,202],[12,199],[5],...]
对于这些"列组中的每一个"我想将原始数组中的列与OR运算结合起来(np.max适用于此)并将组合列添加到新矩阵中。
我目前的解决方案是使用hstack,但速度很慢:
for cg in column_groups:
tmp = np.max(data_orig[:,cg].toarray(), axis=1, keepdims=True)
data = np.hstack((data, tmp))
答案 0 :(得分:2)
你基本上在每次迭代时选择max
列。因此,我们可以选择所有列,然后使用np.maximum.reduceat
来获得“intervaled-maximum”列,从而为我们提供矢量化解决方案,就像这样 -
def grouped_max(data_orig, column_groups):
cols = np.hstack((column_groups))
clens = np.hstack((0,np.cumsum(map(len,column_groups))[:-1]))
all_data = data_orig[:,cols].toarray()
return np.maximum.reduceat(all_data, clens,axis=1)
对于python 3.x版本,我们需要计算clens
,就像这样 -
clens = np.hstack((0,np.cumsum(list(map(len,column_groups)))[:-1]))
因为loopy版本是沿着组迭代的,所以这个向量化的解决方案在使用大量组时会显示出它的好处。
示例运行 -
In [303]: # Setup sample csr matrix
...: a = np.random.randint(0,3,(12,28))
...: data_orig = sparse.csr_matrix(a)
...:
...: # Random column IDs
...: column_groups = [[1,10,3], [5,14],[2]]
...:
...: data = np.empty((12,0),dtype=int)
...: for cg in column_groups:
...: tmp = np.max(data_orig[:,cg].toarray(), axis=1, keepdims=True)
...: data = np.hstack((data, tmp))
...:
In [304]: out = grouped_max(data_orig, column_groups)
In [305]: # Verify results between original and propsed ones
...: print np.allclose(out, data)
True
答案 1 :(得分:1)
我想,主要问题是hstack正在构建一个新的矩阵 - 在每次迭代中复制大量数据。
我还没有使用稀疏矩阵,所以我可能不合适,但据我从文档中可以理解,可以将它们切成普通的numpy数组。在这种情况下,预先分配数组并按列添加结果是个好主意:
rows = data_orig.shape[0]
cols = len(column_groups)
data = scipy.sparse.csr_matrix((rows, cols))
for cg in enumerate(column_groups):
tmp = np.max(data_orig[:,cg[1]].toarray(), axis=1, keepdims=True)
data[:, cg[0]] = tmp
答案 2 :(得分:1)
In [412]: data_orig=sparse.random(10,300,.2,'csr')
In [413]: data_orig = (data_orig>.5).astype(int)
In [414]: cg = [[1,10,3], [5,54,202], [12,199], [5]]
In [420]: data_orig
Out[420]:
<10x300 sparse matrix of type '<class 'numpy.int32'>'
with 299 stored elements in Compressed Sparse Row format>
对于重复加入,最好附加到列表,并叠加一次:
def test1(data_orig, cg):
data = []
for g in cg:
temp=np.max(data_orig[:,g].A, axis=1,keepdims=True)
data.append(temp)
return np.hstack(data)
In [429]: test1(data_orig, cg)
Out[429]:
array([[0, 1, 0, 1],
[1, 1, 0, 0],
[0, 0, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 1, 0, 0],
[1, 0, 0, 0],
[0, 0, 0, 0]], dtype=int32)
我可以将稀疏矩阵转换为密集矩阵,并得到相同的结果
In [431]: dataM=data_orig.todense()
In [432]: test1(dataM, cg)
我可以使用data_orig.A
但是我必须省略函数中的.A
。
In [433]: timeit test1(data_orig, cg)
100 loops, best of 3: 2.52 ms per loop
In [434]: timeit test1(dataM, cg)
10000 loops, best of 3: 118 µs per loop
这些时间证实了我的猜测,即稀疏列索引相对较慢。
使用Divakar的reduceat
版本:
In [451]: timeit grouped_max(data_orig, cg)
1000 loops, best of 3: 706 µs per loop
In [452]: timeit grouped_max(dataM, cg)
10000 loops, best of 3: 90.3 µs per loop
使用我自己的Py3自适应(差异只是风格):
def grouped_max(data_orig, column_groups):
cols = np.hstack((column_groups))
clens = np.hstack((0,np.cumsum([len(i) for i in cg])[:-1]))
all_data = data_orig[:,cols].A
return np.maximum.reduceat(all_data, clens,axis=1)
稀疏矩阵的加速很快。显然,一个较大的列选择比许多较小的列更快。密集矩阵的加速并不是那么重要。
这样的稀疏矩阵列选择实际上是用矩阵乘法执行的。它构造了另一个稀疏矩阵,其中包含所需列的1s,并执行dot
乘积。行/列总和也使用矩阵乘积进行。
这是一个纯粹的稀疏版本:
def test2(data_orig, cg):
data = []
for g in cg:
temp=data_orig[:,g].max(axis=1)
data.append(temp) # alt append(temp.A)
return sparse.hstack(data) # np.hstack
In [465]: timeit test2(data_orig, cg).A
100 loops, best of 3: 3.21 ms per loop (2.99 with np.hstack)