Эффективно вычитает вектор из матрицы (Scipy)

У меня есть большая матрица, которая хранится как scipy.sparse.csc_matrix и хочет вычесть вектор столбца из каждого из столбцов в большой матрице. Это довольно распространенная задача, когда вы делаете такие вещи, как нормализация / стандартизация, но я не могу найти правильный способ сделать это эффективно.

Вот пример, демонстрирующий:

# mat is a 3x3 matrix mat = scipy.sparse.csc_matrix([[1, 2, 3], [2, 3, 4], [3, 4, 5]]) #vec is a 3x1 matrix (or a column vector) vec = scipy.sparse.csc_matrix([1,2,3]).T """ I want to subtract `vec` from each of the columns in `mat` yielding... [[0, 1, 2], [0, 1, 2], [0, 1, 2]] """ 

Один из способов добиться того, что я хочу, – это hstack vec для себя 3 раза, давая матрицу 3×3, где каждый столбец vec а затем вычитает из mat . Но опять же, я ищу способ сделать это эффективно, и hstacked матрица занимает много времени, чтобы создать. Я уверен, что есть волшебный способ сделать это с нарезкой и трансляцией, но это ускользает от меня.

Благодаря!

EDIT: Удалено ограничение «на месте», поскольку структура разреженности будет постоянно изменяться в сценарии назначения на месте.

3 Solutions collect form web for “Эффективно вычитает вектор из матрицы (Scipy)”

Для начала, что бы мы сделали с плотными массивами?

 mat-vec.A # taking advantage of broadcasting mat-vec.A[:,[0]*3] # explicit broadcasting mat-vec[:,[0,0,0]] # that also works with csr matrix 

В https://codereview.stackexchange.com/questions/32664/numpy-scipy-optimization/33566 мы обнаружили, что использование as_strided на векторе mat.indptr является наиболее эффективным способом mat.indptr по строкам разреженной матрицы. ( x.rows , x.cols из lil_matrix почти так же хороши. getrow медленный). Эта функция реализует такие операции, как итерация.

 def sum(X,v): rows, cols = X.shape row_start_stop = as_strided(X.indptr, shape=(rows, 2), strides=2*X.indptr.strides) for row, (start, stop) in enumerate(row_start_stop): data = X.data[start:stop] data -= v[row] sum(mat, vec.A) print mat.A 

Я использую vec.A для простоты. Если мы сохраним vec разреженный, нам придется добавить тест для ненулевого значения в row . Также этот тип итераций только изменяет ненулевые элементы mat . 0's не изменяются.

Я подозреваю, что преимущества времени будут во многом зависеть от разреженности матрицы и вектора. Если vec имеет много нулей, то имеет смысл итерации, изменяя только те строки mat где vec отличен от нуля. Но vec почти плотный, как в этом примере, может быть трудно победить mat-vec.A .

Резюме

Короче говоря, если вы используете CSR вместо CSC, это однострочный:

 mat.data -= numpy.repeat(vec.toarray()[0], numpy.diff(mat.indptr)) 

объяснение

Если вы это осознали, это лучше сделать по-разному, так как мы будем вычитать одинаковое число из каждой строки. В вашем примере: вычесть 1 из первой строки, 2 из второй строки, 3 из третьей строки.

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

 mat = scipy.sparse.csc_matrix([[1, 2, 3], [2, 3, 4], [3, 4, 5]]) #vec is a 3x1 matrix (or a column vector) vec = scipy.sparse.csc_matrix([1,2,3]).T # Use the row version mat_row = mat.tocsr() vec_row = vec.T # mat_row.data contains the values in a 1d array, one-by-one from top left to bottom right in row-wise traversal. # mat_row.indptr (an n+1 element array) contains the pointer to each first row in the data, and also to the end of the mat_row.data array # By taking the difference, we basically repeat each element in the row vector to match the number of non-zero elements in each row mat_row.data -= numpy.repeat(vec_row.toarray()[0],numpy.diff(mat_row.indptr)) print mat_row.todense() 

Результат:

 [[0 1 2]
  [0 1 2]
  [0 1 2]]

Визуализация выглядит примерно так:

 >>> mat_row.data [1 2 3 2 3 4 3 4 5] >>> mat_row.indptr [0 3 6 9] >>> numpy.diff(mat_row.indptr) [3 3 3] >>> numpy.repeat(vec_row.toarray()[0],numpy.diff(mat_row.indptr)) [1 1 1 2 2 2 3 3 3] >>> mat_row.data -= numpy.repeat(vec_row.toarray()[0],numpy.diff(mat_row.indptr)) [0 1 2 0 1 2 0 1 2] >>> mat_row.todense() [[0 1 2] [0 1 2] [0 1 2]] 

Вы можете ввести поддельные размеры, изменив strides вашего вектора. Вы можете без дополнительного выделения «преобразовать» свой вектор в матрицу 3 x 3, используя np.lib.stride_tricks.as_strided . На этой странице есть пример и немного обсуждения об этом, а также некоторое обсуждение связанных тем (например, взглядов). Поиск на странице «Пример: поддельные измерения с шагами».

Есть также немало примеров о SO об этом … но мои навыки поиска меня не сбивают.

  • Интерполяция в SciPy: поиск X, который производит Y
  • Оценка хроматической аберрации в питоне
  • Python: слишком много индексов
  • py2exe и numpy не успевают
  • какие все пакеты / библиотеки python находятся в Google App Engine?
  • пытаясь получить разумные значения от scipy powerlaw
  • Структура входных данных для функции минимизации scipy
  • Что такое «скаляр» в numpy?
  • Python - лучший язык программирования в мире.