Умножение в массиве numpy


87

Я пытаюсь умножить каждый из членов двумерного массива на соответствующие члены одномерного массива. Это очень просто, если я хочу умножить каждый столбец на одномерный массив, как показано в функции numpy.multiply . Но я хочу сделать наоборот, умножить каждый член в строке. Другими словами, я хочу умножить:

[1,2,3]   [0]
[4,5,6] * [1]
[7,8,9]   [2]

и получить

[0,0,0]
[4,5,6]
[14,16,18]

но вместо этого я получаю

[0,2,6]
[0,5,12]
[0,8,18]

Кто-нибудь знает, есть ли элегантный способ сделать это с помощью numpy? Большое спасибо Алекс


3
Ах, я понял это так же, как задал вопрос. Сначала транспонируйте квадратную матрицу, умножьте, а затем транспонируйте ответ.
Alex S

Лучше транспонировать строку в матрицу столбцов, тогда вам не нужно повторно транспонировать ответ. Если A * Bвам нужно сделать A * B[...,None]транспонирование B, добавив новую ось ( None).
askewchan

Спасибо, правда. Проблема в том, что когда у вас есть одномерный массив, вызывающий .transpose () или .T, он не превращает его в массив столбцов, он оставляет его как строку, поэтому, насколько я знаю, вы должны определить его как столбец с места в карьер. Вроде x = [[1],[2],[3]]что ли.
Alex S

Ответы:


114

Нормальное умножение, как вы показали:

>>> import numpy as np
>>> m = np.array([[1,2,3],[4,5,6],[7,8,9]])
>>> c = np.array([0,1,2])
>>> m * c
array([[ 0,  2,  6],
       [ 0,  5, 12],
       [ 0,  8, 18]])

Если вы добавите ось, она умножится так, как вы хотите:

>>> m * c[:, np.newaxis]
array([[ 0,  0,  0],
       [ 4,  5,  6],
       [14, 16, 18]])

Вы также можете дважды транспонировать:

>>> (m.T * c).T
array([[ 0,  0,  0],
       [ 4,  5,  6],
       [14, 16, 18]])

С новым осевым методом можно умножить два одномерных массива и сгенерировать двухмерный массив. Напр [a,b] op [c,d] -> [[a*c, b*c], [a*d, b*d]].
Kon Psy

47

Я сравнил разные варианты скорости и обнаружил, что, к моему большому удивлению, все варианты (кроме diag) одинаково быстрые. Я лично использую

A * b[:, None]

(или (A.T * b).T) потому что он короткий.

введите описание изображения здесь


Код для воспроизведения сюжета:

import numpy
import perfplot


def newaxis(data):
    A, b = data
    return A * b[:, numpy.newaxis]


def none(data):
    A, b = data
    return A * b[:, None]


def double_transpose(data):
    A, b = data
    return (A.T * b).T


def double_transpose_contiguous(data):
    A, b = data
    return numpy.ascontiguousarray((A.T * b).T)


def diag_dot(data):
    A, b = data
    return numpy.dot(numpy.diag(b), A)


def einsum(data):
    A, b = data
    return numpy.einsum("ij,i->ij", A, b)


perfplot.save(
    "p.png",
    setup=lambda n: (numpy.random.rand(n, n), numpy.random.rand(n)),
    kernels=[
        newaxis,
        none,
        double_transpose,
        double_transpose_contiguous,
        diag_dot,
        einsum,
    ],
    n_range=[2 ** k for k in range(14)],
    logx=True,
    logy=True,
    xlabel="len(A), len(b)",
)

2
Приятный штрих, обеспечивающий код сюжета. Спасибо.
RockNwaves,

17

Вы также можете использовать матричное умножение (также известное как точечный продукт):

a = [[1,2,3],[4,5,6],[7,8,9]]
b = [0,1,2]
c = numpy.diag(b)

numpy.dot(c,a)

Что изящнее, наверное, дело вкуса.


2
хорошо, +1, не подумал об этом
jterrace

10
dotздесь действительно перебор. Вы просто делаете ненужное умножение на 0 и прибавление к 0.
Би Рико

2
это также может вызвать проблемы с памятью в случае, если вы хотите умножить вектор nx1 на матрицу nxd, где d больше n.
Jonasson

Голосование против, так как это происходит медленно и использует много памяти при создании плотной diagматрицы.
Нико Шлёмер

16

Еще одна уловка (начиная с v1.6)

A=np.arange(1,10).reshape(3,3)
b=np.arange(3)

np.einsum('ij,i->ij',A,b)

Я хорошо разбираюсь в numpy broadcasting ( newaxis), но я все еще ищу способ использовать этот новый einsumинструмент. Поэтому мне пришлось немного поиграться, чтобы найти это решение.

Сроки (с использованием Ipython timeit):

einsum: 4.9 micro
transpose: 8.1 micro
newaxis: 8.35 micro
dot-diag: 10.5 micro

Между прочим, изменение a iна j, np.einsum('ij,j->ij',A,b)дает матрицу, которую Алекс не хочет. И np.einsum('ji,j->ji',A,b), по сути, делает двойной транспонирование.


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

1
при большем массиве (100x100) относительные числа примерно такие же. einsumm(25 микро) в два раза быстрее остальных (точка-диагональ тормозит больше). Это np 1.7, недавно скомпилированный с помощью libatlas3gf-sse2 и libatlas-base-dev (Ubuntu 10.4, однопроцессорный). timeitдает максимум 10000 петель.
hpaulj

1
Это отличный ответ, и я думаю, что его следовало принять. Однако приведенный выше код действительно дает матрицу, которую Алекс пытался избежать (на моей машине). То, что hpaulj сказал неправильно, на самом деле правильное.
Яир Даон

Время здесь вводит в заблуждение. dot-diag действительно намного хуже, чем три других варианта, и einsum не быстрее, чем другие.
Нико Шлёмер

@ NicoSchlömer, моему ответу почти 5 лет, и много numpyверсий назад.
hpaulj

1

Для тех потерянных душ в Google, использование numpy.expand_dimsthen numpy.repeatбудет работать, а также будет работать в случаях более высоких измерений (например, умножение формы (10, 12, 3) на (10, 12)).

>>> import numpy
>>> a = numpy.array([[1,2,3],[4,5,6],[7,8,9]])
>>> b = numpy.array([0,1,2])
>>> b0 = numpy.expand_dims(b, axis = 0)
>>> b0 = numpy.repeat(b0, a.shape[0], axis = 0)
>>> b1 = numpy.expand_dims(b, axis = 1)
>>> b1 = numpy.repeat(b1, a.shape[1], axis = 1)
>>> a*b0
array([[ 0,  2,  6],
   [ 0,  5, 12],
   [ 0,  8, 18]])
>>> a*b1
array([[ 0,  0,  0],
   [ 4,  5,  6],
   [14, 16, 18]])

-4

Почему бы тебе просто не сделать

>>> m = np.array([[1,2,3],[4,5,6],[7,8,9]])
>>> c = np.array([0,1,2])
>>> (m.T * c).T

??


6
Этот точный подход уже показан в принятом ответе, я не понимаю, как это что-то добавляет.
Baum mit Augen
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.