【问题标题】:Constructing sparse matrix from list of lists of tuples从元组列表中构造稀疏矩阵
【发布时间】:2017-09-22 22:58:03
【问题描述】:

我有一个稀疏矩阵的行信息 Python 列表。每行表示为(列,值)元组的列表。叫它alist

alist = [[(1,10), (3,-3)],
         [(2,12)]]

我怎样才能有效地从这个列表列表中构造一个 scipy 稀疏矩阵,从而得到一个像这样的矩阵:

0  10   0  -3
0   0  12   0

显而易见的方法是创建一个scipy.sparse.lil_matrix,它在内部具有这种“列表列表”结构。但是从scipy.sparse.lil_matrix — SciPy v0.19.0 Reference Guide 我只看到了三种构造它们的方法:

  • 从密集数组开始
  • 从另一个稀疏数组开始
  • 只是构造一个空数组

因此,获取新数据的唯一方法是使用其他一些稀疏矩阵表示来解决这个问题,或者从密集数组开始,这两种方法都不能解决最初的问题,而且两者似乎都不太可能比lil_matrix 本身更有效地表示此数据。

我想我可以做一个空的,并使用循环来添加值,但我肯定错过了一些东西。

当涉及到稀疏矩阵时,scipy 文档确实令人沮丧。

【问题讨论】:

标签: python constructor scipy sparse-matrix


【解决方案1】:

您的数据布局与众不同。这是我第一次尝试使用它。

In [565]: M = sparse.lil_matrix((2,4), dtype=int)
In [566]: M
Out[566]: 
<2x4 sparse matrix of type '<class 'numpy.int32'>'
    with 0 stored elements in LInked List format>
In [567]: for i,row in enumerate(alist):
     ...:     for col in row:
     ...:         M[i, col[0]] = col[1]
     ...:         
In [568]: M
Out[568]: 
<2x4 sparse matrix of type '<class 'numpy.int32'>'
    with 3 stored elements in LInked List format>
In [569]: M.A
Out[569]: 
array([[ 0, 10,  0, -3],
       [ 0,  0, 12,  0]])

是的,它是迭代的;而lil 是为此目的的最佳格式。

或使用常见的coo 输入样式:

In [580]: data,col,row = [],[],[]
In [581]: for i, rr in enumerate(alist):
     ...:     for cc in rr:
     ...:         row.append(i)
     ...:         col.append(cc[0])
     ...:         data.append(cc[1])
     ...:         
In [582]: data,col,row
Out[582]: ([10, -3, 12], [1, 3, 2], [0, 0, 1])
In [583]: M1=sparse.coo_matrix((data,(row,col)),shape=(2,4))
In [584]: M1
Out[584]: 
<2x4 sparse matrix of type '<class 'numpy.int32'>'
    with 3 stored elements in COOrdinate format>
In [585]: M1.A
Out[585]: 
array([[ 0, 10,  0, -3],
       [ 0,  0, 12,  0]])

另一种选择是创建空白lil矩阵,并直接填写其属性:

换句话说,开始于:

In [591]: m.data
Out[591]: array([[], []], dtype=object)
In [592]: m.rows
Out[592]: array([[], []], dtype=object)

并将它们更改为:

In [587]: M.data
Out[587]: array([[10, -3], [12]], dtype=object)
In [588]: M.rows
Out[588]: array([[1, 3], [2]], dtype=object)

它仍然需要对您的 alist 结构进行 2 级迭代。

In [593]: for i, rr in enumerate(alist):
     ...:     for cc in rr:
     ...:         m.rows[i].append(cc[0])
     ...:         m.data[i].append(cc[1])       
In [594]: m
Out[594]: 
<2x4 sparse matrix of type '<class 'numpy.int32'>'
    with 3 stored elements in LInked List format>
In [595]: m.A
Out[595]: 
array([[ 0, 10,  0, -3],
       [ 0,  0, 12,  0]])

在另一条评论中,您提到了理解 csr indptr 的困难。最简单的方法是转换以下格式之一:

In [597]: Mr=M.tocsr()
In [598]: Mr.indptr
Out[598]: array([0, 2, 3], dtype=int32)
In [599]: Mr.data
Out[599]: array([10, -3, 12])
In [600]: Mr.indices
Out[600]: array([1, 3, 2], dtype=int32)

【讨论】:

  • 这样一个清晰、有用、详细的答案——谢谢!基于 COO 格式的构造函数似乎是最自然的,我可以想出一些生成器来生成它并实现内存和时间高效的输入管道。我希望 scipy 的人以人们能找到的方式添加一些这样的例子。这是我的数据进来的格式,考虑到支持所有这些不同稀疏格式的系统数量,正如Sparse array - Wikipedia 所讨论的那样,我认为更多的人会使用它们交换数据。
  • 我首先在 MATLAB 中使用稀疏矩阵来解决有限元问题。 coo 输入样式是唯一的选择,尽管在内部它将数据存储为 csc (至少这是它保存到 .mat 文件的格式)。大多数稀疏数学是为线性代数问题开发的。 scipy 添加了多种格式(请注意 Wiki 文章中的链接)。现在,稀疏矩阵的大部分兴趣来自大数据问题、稀疏特征矩阵、机器学习、语言学等。 scikit learn 例如添加了一些自己编译的稀疏矩阵实用程序。
  • 我发现令人惊讶的是 OP 的原始数据结构会被认为是不寻常的(与上面的评论相反)
【解决方案2】:

只是想使用coo_matrix 发布另一个答案,这是构造稀疏矩阵的快速格式。

>>> alist = [[(1, 10), (3, -3)], [(2, 12)]]
>>> row, col, data = zip(*((i,j,v) for i,l in enumerate(alist) for j,v in l))
>>>
>>> from scipy.sparse import coo_matrix
>>> S = coo_matrix((data, (row, col)), (max(row)+1,max(col)+1), dtype=np.int8)
>>> S.todense()
matrix([[ 0, 10,  0, -3],
        [ 0,  0, 12,  0]], dtype=int8)
>>> 

【讨论】:

    【解决方案3】:

    如果您在创建稀疏矩阵之前拥有整个alist,则无需使用lil_matrix,因为它已针对增量更新稀疏矩阵进行了优化。

    如果您想对矩阵后缀进行任何类型的算术运算,csr_matrix 可能是您的最佳选择。您可以使用(data, (row, column)) 格式直接构造csr_matrix,如下所示:

    In [40]: alist = [[(1,10), (3,-3)],
        ...:          [(2,12)]]
    
    In [41]: i, j, data = zip(*((i, t[0], t[1]) for i, row in enumerate(alist) for t in row))
    
    In [42]: (i, j, data)
    Out[42]: ((0, 0, 1), (1, 3, 2), (10, -3, 12))
    
    In [43]: csr_matrix((data, (i, j)), shape=(2, 4)).todense()
    Out[43]: 
    matrix([[ 0, 10,  0, -3],
            [ 0,  0, 12,  0]], dtype=int64)
    

    如果真正关心效率,您可以直接创建csr_matrix 内部格式(使用 indptr):

    In [57]: indptr = np.cumsum([0] + [len(row) for row in alist])
    
    In [58]: j, data = zip(*(t for row in alist for t in row))
    
    In [59]: csr_matrix((data, j, indptr), shape=(2, 4)).todense()
    Out[59]: 
    matrix([[ 0, 10,  0, -3],
            [ 0,  0, 12,  0]])
    

    如果您要转换为 pandas 后缀,coo_matrix 是要走的路,因为 pandas 无论如何都会转换为 coo_matrix

    In [41]: i, j, data = zip(*((i, t[0], t[1]) for i, row in enumerate(alist) for t in row))
    
    In [43]: coo_matrix((data, (i, j)), shape=(2, 4))
    

    【讨论】:

    • 感谢您的审议!实际上,在我的特定情况下,我只需要将它作为稀疏特征矩阵与其他数据框列一起使用,然后在上面拟合 sklearn 模型 (.fit)
    • 在这种情况下 coo_matrix 是可行的方法,因为 pandas 无论如何都会转换为该格式
    • coo_matrix 提供形状对性能有帮助/必要吗?即使没有它也可以工作!
    • 如果不提供形状,coo_matrix 会从 shape = (max(i), max(j)) 推导出来。如果最后一列或最后一行全为零,这可能会导致问题,因为您将从矩阵中丢失该行/列。
    【解决方案4】:

    您可以从(列,值)元组alist 的列表中创建位置和值的dict,然后使用dok_matrix 构造稀疏矩阵

    >>> d = {(i,j):v for i,l in enumerate(alist) for j,v in l}
    >>> d
    {(0, 1): 10, (0, 3): -3, (1, 2): 12}
    >>> 
    >>> from operator import itemgetter
    >>> m = max(d.keys(), key=itemgetter(0))[0] + 1
    >>> n = max(d.keys(), key=itemgetter(1))[1] + 1
    >>> m,n
    (2, 4)
    >>>
    >>> from scipy.sparse import dok_matrix
    >>> S = dok_matrix((m,n), dtype=int)
    >>> for pos,v in d.items():
    ...     S[pos] = v
    ... 
    >>> S.todense()
    matrix([[ 0, 10,  0, -3],
            [ 0,  0, 12,  0]])
    >>> 
    

    【讨论】:

    • 什么时候以.todense()结尾?
    • 这里有什么绝对需要itemgetter的理由吗?
    • @matanster。 .todense() 只是为了展示矩阵的样子
    • @matanster。 itemgetter 不是必需的。我们只需要自动获取尺寸。您可以将其替换为lambda t: t[0]。所以它看起来像 m = max(d.keys(), key=lambda t: t[0])[0] + 1n = max(d.keys(), key=lambda t: t[1])[1] + 1
    猜你喜欢
    • 1970-01-01
    • 2013-11-29
    • 2012-06-20
    • 1970-01-01
    • 2016-10-04
    • 1970-01-01
    • 1970-01-01
    • 2016-07-29
    • 2021-09-22
    相关资源
    最近更新 更多