Argmax каждой строки или столбца в скудной разреженной матрице
scipy.sparse.coo_matrix.max
возвращает максимальное значение каждой строки или столбца, заданной оси. Я хотел бы знать не значение, а индекс максимального значения каждой строки или столбца. Я еще не нашел способ сделать это эффективно, поэтому с радостью приму любую помощь.
6 ответов
С версии Scipy 0.19, оба csr_matrix
а также csc_matrix
служба поддержки argmax()
а также argmin()
методы.
Я бы предложил изучить код для
moo._min_or_max_axis
где moo
это coo_matrix
,
mat = mat.tocsc() # for axis=0
mat.sum_duplicates()
major_index, value = mat._minor_reduce(min_or_max)
not_full = np.diff(mat.indptr)[major_index] < N
value[not_full] = min_or_max(value[not_full], 0)
mask = value != 0
major_index = np.compress(mask, major_index)
value = np.compress(mask, value)
return coo_matrix((value, (np.zeros(len(value)), major_index)),
dtype=self.dtype, shape=(1, M))
В зависимости от оси предпочитает работать с csc над csr. У меня не было времени, чтобы проанализировать это, но я предполагаю, что можно включить argmax
в расчете.
Это предложение может не сработать. Ключ является mat._minor_reduce
метод, который делает, с некоторым уточнением:
ufunc.reduceat(mat.data, mat.indptr[:-1])
То есть применяется ufunc
к блокам матрицы data
массив, используя indptr
определить блоки. np.sum
, np.maxiumum
являются ufunc
где это работает. Я не знаю эквивалента argmax
ufunc.
В общем, если вы хотите сделать что-то по 'row' для матрицы csr (или col of csc), вы должны либо выполнить итерацию по строкам, что относительно дорого, либо использовать это ufunc.reduceat
сделать то же самое по квартире mat.data
вектор.
группа argmax/argmin над индексами разбиения в numpy пытается выполнить argmax.reduceat
, Решение может быть адаптировано к разреженной матрице.
Последний выпуск пакета numpy_indexed (заявление об отказе: я его автор) может решить эту проблему эффективным и элегантным способом:
import numpy_indexed as npi
col, argmax = group_by(coo.col).argmax(coo.data)
row = coo.row[argmax]
Здесь мы группируем по col, поэтому это argmax по столбцам; поменяв ряд и столбец, вы получите argmax по строкам.
Если A
твой scipy.sparse.coo_matrix
затем вы получите строку и столбец максимального значения следующим образом:
I=A.data.argmax()
maxrow = A.row[I]
maxcol=A.col[I]
Чтобы получить индекс максимального значения в каждой строке, см. РЕДАКТИРОВАТЬ ниже:
from scipy.sparse import coo_matrix
import numpy as np
row = np.array([0, 3, 1, 0])
col = np.array([0, 2, 3, 2])
data = np.array([-3, 4, 11, -7])
A= coo_matrix((data, (row, col)), shape=(4, 4))
print A.toarray()
nrRows=A.shape[0]
maxrowind=[]
for i in range(nrRows):
r = A.getrow(i)# r is 1xA.shape[1] matrix
maxrowind.append( r.indices[r.data.argmax()] if r.nnz else 0)
print maxrowind
r.nnz
количество явно сохраненных значений (т. е. ненулевых значений)
Расширяя ответы от @hpaulj и @joeln и используя код из группы argmax/argmin для разделения индексов в numpy, как предлагается, эта функция будет вычислять argmax по столбцам для CSR или argmax по строкам для CSC:
import numpy as np
import scipy.sparse as sp
def csr_csc_argmax(X, axis=None):
is_csr = isinstance(X, sp.csr_matrix)
is_csc = isinstance(X, sp.csc_matrix)
assert( is_csr or is_csc )
assert( not axis or (is_csr and axis==1) or (is_csc and axis==0) )
major_size = X.shape[0 if is_csr else 1]
major_lengths = np.diff(X.indptr) # group_lengths
major_not_empty = (major_lengths > 0)
result = -np.ones(shape=(major_size,), dtype=X.indices.dtype)
split_at = X.indptr[:-1][major_not_empty]
maxima = np.zeros((major_size,), dtype=X.dtype)
maxima[major_not_empty] = np.maximum.reduceat(X.data, split_at)
all_argmax = np.flatnonzero(np.repeat(maxima, major_lengths) == X.data)
result[major_not_empty] = X.indices[all_argmax[np.searchsorted(all_argmax, split_at)]]
return result
Он возвращает -1 для argmax любых строк (CSR) или столбцов (CSC), которые являются полностью разреженными (то есть, которые полностью равны нулю после X.eliminate_zeros()
).
Как отмечают другие, теперь есть встроенный
argmax()
для
scipy.sparse
матрицы. Однако я обнаружил, что это довольно медленно для больших матриц, поэтому я взглянул на исходный код . Логика очень умная, но в ней есть цикл Python, замедляющий работу. Взяв исходный код и уменьшив его до argmax для каждой строки, например (при этом жертвуя всей общностью, проверкой формы и т.д. для простоты) и украсив его
numba
может дать хорошие улучшения скорости.
Вот функция:
import numpy as np
from numba import jit
def argmax_row_numba(X):
return _argmax_row_numba(X.shape[0], X.indptr, X.data, X.indices)
@jit(nopython=True)
def _argmax_row_numba(shape, indptr, data, indices):
# prep an array to hold the indices
ret = np.zeros(shape)
# figure out which lines actually contain data
nz_lines, = np.diff(indptr).nonzero()
# loop through the lines
for i in nz_lines:
p, q = indptr[i: i + 2]
line_data = data[p: q]
line_indices = indices[p: q]
am = np.argmax(line_data)
ret[i] = line_indices[am]
return ret
Формирование матрицы для тестирования:
from scipy.sparse import random
size = 10000
m = random(m=size, n=size, density=0.0001, format="csr")
n_vals = m.data.shape[0]
m.data = np.random.random(size=n_vals).astype("float")
# the original scipy implementation reformatted to return a np.array
maxima1 = np.squeeze(np.array(m.argmax(axis=1)))
# calling the numba version
maxima2 = argmax_row_numba(m)
# Check that the results are the same
print(np.allclose(maxima1, maxima2))
# True
Результаты по времени:
%timeit m.argmax(axis=1)
# 30.1 ms ± 246 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit argmax_row_numba(m)
# 211 µs ± 1.04 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)