【问题标题】:slicing sparse (scipy) matrix切片稀疏(scipy)矩阵
【发布时间】:2011-11-28 09:00:59
【问题描述】:

如果从 scipy.sparse 包中切片 lil_matrix (A) 时了解以下行为,我将不胜感激。

实际上,我想根据行和列的任意索引列表提取一个子矩阵。

当我使用这两行代码时:

x1 = A[list 1,:]
x2 = x1[:,list 2]

一切都很好,我可以提取正确的子矩阵。

当我尝试在一行中执行此操作时,它失败了(返回矩阵为空)

x=A[list 1,list 2]

为什么会这样?总的来说,我在 matlab 中使用了一个类似的命令,并且它在那里工作。 那么,既然它有效,为什么不使用第一个呢?这似乎非常耗时。由于我必须浏览大量条目,因此我想使用单个命令加快速度。也许我使用了错误的稀疏矩阵类型...知道吗?

【问题讨论】:

  • list1 和 list2 的内容是什么?是什么给了 A[list1:list2] ??
  • list1 和 list 2 是包含整数的 python 列表对象,例如[1,4,6,8] A[list1:list2] 为空(',其中 0 个以链接列表格式存储的元素>

标签: python scipy slice sparse-matrix submatrix


【解决方案1】:

使用以下语法进行切片:

a[1:4]

对于a = array([1,2,3,4,5,6,7,8,9]),结果是

array([2, 3, 4])

元组的第一个参数表示要保留的第一个值,第二个参数表示不保留的第一个值。

如果你在两边都使用列表,这意味着你的数组的维度与列表长度一样多。

所以,根据你的语法,你可能需要这样的东西:

x = A[list1,:,list2]

取决于 A 的形状。

希望对你有所帮助。

【讨论】:

  • 问题不在于array()。
【解决方案2】:

你已经在使用的方法,

A[list1, :][:, list2]

似乎是从备用矩阵中选择所需值的最快方法。请参阅下面的基准。

但是,要回答您关于如何从A使用单个索引的任意行和列中选择值的问题, 你需要使用所谓的"advanced indexing":

A[np.array(list1)[:,np.newaxis], np.array(list2)]

使用高级索引,如果 arr1arr2 是 NDarray,则 A[arr1, arr2](i,j) 组件等于

A[arr1[i,j], arr2[i,j]]

因此,对于所有 j,您会希望 arr1[i,j] 等于 list1[i],并且 对于所有 iarr2[i,j] 等于 list2[j]

这可以通过设置在broadcasting(见下文)的帮助下进行安排 arr1 = np.array(list1)[:,np.newaxis]arr2 = np.array(list2)

arr1 的形状是 (len(list1), 1)arr2 的形状是 (len(list2), ) 广播到 (1, len(list2)),因为添加了新轴 需要时自动在左侧。

每个数组可以进一步广播以形成(len(list1),len(list2))。 这正是我们想要的 A[arr1[i,j],arr2[i,j]] 是有意义的,因为我们希望 (i,j) 遍历形状为 (len(list1),len(list2)) 的结果数组的所有可能索引。


这是一个测试用例的微基准,表明A[list1, :][:, list2] 是最快的选择:

In [32]: %timeit orig(A, list1, list2)
10 loops, best of 3: 110 ms per loop

In [34]: %timeit using_listener(A, list1, list2)
1 loop, best of 3: 1.29 s per loop

In [33]: %timeit using_advanced_indexing(A, list1, list2)
1 loop, best of 3: 1.8 s per loop

这是我用于基准测试的设置:

import numpy as np
import scipy.sparse as sparse
import random
random.seed(1)

def setup(N):
    A = sparse.rand(N, N, .1, format='lil')
    list1 = np.random.choice(N, size=N//10, replace=False).tolist()
    list2 = np.random.choice(N, size=N//20, replace=False).tolist()
    return A, list1, list2

def orig(A, list1, list2):
    return A[list1, :][:, list2]

def using_advanced_indexing(A, list1, list2):
    B = A.tocsc()  # or `.tocsr()`
    B = B[np.array(list1)[:, np.newaxis], np.array(list2)]
    return B

def using_listener(A, list1, list2):
    """https://stackoverflow.com/a/26592783/190597 (listener)"""
    B = A.tocsr()[list1, :].tocsc()[:, list2]
    return B

N = 10000
A, list1, list2 = setup(N)
B = orig(A, list1, list2)
C = using_advanced_indexing(A, list1, list2)
D = using_listener(A, list1, list2)
assert np.allclose(B.toarray(), C.toarray())
assert np.allclose(B.toarray(), D.toarray())

【讨论】:

  • 谢谢。这看起来很优雅,但请记住,我使用的是 scipy.sparse 包中的稀疏矩阵。不幸的是,这种索引不起作用。它给出了一个 IndexError。
  • 嗯。事实上,它不适用于lil_matrix,但它确实适用于csc_matrixcsr_matrix
  • 非常感谢。这很有帮助。
  • 在我看来,像A[list1,:][:,list2] 这样的东西给出了相同的结果,但在稀疏矩阵上运行得更快。
  • @unutbu,请考虑在您的回答中包含我对该主题的新回答,就像您对听众的解决方案所做的那样。
【解决方案3】:

对我来说,来自 unutbu 的解决方案效果很好,但速度很慢。

我发现这是一个快速的替代方案,

A = B.tocsr()[np.array(list1),:].tocsc()[:,np.array(list2)]

可以看到 row'S 和 col's 被分别切割,但每个都转换为最快的稀疏格式,以获取本次索引。

在我的测试环境中,这段代码比另一个快 1000 倍。

我希望,我不会说错话或犯错误。

【讨论】:

    【解决方案4】:

    B[arr1, arr2] 中的同时索引确实有效,而且在我的机器上它比 listener's solution 更快。请参阅下面 Jupyter 示例中的 In [5]。要将其与上述答案进行比较,请参阅In [6]。此外,我的解决方案不需要 .tocsc() 转换,使其更具 IMO 可读性。

    请注意,要使 B[arr1, arr2] 工作,arr1arr2 必须是 broadcastable numpy 数组。

    然而,更快的解决方案是使用B[list1][:, list2] 作为pointed out by unutbu。请参阅下面的In [7]

    In [1]: from scipy import sparse
          : import numpy as np
          : 
          : 
    
    In [2]: B = sparse.rand(1000, 1000, .1, format='lil')
          : list1=[1,4,6,8]
          : list2=[2,4]
          : 
          : 
    
    In [3]: arr1 = np.array(list1)[:, None]  # make arr1 a (n x 1)-array
          : arr1
          : 
          : 
    Out[3]: 
    array([[1],
           [4],
           [6],
           [8]])
    
    In [4]: arr2 = np.array(list2)[None, :]  # make arr2 a (1 x m)-array
          : arr2
          : 
          : 
    Out[4]: array([[2, 4]])
    
    In [5]: %timeit A = B.tocsr()[arr1, arr2]
    100 loops, best of 3: 13.1 ms per loop
    
    In [6]: %timeit A = B.tocsr()[np.array(list1),:].tocsc()[:,np.array(list2)]
    100 loops, best of 3: 14.6 ms per loop
    
    In [7]: %timeit B[list1][:, list2]
    1000 loops, best of 3: 205 µs per loop
    

    【讨论】:

      猜你喜欢
      • 2019-02-12
      • 2019-10-04
      • 1970-01-01
      • 1970-01-01
      • 2023-03-03
      • 2017-03-26
      • 2017-03-31
      • 2023-04-10
      • 2017-07-21
      相关资源
      最近更新 更多