大きなスパース行列の行を正規化する関数(合計が1になるような関数)を書きたいのですが。
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
を使用して、マトリックスを適切に変更します。
これが私の解決策です。
転置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()
Aaronsの答えは正しいですが、sklearnが提供していないabsolute値の最大値に関して正規化したいときに、ソリューションを実装しました。私の方法では、ゼロ以外のエントリを使用し、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秒かかりました。
これは、組み込み関数を使用せずにそれを行うエレガントな方法であることがわかりました。
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
_
Remember _W.indices
_は列インデックスの配列であり、_W.data
_は対応する非ゼロ値の配列であり、_W.indptr
_はインデックスとデータで行の先頭を指します。
L1ノルムが必要な場合は、合計を取るときにnumpy.abs()
を追加するか、numpy.max()
を使用して、行ごとの最大値で正規化できます。