The method you are already using,
A[list1, :][:, list2]
seems to be the fastest way to select the desired values from a spares matrix. See below for a benchmark.
However, to answer your question about how to select values from arbitrary rows and columns of A with a single index,
you would need to use so-called "advanced indexing":
A[np.array(list1)[:,np.newaxis], np.array(list2)]
With advanced indexing, if arr1 and arr2 are NDarrays, the (i,j) component of A[arr1, arr2] equals
A[arr1[i,j], arr2[i,j]]
Thus you would want arr1[i,j] to equal list1[i] for all j, and
arr2[i,j] to equal list2[j] for all i.
That can be arranged with the help of broadcasting (see below) by setting
arr1 = np.array(list1)[:,np.newaxis], and arr2 = np.array(list2).
The shape of arr1 is (len(list1), 1) while the shape of arr2 is
(len(list2), ) which broadcasts to (1, len(list2)) since new axes are added
on the left automatically when needed.
Each array can be further broadcasted to shape (len(list1),len(list2)).
This is exactly what we want for
A[arr1[i,j],arr2[i,j]] to make sense, since we want (i,j) to run over all possible indices for a result array of shape (len(list1),len(list2)).
Here is a microbenchmark for one test case which suggests that A[list1, :][:, list2] is the fastest option:
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
Here is the setup I used for the benchmark:
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())