Python 使用scipy.sparse.csc_矩阵替代numpy广播

Python 使用scipy.sparse.csc_矩阵替代numpy广播,python,numpy,scipy,sparse-matrix,Python,Numpy,Scipy,Sparse Matrix,我的代码中有以下表达式: a = (b / x[:, np.newaxis]).sum(axis=1) 其中b是形状(M,N)的一个数组,x是形状(M,)的一个数组。现在,b实际上是稀疏的,所以为了提高内存效率,我想用scipy.sparse.csc_矩阵或csr_矩阵代替。但是,不以这种方式实现广播(即使保证除法或乘法保持稀疏性)(x的条目不为零),并引发NotImplementedError。是否有我不知道的sparse函数可以实现我想要的功能?(dot()将沿着错误的轴求和。)要实现a=

我的代码中有以下表达式:

a = (b / x[:, np.newaxis]).sum(axis=1)
其中
b
是形状
(M,N)
的一个数组,
x
是形状
(M,)
的一个数组。现在,
b
实际上是稀疏的,所以为了提高内存效率,我想用
scipy.sparse.csc_矩阵
csr_矩阵
代替。但是,不以这种方式实现广播(即使保证除法或乘法保持稀疏性)(x的条目不为零),并引发
NotImplementedError
。是否有我不知道的
sparse
函数可以实现我想要的功能?(
dot()
将沿着错误的轴求和。)

要实现
a=(b/x[:,np.newaxis]).sum(axis=1)
,可以使用
a=b.sum(axis=1).A1/x。
A1
属性返回1D ndarray,因此结果是1D ndarray,而不是
矩阵。这个简洁的表达式之所以有效,是因为您同时按
x
缩放并沿轴1求和。例如:

In [190]: b
Out[190]: 
<3x3 sparse matrix of type '<type 'numpy.float64'>'
        with 5 stored elements in Compressed Sparse Row format>

In [191]: b.A
Out[191]: 
array([[ 1.,  0.,  2.],
       [ 0.,  3.,  0.],
       [ 4.,  0.,  5.]])

In [192]: x
Out[192]: array([ 2.,  3.,  4.])

In [193]: b.sum(axis=1).A1 / x
Out[193]: array([ 1.5 ,  1.  ,  2.25])
In [71]: from scipy.sparse import csc_matrix, spdiags

In [72]: b = csc_matrix([[1,0,2],[0,3,0],[4,0,5]], dtype=np.float64)

In [73]: b.A
Out[73]: 
array([[ 1.,  0.,  2.],
       [ 0.,  3.,  0.],
       [ 4.,  0.,  5.]])

In [74]: x = array([2., 3., 4.])

In [75]: d = spdiags(1.0/x, 0, len(x), len(x))

In [76]: d.A
Out[76]: 
array([[ 0.5       ,  0.        ,  0.        ],
       [ 0.        ,  0.33333333,  0.        ],
       [ 0.        ,  0.        ,  0.25      ]])

In [77]: p = d * b

In [78]: p.A
Out[78]: 
array([[ 0.5 ,  0.  ,  1.  ],
       [ 0.  ,  1.  ,  0.  ],
       [ 1.  ,  0.  ,  1.25]])

In [79]: a = p.sum(axis=1)

In [80]: a
Out[80]: 
matrix([[ 1.5 ],
        [ 1.  ],
        [ 2.25]])

如果
b
为CSC格式,则
b.data
具有
b
的非零项,
b.index
具有每个非零项的行索引,因此您可以按以下方式进行除法:

b.data /= np.take(x, b.indices)
它比Warren优雅的解决方案更具黑客性,但在大多数情况下也可能更快:

b = sps.rand(1000, 1000, density=0.01, format='csc')
x = np.random.rand(1000)

def row_divide_col_reduce(b, x):
    data = b.data.copy() / np.take(x, b.indices)
    ret = sps.csc_matrix((data, b.indices.copy(), b.indptr.copy()),
                         shape=b.shape)
    return ret.sum(axis=1)

def row_divide_col_reduce_bis(b, x):
    d = sps.spdiags(1.0/x, 0, len(x), len(x))
    return (d * b).sum(axis=1)

In [2]: %timeit row_divide_col_reduce(b, x)
1000 loops, best of 3: 210 us per loop

In [3]: %timeit row_divide_col_reduce_bis(b, x)
1000 loops, best of 3: 697 us per loop

In [4]: np.allclose(row_divide_col_reduce(b, x),
   ...:             row_divide_col_reduce_bis(b, x))
Out[4]: True
在上述示例中,如果进行适当的划分,您可以将时间缩短一半,即:

def row_divide_col_reduce(b, x):
    b.data /= np.take(x, b.indices)
    return b.sum(axis=1)

In [2]: %timeit row_divide_col_reduce(b, x)
10000 loops, best of 3: 131 us per loop

为了清楚起见,您希望沿轴1进行元素分割吗?i、 例如,
b[i,:]
的所有
N
元素被
x[i]
除?是的。“要清楚”是我包含代码的原因这甚至适用于
M!=N
只要
x
的对角矩阵具有形状
(M,M)
。谢谢Warren!对不起,我选择了Jaime更快的方法。。。我真的在速度和优雅之间挣扎!这两种方法都很棒,能准确地解决我的问题。还要注意,我的问题有点说错了,我还需要在沿轴求和之前将
xlogx()
应用到
b
(0 log(0)定义为等于0),因此我仍然需要对b.data进行操作!别担心,胡安。Jaime的答案很好。为什么你选择了
np.take(x,b.index)
而不是
x[b.index]
?@askewchan它通常更快,我正试图让它尽可能快地运行。谢谢Jaime!我知道我可以对
b.data
进行操作,但我在概念上缺少
np.take
调用!美好的