разреженная (скучная) матрица - PullRequest
11 голосов
/ 30 сентября 2011

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

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

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

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

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

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

x=A[list 1,list 2]

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

Ответы [ 4 ]

14 голосов
/ 30 сентября 2011

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

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] равнялся list1[i] для всех j, а arr2[i,j] равнялся list2[j] для всех 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())
3 голосов
/ 27 октября 2014

для меня решение от unutbu работает хорошо, но медленно.

Я нашел в качестве быстрой альтернативы,

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

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

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

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

1 голос
/ 04 сентября 2017

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

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

A гораздо более быстрое решение , однако, использует 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 (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
0 голосов
/ 30 сентября 2011

нарезка происходит с этим синтаксисом:

a[1:4]

для массива a = ([1,2,3,4,5,6,7,8,9]), результат равен

array([2, 3, 4])

Первый параметр кортежа указывает первое значение, которое должно быть сохранено, а второй параметр указывает первое значение, которое не должно быть сохранено.

Если вы используете списки с обеих сторон, это означает, что ваш массив имеет столько же размеров, сколько длина списков.

Итак, с вашим синтаксисом вам, вероятно, понадобится что-то вроде этого:

x = A[list1,:,list2]

в зависимости от формы А.

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

...