繁体   English   中英

切片稀疏(scipy)矩阵

[英]slicing sparse (scipy) matrix

感谢任何帮助,以便在从scipy.sparse包中切割lil_matrix(A)时理解以下行为。

实际上,我想基于行和列的任意索引列表提取子矩阵。

当我使用这两行代码时:

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

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

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

x=A[list 1,list 2]

为什么会这样? 总的来说,我在matlab中使用了类似的命令,并在那里工作。 那么,为什么不使用第一个,因为它有效? 这似乎非常耗时。 由于我必须经历大量的条目,我想使用单个命令加速它。 也许我使用错误的稀疏矩阵类型......任何想法?

你已经使用的方法,

A[list1, :][:, list2]

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

但是,要回答有关如何使用单个索引A任意行和列中选择值的问题,您需要使用所谓的“高级索引”

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

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

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

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

这可以通过设置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())

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

我发现这是一个快速的选择,

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

您可以看到行'S和col'被单独切割,但每一行都转换为最快的稀疏格式,这次得到索引。

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

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

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

请注意,要使B[arr1, arr2]起作用, arr1arr2必须是可广播的 numpy数组。

然而, 更快的解决方案是使用unutbu指出的 B[list1][:, list2] 见下面的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

使用以下语法进行切片:

a[1:4]

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

array([2, 3, 4])

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

如果您在两侧都使用列表,则表示您的数组具有与列表长度一样多的维度。

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

x = A[list1,:,list2]

取决于A的形状。

希望它能帮助你。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM