如何获得for循环中lil_矩阵元素的索引?

2024-10-02 22:30:04 发布

您现在位置:Python中文网/ 问答频道 /正文

我使用scipy.sparse.lil_matrix创建了一个稀疏矩阵:

import scipy.sparse as sp
test = sp.lil_matrix((3,3))
test[0,0]=1

我可以通过执行以下操作循环并打印非零元素:

for el in test:
    print(el)

打印出(0, 0) 1.0。如何在不打印的情况下访问这两条信息?换句话说,返回索引和值的lil_matrix元素的适当方法是什么?执行el.data返回array([list([])], dtype=object)

注意,我使用的是lil_matrix,因为我需要在一个非常大的双for循环中为它分配非零值


Tags: intestimport元素foras情况矩阵
2条回答

您寻找的显示非常类似于str稀疏矩阵的coo显示

In [216]: M = (sparse.random(5,5,.2)*10).astype(int)
In [217]: M
Out[217]: 
<5x5 sparse matrix of type '<class 'numpy.int64'>'
    with 5 stored elements in COOrdinate format>
In [218]: print(M)   # str(M)
  (0, 0)    0
  (0, 2)    8
  (1, 3)    8
  (1, 4)    8
  (4, 4)    4

稀疏矩阵有一种nonzero方法来显示非零元素的坐标

In [219]: M.nonzero()
Out[219]: (array([0, 1, 1, 4], dtype=int32), array([2, 3, 4, 4], dtype=int32))

对于coo,值存储为3个数组:

In [220]: M.data, M.row, M.col
Out[220]: 
(array([0, 8, 8, 8, 4]),
 array([0, 0, 1, 1, 4], dtype=int32),
 array([0, 2, 3, 4, 4], dtype=int32))

这些元素在coo格式中的顺序没有限制。甚至可能存在重复项,尽管这些项在转换为显示或csr格式时被求和

当我们将其转换为lil格式时,数据现在存储在两个列表数组中,每行一个列表:

In [221]: Ml = M.tolil()
In [222]: Ml.data
Out[222]: 
array([list([0, 8]), list([8, 8]), list([]), list([]), list([4])],
      dtype=object)
In [223]: Ml.rows
Out[223]: 
array([list([0, 2]), list([3, 4]), list([]), list([]), list([4])],
      dtype=object)

它也有nonzero,但看看代码(它使用coo格式):

In [224]: Ml.nonzero()
Out[224]: (array([0, 1, 1, 4], dtype=int32), array([2, 3, 4, 4], dtype=int32))
In [225]: Ml.nonzero??
Signature: Ml.nonzero()
Source:   
    def nonzero(self):
         ...
        # convert to COOrdinate format
        A = self.tocoo()
        nz_mask = A.data != 0
        return (A.row[nz_mask], A.col[nz_mask])
File:      /usr/local/lib/python3.6/dist-packages/scipy/sparse/base.py
Type:      method

实际上,这是所有稀疏格式的通用nonzeronz_mask部分允许矩阵可能有0个尚未清理的值

虽然lil是为方便逐元素更新而设计的,但如果可能,我们通常建议从输入数组的coo样式创建一个矩阵。通常可以更有效地创建这些阵列。即使是列表附加或扩展也可以更快

进一步查看Ml矩阵上的迭代-它为每一行创建一个lil

In [230]: [x for x in Ml]
Out[230]: 
[<1x5 sparse matrix of type '<class 'numpy.int64'>'
    with 2 stored elements in List of Lists format>,
 <1x5 sparse matrix of type '<class 'numpy.int64'>'
    with 2 stored elements in List of Lists format>,
 <1x5 sparse matrix of type '<class 'numpy.int64'>'
    with 0 stored elements in List of Lists format>,
 <1x5 sparse matrix of type '<class 'numpy.int64'>'
    with 0 stored elements in List of Lists format>,
 <1x5 sparse matrix of type '<class 'numpy.int64'>'
    with 1 stored elements in List of Lists format>]

我们可以显示每行的数据:

In [231]: [((i,x.rows[0]),x.data[0]) for i,x in enumerate(Ml)]
Out[231]: 
[((0, [0, 2]), [0, 8]),
 ((1, [3, 4]), [8, 8]),
 ((2, []), []),
 ((3, []), []),
 ((4, [4]), [4])]

或者过滤掉空行:

In [232]: [((i,x.rows[0]),x.data[0]) for i,x in enumerate(Ml) if x.data[0]]
Out[232]: [((0, [0, 2]), [0, 8]), ((1, [3, 4]), [8, 8]), ((4, [4]), [4])]

我们需要另一次迭代来分离每行中的元素

关于稀疏数组与密集数组的使用,一条经验法则是稀疏度(%非零元素)应该小于10%,这样才值得使用稀疏格式。但这在很大程度上取决于你的使用和关注

从简单的数据存储角度来看,请注意coo格式必须为每个非零项使用3个数字,而密集数组仅使用1个数字。稀疏矩阵乘法对于csr格式比较好。其他只关注data值(例如sin)的计算也相对有效。但是,如果数学必须比较两个矩阵的稀疏性,例如加法和元素乘法,稀疏性会更差

索引、切片和求和实际上可能使用矩阵乘法coo格式没有实现这些功能lil可以很好地执行一些面向行的操作。创建稀疏矩阵的基本动作需要时间

都在.data.rows

from scipy import sparse
arr = sparse.random(10,5,format='lil', density=0.5)

对于此包含25个元素的10x5阵列:

>>> arr
<10x5 sparse matrix of type '<class 'numpy.float64'>'
    with 25 stored elements in List of Lists format>

>>> arr.data.shape
(10,)

>>> arr.data
array([list([0.7656088763162588, 0.7262695483137545]),
       list([0.5229054168281109, 0.6329489698531673, 0.9090750679268123]),
       list([0.3285250285217297, 0.12678874412598085, 0.49074613569184733]),
       list([0.9376762935882884]), list([0.7783159122917774]),
       list([0.8750078624527947, 0.017065437987856757, 0.7161352157970525]),
       list([0.6849637433019786, 0.05732598765212671, 0.09948536587262824]),
       list([0.5683250727980487, 0.960851197599538, 0.7540173942047833]),
       list([0.5891879469424754, 0.7901005027272154, 0.5829700379167293]),
       list([0.6266097436787399, 0.8843420498719459, 0.9040791506861361])],
      dtype=object)

.data数组的每个元素都是一个列表,其中包含该行的值

>>> arr.rows
array([list([0, 4]), list([0, 1, 4]), list([1, 3, 4]), list([1]),
       list([3]), list([0, 1, 2]), list([0, 1, 4]), list([1, 2, 3]),
       list([0, 2, 4]), list([0, 1, 3])], dtype=object)

.rows数组的每个元素都是.data中每个非零值的列索引列表

Note that I'm using lil_matrix because I will need to assign nonzero values to it within a very large, double for loop.

这几乎肯定不是一个好主意。lil_matrix的开销意味着,如果它的稀疏度不小于5%,那么填充密集数组几乎肯定会更好。即使这样,也很不确定。这是一种非常糟糕的数据存储格式

编辑:

>>>> for r in arr:
>>>>     print(r.data)

[list([0.7656088763162588, 0.7262695483137545])]
[list([0.5229054168281109, 0.6329489698531673, 0.9090750679268123])]
[list([0.3285250285217297, 0.12678874412598085, 0.49074613569184733])]
[list([0.9376762935882884])]
[list([0.7783159122917774])]
[list([0.8750078624527947, 0.017065437987856757, 0.7161352157970525])]
[list([0.6849637433019786, 0.05732598765212671, 0.09948536587262824])]
[list([0.5683250727980487, 0.960851197599538, 0.7540173942047833])]
[list([0.5891879469424754, 0.7901005027272154, 0.5829700379167293])]
[list([0.6266097436787399, 0.8843420498719459, 0.9040791506861361])]

编辑2:

我不知道你的实际功能或目标是什么,但如果你知道你有多少非零项,你可以预先分配你需要的数组,跳过整个lil的事情

import numpy as np

N = 10000
data, rows, cols = np.zeros(N), np.zeros(N), np.zeros(N)

for i, r in enumerate(_):
    for j, c in enumerate(_):
        _idx = i * len(cols) + j
        data[_idx] = some_data_function()
        rows[_idx] = r
        cols[_idx] = c

arr = sparse.csr_matrix((data, (rows, cols)))

相关问题 更多 >