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

1

Скажите, что у меня есть 2D SciPy Sparse Matrix:

import numpy as np
from scipy.sparse import csc_matrix

arr = np.array([[0, 0, 1, 0, 1],
                [1, 0, 0, 1, 0],
                [0, 1, 1, 0, 0],
                [1, 0, 0, 1, 0],
                [0, 1, 0, 0, 0],
               ])

csc = csc_matrix(arr)

Для каждого ненулевого элемента в матрице я хотел бы создать четыре новые разреженные матрицы, которые содержат индекс, соответствующий следующему ближайшему ненулевому соседу Left, Right, Up и Down. Элементы на концах могут иметь соседей, которые обернуты вокруг (представьте круговой двусвязный список в горизонтальном и вертикальном направлениях или тороидальный). В случае, когда элемент является единственным ненулевым элементом в своей строке/столбце, соответствующий индекс будет указывать на себя. Кроме того, поскольку индексы могут иметь нулевое значение (при обращении к первой строке или столбцу) и быть неотличимыми от естественно нулевых элементов, мы устанавливаем эти нулевые индексы равными -1, чтобы отсеять фактический индекс от нулевых элементов.

Для вышеприведенной матрицы плотные матрицы Left и Down будут выглядеть так:

left = np.array([[0, 0, 4,  0, 2],
                 [3, 0, 0, -1, 0],
                 [0, 2, 1,  0, 0],
                 [3, 0, 0, -1, 0],
                 [0, 1, 0,  0, 0],
                ])

down = np.array([[0, 0,  2, 0, -1],
                 [3, 0,  0, 3,  0],
                 [0, 4, -1, 0,  0],
                 [1, 0,  0, 1,  0],
                 [0, 2,  0, 0,  0],
                ])

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

Теги:
numpy
matrix
scipy
sparse-matrix

4 ответа

1
Лучший ответ

Вот возможный способ сделать левого соседа. Это не особенно эффективно, но, вероятно, работает нормально, если во всей матрице не много ненулевых записей. Вы можете немного оптимизировать его, получая ненулевые записи каждой строки по ходу и вычисляя только j[i==row] один раз.

Обратите внимание, что я просто -1 индексы на единицу, а не устанавливаю 0 на -1.

i,j = csc.nonzero()
ind = sp.sparse.csc_matrix(csc.shape,dtype='int')
for row in range(csc.shape[0]):
    ind[row,j[i==row]] = np.roll(j[i==row]+1,1)

ind.A = array([[0, 0, 5, 0, 3],
   [4, 0, 0, 1, 0],
   [0, 3, 2, 0, 0],
   [4, 0, 0, 1, 0],
   [0, 2, 0, 0, 0]])
  • 0
    Я думал np.roll выдает плотную матрицу?
  • 0
    np.roll применяется к массиву индексов ненулевых записей.
Показать ещё 2 комментария
1
In [183]: arr = np.array([[0, 0, 1, 0, 1],
     ...:                 [1, 0, 0, 1, 0],
     ...:                 [0, 1, 1, 0, 0],
     ...:                 [1, 0, 0, 1, 0],
     ...:                 [0, 1, 0, 0, 0],
     ...:                ])
     ...:                
In [184]: from scipy import sparse
In [185]: M = sparse.lil_matrix(arr)
In [186]: M.rows
Out[186]: 
array([list([2, 4]), list([0, 3]), list([1, 2]), list([0, 3]), list([1])],
      dtype=object)

Та же самая информация, которую вы получили бы из плотного массива:

In [187]: [np.where(row)[0] for row in arr]
Out[187]: [array([2, 4]), array([0, 3]), array([1, 2]), array([0, 3]), array([1])]

Я предполагаю, что вы уже выяснили, как генерировать желаемый left (или right) из плотного массива, поэтому я не буду вдаваться в эти детали (мне лень бороться с вашими спецификациями обёртывания).

Для столбцов:

 In [189]: M.T.rows
 Out[189]: 
 array([list([1, 3]), list([2, 4]), list([0, 2]), list([1, 3]), list([0])],
  dtype=object)

Из формата csc вы можете использовать:

In [190]: Mc = sparse.csc_matrix(arr)
In [191]: Mc.indptr
Out[191]: array([0, 2, 4, 6, 8, 9], dtype=int32)
In [192]: Mc.indices
Out[192]: array([1, 3, 2, 4, 0, 2, 1, 3, 0], dtype=int32)
In [193]: for i in range(5):
     ...:     print(Mc.indices[Mc.indptr[i]:Mc.indptr[i+1]])
     ...:     
[1 3]
[2 4]
[0 2]
[1 3]
[0]

В этом примере все строки или столбцы имеют только 1 или 2 ненулевых значения. Я предполагаю, что в более широком, более общем, будет много ненулевых. Также с помощью csccsr существует вероятность того, что индексы для каждой строки не сортируются - есть редкий метод для решения этой проблемы.

Что касается построения возвратной разреженной матрицы, можно изменять data атрибута копии (она будет иметь ту же разреженность).

In [194]: M.data
Out[194]: 
array([list([1, 1]), list([1, 1]), list([1, 1]), list([1, 1]), list([1])],
      dtype=object)
In [195]: Mc.data
Out[195]: array([1, 1, 1, 1, 1, 1, 1, 1, 1], dtype=int64)

Или создайте разреженную матрицу из массивов (как обычно для ввода стиля coo).


С моей lil версией решение tch's немного быстрее:

ind = sparse.lil_matrix(M.shape,dtype='int')
for i,row in enumerate(M.rows):
    k = np.array(row)
    ind[i,k] = np.roll(k+1,1)

Еще лучше с моей идеей замены data:

ind = M.copy()
for row,dat in zip(ind.rows,ind.data):
    k = np.array(row)
    dat[:] = np.roll(k+1,1).tolist()

Или с помощью Mr = Mc.tocsr()

ind = Mr.copy()
for i in range(Mr.shape[0]):
    slc = slice(Mr.indptr[i],Mr.indptr[i+1])
    k = Mr.indices[slc]
    ind.data[slc] = np.roll(k+1,1)
0

Более направленный подход:

csc = csc_matrix(arr)
inds = (csc.indices,csc.indptr)
irows = np.split(*inds)[1:-1]

down = csc_matrix((np.hstack([np.roll(row,-1) for row in irows]),*inds))
up = csc_matrix((np.hstack([np.roll(row,1) for row in irows]),*inds))

Проверьте:

>>> down.A 
array([[0, 0, 2, 0, 0],
       [3, 0, 0, 3, 0],
       [0, 4, 0, 0, 0],
       [1, 0, 0, 1, 0],
       [0, 2, 0, 0, 0]], dtype=int32)

Слева и справа можно получить с помощью представления CSR.

Я не думаю, что кодирование 0 с помощью -1 является хорошей идеей, так как if сломает все редкие улучшения вычислений. только места, спроектированные csc.nonzeros() должны быть посещены.

0

Один возможный ответ (плотная форма):

ix, iy = csc.nonzero()
w = np.where(np.insert(np.diff(ix), 0,1) != 0)[0]
iy2 = np.concatenate([np.roll(_, 1) for _ in np.split(iy,w)])
iy2[iy2==0] = -1

left = csc_matrix(arr.shape)
left[ix, iy] = iy2

ix, iy = csc.transpose().nonzero()
w = np.where(np.insert(np.diff(ix), 0,1) != 0)[0]
iy2 = np.concatenate([np.roll(_, 1) for _ in np.split(iy,w)])
iy2[iy2==0] = -1

down = csc_matrix(arr.T.shape)
down[ix, iy] = iy2
down = down.transpose()
print(left.todense(), '\n', down.todense())


 >> [[ 0  0  4  0  2]
 [ 3  0  0 -1  0]
 [ 0  2  1  0  0]
 [ 3  0  0 -1  0]
 [ 0  1  0  0  0]]

[[ 0  0  2  0 -1]
 [ 3  0  0  3  0]
 [ 0  4 -1  0  0]
 [ 1  0  0  1  0]
 [ 0  2  0  0  0]]
  • 0
    Я думал np.roll выдает плотную матрицу? Мне нужно, чтобы промежуточные данные были в разреженном формате, а конечная матрица также должна быть разреженной.
  • 0
    Я отредактировал ответ. По сути, я предлагаю вам работать только с ненулевыми индексами в плотной форме, свернуть их, а затем поместить в большую матрицу в разреженной форме. Ненулевые индексы (IMO) могут работать в плотной форме, поскольку они представляют только небольшую часть вашей исходной матрицы

Ещё вопросы

Сообщество Overcoder
Наверх
Меню