Умножение в массиве 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.