【发布时间】:2016-11-20 00:44:50
【问题描述】:
我正在尝试计算一个简单的点积,但保留原始矩阵中的非零值不变。一个玩具例子:
import numpy as np
A = np.array([[2, 1, 1, 2],
[0, 2, 1, 0],
[1, 0, 1, 1],
[2, 2, 1, 0]])
B = np.array([[ 0.54331039, 0.41018682, 0.1582158 , 0.3486124 ],
[ 0.68804647, 0.29520239, 0.40654206, 0.20473451],
[ 0.69857579, 0.38958572, 0.30361365, 0.32256483],
[ 0.46195299, 0.79863505, 0.22431876, 0.59054473]])
期望的结果:
C = np.array([[ 2. , 1. , 1. , 2. ],
[ 2.07466874, 2. , 1. , 0.73203386],
[ 1. , 1.5984076 , 1. , 1. ],
[ 2. , 2. , 1. , 1.42925865]])
然而,实际的矩阵是稀疏的,看起来更像这样:
A = sparse.rand(250000, 1700, density=0.001, format='csr')
B = sparse.rand(1700, 1700, density=0.02, format='csr')
一种简单的方法是使用掩码索引设置值,如下所示:
mask = A != 0
C = A.dot(B)
C[mask] = A[mask]
但是,我的原始数组非常稀疏且非常大,因此通过索引分配更改它们非常缓慢。转换为 lil 矩阵有点帮助,但同样,转换本身需要很多时间。
我猜,另一种明显的方法是诉诸迭代并跳过掩码值,但我不想放弃 numpy/scipy 优化的数组乘法的好处。
一些澄清:我实际上对某种特殊情况感兴趣,其中B 总是正方形,因此A 和C 具有相同的形状。因此,如果有一个解决方案不适用于任意数组但适合我的情况,那很好。
更新:一些尝试:
from scipy import sparse
import numpy as np
def naive(A, B):
mask = A != 0
out = A.dot(B).tolil()
out[mask] = A[mask]
return out.tocsr()
def proposed(A, B):
Az = A == 0
R, C = np.where(Az)
out = A.copy()
out[Az] = np.einsum('ij,ji->i', A[R], B[:, C])
return out
%timeit naive(A, B)
1 loops, best of 3: 4.04 s per loop
%timeit proposed(A, B)
/usr/local/lib/python2.7/dist-packages/scipy/sparse/compressed.py:215: SparseEfficiencyWarning: Comparing a sparse matrix with 0 using == is inefficient, try using != instead.
/usr/local/lib/python2.7/dist-packages/scipy/sparse/coo.pyc in __init__(self, arg1, shape, dtype, copy)
173 self.shape = M.shape
174
--> 175 self.row, self.col = M.nonzero()
176 self.data = M[self.row, self.col]
177 self.has_canonical_format = True
MemoryError:
另一个更新:
Cython 不能使任何东西或多或少有用,至少在离 Python 太远的情况下。我们的想法是将点积留给 scipy,并尝试尽可能快地设置这些原始值,如下所示:
cimport cython
@cython.cdivision(True)
@cython.boundscheck(False)
@cython.wraparound(False)
cpdef coo_replace(int [:] row1, int [:] col1, float [:] data1, int[:] row2, int[:] col2, float[:] data2):
cdef int N = row1.shape[0]
cdef int M = row2.shape[0]
cdef int i, j
cdef dict d = {}
for i in range(M):
d[(row2[i], col2[i])] = data2[i]
for j in range(N):
if (row1[j], col1[j]) in d:
data1[j] = d[(row1[j], col1[j])]
这比我之前的第一个“幼稚”实现(使用.tolil())要好一些,但是按照 hpaulj 的方法,可以丢弃 lil。也许用 std::map 之类的东西替换 python dict 会有所帮助。
【问题讨论】:
-
mask的形状为A,而C的形状不一定与A的形状相同。所以,C[mask]没有多大意义,因此我想我们需要更多关于你的意思的细节zero values。 -
哦,当然。忘了提到在我的情况下
B是方形的,coA.dot(B).shape == A.shape。然而,A本身可能不是方形的。但你是对的,这不适用于任意数组。 -
零值是矩阵
A的值等于零。 “原始”矩阵是指A,是的。 -
B也是稀疏的吗? -
是的。 (试图做出 15 个符号的有意义的评论)
标签: python numpy matrix scipy sparse-matrix