我想编写一个函数来规范化大型稀疏矩阵的行(这样它们总和为一个)。
from pylab import *
import scipy.sparse as sp
def normalize(W):
z = W.sum(0)
z[z < 1e-6] = 1e-6
return W / z[None,:]
w = (rand(10,10)<0.1)*rand(10,10)
w = sp.csr_matrix(w)
w = normalize(w)
然而,这给出了以下例外:
File "/usr/lib/python2.6/dist-packages/scipy/sparse/base.py", line 325, in __div__
return self.__truediv__(other)
File "/usr/lib/python2.6/dist-packages/scipy/sparse/compressed.py", line 230, in __truediv__
raise NotImplementedError
是否有任何相当简单的解决方案?我看过this,但我仍然不清楚如何实际进行除法。
这已在 scikit-learn sklearn.preprocessing.normalize.
中实现from sklearn.preprocessing import normalize
w_normalized = normalize(w, norm='l1', axis=1)
axis=1
应该按行归一化,axis=0
按列归一化。使用可选参数 copy=False
就地修改矩阵。
虽然 Aarons 的回答是正确的,但当我想对 absolute 值的最大值进行归一化时,我实施了一个解决方案,sklearn 没有提供。我的方法使用非零条目并在 csr_matrix.data 数组中找到它们以快速替换那里的值。
def normalize_sparse(csr_matrix):
nonzero_rows = csr_matrix.nonzero()[0]
for idx in np.unique(nonzero_rows):
data_idx = np.where(nonzero_rows==idx)[0]
abs_max = np.max(np.abs(csr_matrix.data[data_idx]))
if abs_max != 0:
csr_matrix.data[data_idx] = 1./abs_max * csr_matrix.data[data_idx]
与 sunan 的解决方案相反,此方法不需要将矩阵转换为密集格式(这可能会引发内存问题),也不需要矩阵乘法。我在形状为 (35'000, 486'000) 的稀疏矩阵上测试了该方法,耗时约 18 秒。
这是我的解决方案。
转置 C
import scipy.sparse as sp
import numpy as np
import math
minf = 0.0001
A = sp.lil_matrix((5,5))
b = np.arange(0,5)
A.setdiag(b[:-1], k=1)
A.setdiag(b)
print A.todense()
A = A.T
print A.todense()
sum_of_col = A.sum(0).tolist()
print sum_of_col
c = []
for i in sum_of_col:
for j in i:
if math.fabs(j)<minf:
c.append(0)
else:
c.append(1/j)
print c
B = sp.lil_matrix((5,5))
B.setdiag(c)
print B.todense()
C = A*B
print C.todense()
C = C.T
print C.todense()
我发现这是一种不使用内置函数的优雅方式。
import scipy.sparse as sp
def normalize(W):
#Find the row scalars as a Matrix_(n,1)
rowSumW = sp.csr_matrix(W.sum(axis=1))
rowSumW.data = 1/rowSumW.data
#Find the diagonal matrix to scale the rows
rowSumW = rowSumW.transpose()
scaling_matrix = sp.diags(rowSumW.toarray()[0])
return scaling_matrix.dot(W)
无需导入 sklearn,转换为密集或乘法矩阵并利用 csr 矩阵的数据表示:
from scipy.sparse import isspmatrix_csr
def normalize(W):
""" row normalize scipy sparse csr matrices inplace.
"""
if not isspmatrix_csr(W):
raise ValueError('W must be in CSR format.')
else:
for i in range(W.shape[0]):
row_sum = W.data[W.indptr[i]:W.indptr[i+1]].sum()
if row_sum != 0:
W.data[W.indptr[i]:W.indptr[i+1]] /= row_sum
W.indices
是列索引数组,
W.data
是相应非零值的数组
和W.indptr
指向索引和数据中的行开始。
如果需要L1范数,可以在求和的时候加上
numpy.abs()
,或者用numpy.max()
按每行的最大值归一化。
我的设置:Python 3.8.10,SciPy 1.5.4
仅使用 csr_array 你现在可以使用它的
multiply
和 sum
方法并做:
# w : scipy.sparse.csr_array
row_sum = w.sum(axis=1)
row_sum[row_sum == 0] = 1 # to avoid divide by zero
w = w.multiply(1. / row_sum)