разрезающая разреженная (scipy) матрица

Я был бы признателен за любую помощь, чтобы понять следующее поведение при разрезании lil_matrix (A) из пакета scipy.sparse.

На самом деле, я хотел бы извлечь подматрицу на основе произвольного списка индексов для обеих строк и столбцов.

Когда я использовал две строки кода:

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

Все было хорошо, и я мог извлечь нужную подматрицу.

Когда я попытался сделать это в одной строке, он потерпел неудачу (возвращающая матрица была пуста)

 x=A[list 1,list 2] 

Почему это так? В целом, я использовал аналогичную команду в Matlab, и там она работает. Итак, почему бы не использовать первый, так как он работает? Кажется, это занимает много времени. Поскольку мне нужно пройти через большое количество записей, я бы хотел ускорить его с помощью одной команды. Может быть, я использую неправильный разреженный тип матрицы … Любая идея?

4 Solutions collect form web for “разрезающая разреженная (scipy) матрица”

Метод, который вы уже используете,

 A[list1, :][:, list2] 

представляется самым быстрым способом выбора желаемых значений из матрицы запасных частей. См. Ниже для теста.

Однако, чтобы ответить на вопрос о том, как выбирать значения из произвольных строк и столбцов A с одним индексом , вам нужно будет использовать так называемую «расширенную индексацию» :

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

При расширенном индексировании, если arr1 и arr2 являются NDarrays, компонент (i,j) A[arr1, arr2] равен

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

Таким образом, вы хотите, чтобы arr1[i,j] равным arr1[i,j] list1[i] для всех j и arr2[i,j] равным list2[j] для всех i .

Это можно организовать с помощью трансляции (см. Ниже), установив arr1 = np.array(list1)[:,np.newaxis] и arr2 = np.array(list2) .

Форма arr1 равна (len(list1), 1) 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] list1,: 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)] 

Вы можете видеть, что строки и столбцы обрабатываются отдельно, но каждый из них преобразуется в самый быстрый разреженный формат, чтобы получить индекс на этот раз.

В моей тестовой среде этот код в 1000 раз быстрее, чем другой.

Надеюсь, я не скажу что-то не так или ошибаюсь.

Одновременная индексация, как в B[arr1, arr2] , работает, и она быстрее, чем решение для слушателя на моей машине. См. In [5] в примере Jupyter ниже. Чтобы сравнить его с указанным ответом, см. In [6] . Кроме того, моему решению не требуется преобразование .tocsc() , что делает его более читаемым IMO.

Обратите внимание, что для работы B[arr1, arr2] , arr1 и arr2 должны быть широковещательными массивами numpy.

Однако гораздо более быстрое решение использует B[list1][:, list2] как указано 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 (nx 1)-array : arr1 : : Out[3]: array([[1], [4], [6], [8]]) In [4]: arr2 = np.array(list2)[None, :] # make arr2 a (1 xm)-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.

Надеюсь, это помогло вам.

Python - лучший язык программирования в мире.