首页 > 解决方案 > 为什么 .nnz 表示稀疏矩阵中存在更多非零元素?

问题描述

在 SciPy 中,我有一个 CSR 矩阵。我通过从该矩阵中选择每列的某些行来创建一个 LiL 矩阵。然后将结果矩阵转换为 CSR 矩阵。

我有一个特殊情况,每列的所有行都被选中。在执行此操作时,我注意到值的轻微扰动。这是一个MWE。可以使用此处给出的 CSR 矩阵进行尝试。

from scipy import sparse
import numpy as np
ip_mat = sparse.load_npz('a_di_mat.npz')

# this is not useful for me in practice
# this is being done for comparison
lil_mat_direct = ip_mat.tolil()
csr_mat_direct = lil_mat_direct.tocsr()

# I need to copy column-by-column.
# this a MWE representing the special case where the entire column is copied
lil_mat_steps = sparse.lil_matrix((ip_mat.shape), dtype=np.float64)

for i_col in range(ip_mat.shape[1]):
    lil_mat_steps[:, i_col] = ip_mat[:, i_col]

csr_mat_steps = lil_mat_steps.tocsr()

diff_mat = csr_mat_direct - csr_mat_steps

print('nnz: direct copy: {} columnwise copy: {} diff: {}'.format(
    csr_mat_direct.nnz, csr_mat_steps.nnz, diff_mat.nnz))

# a colleague suggested the following
ind_x, ind_y = ip_mat.nonzero()

print('ip_mat: nonzero indices {} nnz {}'.format(len(ind_x), ip_mat.nnz))

在第一个打印语句中,人们期望:

nnz: direct copy: 2886100 columnwise copy: 2886100 diff: 0

然而,一个人获得:

nnz: direct copy: 2886100 columnwise copy: 2879757 diff: 0

差分矩阵全为零表明矩阵非常接近,如果不完全相同的话。如何解释非零数量的减少?这意味着非零值受到干扰。在原始矩阵中非常接近零的值的情况下,它们会受到干扰并在输出矩阵中变为零。恐怕这种扰动会发生在所有非零元素上,并可能影响更一般的情况,即每列只选择行的一个子集。

对于第二个打印语句,可以获得:

ip_mat: nonzero indices 2879757 nnz 2886100

那么,错误是否会妨碍.nnz实施?按列复制是否删除了一些本来应该为零的值?

标签: pythonnumpyscipy

解决方案


我没有尝试遵循您的操作,但可以提出一些差异来源。一些操作(在 csr 上)只是将数据元素设置为 0,而不会将它们从稀疏结构中删除。稀疏矩阵将“非零”元素存储在几个数组中。 nnz本质上报告这些数组的大小。

看代码nonzero

    A = self.tocoo()
    nz_mask = A.data != 0
    return (A.row[nz_mask], A.col[nz_mask])

它进行了额外的data!=0测试,因此可以不同于nnz.

csr还具有eliminate_zeros就地清理稀疏性。此操作非常昂贵,csr不会在您对矩阵执行任何操作时执行它。

所以是的,有可能有一个nnz大于nonzero计数的。对于csr从“从头开始”创建的(即从密集数组或coo样式输入),nnz应该匹配nonzero. 但是是从带有属性数组的文件ip_mat中加载的。如果保存的 csr 不是“干净的”,则加载的 csr 也不是。npzcsr

插图

In [103]: from scipy import sparse                                                                     
In [104]: M = sparse.csr_matrix([[1,0,0,2],[0,3,4,0]])                                                 
In [105]: M                                                                                            
Out[105]: 
<2x4 sparse matrix of type '<class 'numpy.longlong'>'
    with 4 stored elements in Compressed Sparse Row format>
In [106]: M.A                                                                                          
Out[106]: 
array([[1, 0, 0, 2],
       [0, 3, 4, 0]], dtype=int64)
In [107]: M.data                                                                                       
Out[107]: array([1, 2, 3, 4], dtype=int64)

修改元素:

In [108]: M[0,1] = 12                                                                                  
/usr/local/lib/python3.6/dist-packages/scipy/sparse/_index.py:84: SparseEfficiencyWarning: Changing the sparsity structure of a csr_matrix is expensive. lil_matrix is more efficient.
  self._set_intXint(row, col, x.flat[0])
In [109]: M.data                                                                                       
Out[109]: array([ 1, 12,  2,  3,  4], dtype=int64)
In [110]: M                                                                                            
Out[110]: 
<2x4 sparse matrix of type '<class 'numpy.longlong'>'
    with 5 stored elements in Compressed Sparse Row format>
In [111]: M.nnz                                                                                        
Out[111]: 5
In [112]: M[0,1] = 0      # data is set to 0, but indices does not change                                                                                 
In [113]: M                                                                                            
Out[113]: 
<2x4 sparse matrix of type '<class 'numpy.longlong'>'
    with 5 stored elements in Compressed Sparse Row format>
In [114]: M.data                                                                                       
Out[114]: array([1, 0, 2, 3, 4], dtype=int64)

打扫干净:

In [115]: M.eliminate_zeros()                                                                          
In [116]: M                                                                                            
Out[116]: 
<2x4 sparse matrix of type '<class 'numpy.longlong'>'
    with 4 stored elements in Compressed Sparse Row format>

csr从样式输入创建一个数组(数据为 0):

In [120]: M1 = sparse.csr_matrix(([1,0,2,3,4],[0,1,3,1,2],[0,3,5]))                                    
In [121]: M1                                                                                           
Out[121]: 
<2x4 sparse matrix of type '<class 'numpy.int64'>'
    with 5 stored elements in Compressed Sparse Row format>
In [122]: M1.data                                                                                      
Out[122]: array([1, 0, 2, 3, 4])
In [123]: M1.nonzero()                                                                                 
Out[123]: (array([0, 0, 1, 1], dtype=int32), array([0, 3, 1, 2], dtype=int32))
In [124]: M1.eliminate_zeros()                                                                         
In [125]: M1                                                                                           
Out[125]: 
<2x4 sparse matrix of type '<class 'numpy.int64'>'
    with 4 stored elements in Compressed Sparse Row format>

推荐阅读