你已经在使用的方法,
A[list1, :][:, list2]
似乎是从备用矩阵中选择所需值的最快方式。请参见下面的基准测试。
但是,如果要使用单个索引从任意行和列中选择
A
的值,您需要使用所谓的
"高级索引":
A[np.array(list1)[:,np.newaxis], np.array(list2)]
使用高级索引,如果arr1
和arr2
是NDarrays,那么A[arr1, arr2]
的(i,j)
分量等于
A[arr1[i,j], arr2[i,j]]
因此,您希望对于所有的j,
arr1[i,j]
等于
list1[i]
,并且对于所有的i,
arr2[i,j]
等于
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()
B = B[np.array(list1)[:, np.newaxis], np.array(list2)]
return B
def using_listener(A, list1, list2):
"""https://dev59.com/S2sz5IYBdhLWcg3w3bxb#26592783 (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())