Множення поперек у масиві numpy


88

Я намагаюсь помножити кожен член у 2D-масиві на відповідні умови в 1D-масиві. Це дуже просто, якщо я хочу помножити кожен стовпець на 1D-масив, як показано у функції 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

Дякую, це правда. Проблема полягає в тому, що у вас є 1D-масив, який викликає .transpose () або .T на ньому не перетворює його на масив стовпців, він залишає його як рядок, тому, наскільки я знаю, ви повинні визначити його як стовпець відразу. Подобається x = [[1],[2],[3]]чи щось.
Alex S

Відповіді:


115

Звичайне множення, як ви показали:

>>> 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]])

За допомогою нового методу осі можна помножити два одновимірних масиви та сформувати 2D-масив. Напр [a,b] op [c,d] -> [[a*c, b*c], [a*d, b*d]].
kon psych

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
Приємний штрих, надаючи код сюжету. Дякую.
rocksNwaves

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

До речі, зміна 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 сказав, що помиляється, насправді є правильним.
Yair Daon

Тут терміни вводять в оману. dot-diag насправді набагато гірший за інші три варіанти, і einsum також не швидший за інші.
Ніко Шлёмер,

@ NicoSchlömer, моя відповідь майже 5 років тому, і багато numpyверсій назад.
hpaulj

1

Для тих загублених душ на Google, використовуючи numpy.expand_dims тоді numpy.repeatбуде працювати, а також працюватиме у випадках вищих розмірів (тобто помножуючи фігуру (10, 12, 3) на a (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.