Подтвердить что ты не робот

Эффективный способ нормализации Scipy Sparse Matrix

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

from pylab import *
import scipy.sparse as sp

def normalize(W):
    z = W.sum(0)
    z[z < 1e-6] = 1e-6
    return W / z[None,:]

w = (rand(10,10)<0.1)*rand(10,10)
w = sp.csr_matrix(w)
w = normalize(w)

Однако это дает следующее исключение:

File "/usr/lib/python2.6/dist-packages/scipy/sparse/base.py", line 325, in __div__
     return self.__truediv__(other)
File "/usr/lib/python2.6/dist-packages/scipy/sparse/compressed.py", line 230, in  __truediv__
   raise NotImplementedError

Существуют ли разумно простые решения? Я просмотрел этот, но до сих пор неясно, как на самом деле выполнять разделение.

4b9b3361

Ответ 1

Это было реализовано в scikit-learn sklearn.preprocessing.normalize.

from sklearn.preprocessing import normalize
w_normalized = normalize(w, norm='l1', axis=1)

axis=1 должен нормализоваться по строкам, axis=0 для нормализации по столбцу. Используйте необязательный аргумент copy=False для изменения матрицы на месте.

Ответ 2

вот мое решение.

  • транспонировать A
  • вычислить сумму каждого col
  • формат диагональной матрицы B с обратной суммой
  • A * B равно нормализации
  • транспонировать C

    import scipy.sparse as sp
    import numpy as np
    import math
    
    minf = 0.0001
    
    A = sp.lil_matrix((5,5))
    b = np.arange(0,5)
    A.setdiag(b[:-1], k=1)
    A.setdiag(b)
    print A.todense()
    A = A.T
    print A.todense()
    
    sum_of_col = A.sum(0).tolist()
    print sum_of_col
    c = []
    for i in sum_of_col:
        for j in i:
            if math.fabs(j)<minf:
                c.append(0)
            else:
                c.append(1/j)
    
    print c
    
    B = sp.lil_matrix((5,5))
    B.setdiag(c)
    print B.todense()
    
    C = A*B
    print C.todense()
    C = C.T
    print C.todense()
    

Ответ 3

Хотя ответ Аарона верен, я реализовал решение, когда хотел нормализовать по отношению к максимуму абсолютных значений, которые не предлагает sklearn. Мой метод использует ненулевые записи и находит их в массиве csr_matrix.data для быстрой замены значений там.

def normalize_sparse(csr_matrix):
    nonzero_rows = csr_matrix.nonzero()[0]
    for idx in np.unique(nonzero_rows):
        data_idx = np.where(nonzero_rows==idx)[0]
        abs_max = np.max(np.abs(csr_matrix.data[data_idx]))
        if abs_max != 0:
            csr_matrix.data[data_idx] = 1./abs_max * csr_matrix.data[data_idx]

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

Ответ 4

Без импорта sklearn, преобразования в плотные или умножающиеся матрицы и использования представления данных матриц csr:

from scipy.sparse import isspmatrix_csr

def normalize(W):
    """ row normalize scipy sparse csr matrices inplace.
    """
    if not isspmatrix_csr(W):
        raise ValueError('W must be in CSR format.')
    else:
        for i in range(W.shape[0]):
            row_sum = W.data[W.indptr[i]:W.indptr[i+1]].sum()
            if row_sum != 0:
                W.data[W.indptr[i]:W.indptr[i+1]] /= row_sum

Помните, что W.indices - это массив индексов столбцов, W.data - это массив соответствующих ненулевых значений, а W.indptr указывает на начало строк в индексах и данных.

Вы можете добавить numpy.abs() при получении суммы, если вам нужна норма L1, или использовать numpy.max() для нормализации по максимальному значению в строке.