标准化 Scipy 稀疏矩阵的有效方法

问题描述 投票:0回答:6

我想编写一个函数来规范化大型稀疏矩阵的行(这样它们总和为一个)。

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,但我仍然不清楚如何实际进行除法。

python numpy scipy sparse-matrix
6个回答
49
投票

这已在 scikit-learn sklearn.preprocessing.normalize.

中实现
from sklearn.preprocessing import normalize
w_normalized = normalize(w, norm='l1', axis=1)

axis=1
应该按行归一化,
axis=0
按列归一化。使用可选参数
copy=False
就地修改矩阵。


3
投票

虽然 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 秒。


2
投票

这是我的解决方案。

  • 转置A
  • 计算每列的总和
  • 用总和的倒数格式化对角矩阵 B
  • A*B 等于归一化
  • 转置 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()
    

1
投票

我发现这是一种不使用内置函数的优雅方式。

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)  

0
投票

无需导入 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()
按每行的最大值归一化。


0
投票

我的设置: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)
© www.soinside.com 2019 - 2024. All rights reserved.