我很感激任何帮助,以便在从scipy.sparse包中切割lil_matrix(A)时理解以下行为。
实际上,我想基于行和列的任意索引列表提取子矩阵。
当我使用这两行代码时:
x1 = A[list 1,:]
x2 = x1[:,list 2]
一切都很好,我可以提取正确的子矩阵。
当我尝试在一行中执行此操作时,它失败(返回的矩阵为空)
x=A[list 1,list 2]
为什么会这样?总的来说,我在matlab中使用了类似的命令,并在那里工作。 那么,为什么不使用第一个,因为它有效?这似乎非常耗时。由于我必须经历大量的条目,我想使用单个命令加速它。也许我使用错误的稀疏矩阵类型......任何想法?
答案 0 :(得分:14)
您已使用的方法,
A[list1, :][:, list2]
似乎是从备用矩阵中选择所需值的最快方法。请参阅下面的基准。
但是,要回答有关如何使用单个索引从A
的任意行和列中选择值的问题,
你需要使用所谓的"advanced indexing":
A[np.array(list1)[:,np.newaxis], np.array(list2)]
使用高级索引,如果arr1
和arr2
是NDarrays,则(i,j)
的{{1}}组件等于
A[arr1, arr2]
因此,您希望所有A[arr1[i,j], arr2[i,j]]
arr1[i,j]
等于list1[i]
,并且
所有j
都arr2[i,j]
等于list2[j]
。
可以通过设置在broadcasting(见下文)的帮助下进行安排
i
和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())
答案 1 :(得分:3)
对我来说unutbu的解决方案效果很好,但速度很慢。
我发现这是一个快速的选择,
A = B.tocsr()[np.array(list1),:].tocsc()[:,np.array(list2)]
你可以看到行&#S;和col被单独剪切,但每次都转换为最快的稀疏格式,这次得到索引。
在我的测试环境中,此代码比另一代码快1000倍。
我希望,我不会说错话或犯错误。
答案 2 :(得分:1)
B[arr1, arr2]
中的同步索引确实有效,并且比我机器上的listener's solution更快。请参阅下面的Jupyter示例中的In [5]
。要将其与上述答案进行比较,请参阅In [6]
。此外,我的解决方案不需要.tocsc()
转换,使其更具可读性IMO。
请注意,要使B[arr1, arr2]
生效,arr1
和arr2
必须为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
答案 3 :(得分:-1)
使用以下语法进行切片:
a[1:4]
对于a = array([1,2,3,4,5,6,7,8,9]),结果是
array([2, 3, 4])
元组的第一个参数表示要保留的第一个值,第二个参数表示不保留的第一个值。
如果您在两侧使用列表,则表示您的数组的维度与列表长度一样多。
因此,使用您的语法,您可能需要这样的东西:
x = A[list1,:,list2]
取决于A的形状。
希望它对您有所帮助。