Mnożenie w tablicy numpy


88

Próbuję pomnożyć każdy z warunków w tablicy 2D przez odpowiadające im terminy w tablicy 1D. Jest to bardzo proste, jeśli chcę pomnożyć każdą kolumnę przez tablicę 1D, jak pokazano w funkcji numpy.multiply . Ale chcę zrobić odwrotnie, pomnożyć każdy wyraz w wierszu. Innymi słowy, chcę pomnożyć:

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

i dostać

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

ale zamiast tego dostaję

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

Czy ktoś wie, czy istnieje elegancki sposób na zrobienie tego z numpy? Wielkie dzięki, Alex


3
Ach, zorientowałem się, kiedy zadałem pytanie. Najpierw transponuj macierz kwadratową, pomnóż, a następnie transponuj odpowiedź.
Alex S

Lepiej przetransponować wiersz do macierzy kolumnowej, wtedy nie musisz ponownie transponować odpowiedzi. Jeśli A * Bmusiałbyś zrobić, A * B[...,None]co transponuje B, dodając nową oś ( None).
askewchan

Dzięki, to prawda. Problem polega na tym, że masz tablicę 1D wywołującą .transpose () lub .T na niej nie zmienia się ona w tablicę kolumn, pozostawia ją jako wiersz, o ile wiem, musisz zdefiniować ją jako kolumnę od razu. Jak x = [[1],[2],[3]]czy coś.
Alex S,

Odpowiedzi:


115

Normalne mnożenie, jak pokazałeś:

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

Jeśli dodasz oś, pomnoży się tak, jak chcesz:

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

Możesz również transponować dwukrotnie:

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

Dzięki nowej metodzie osi można pomnożyć dwie tablice 1D i wygenerować tablicę 2D. Np [a,b] op [c,d] -> [[a*c, b*c], [a*d, b*d]].
kon psych,

47

Porównałem różne opcje szybkości i stwierdziłem, że - ku mojemu zdziwieniu - wszystkie opcje (z wyjątkiem diag) są równie szybkie. Osobiście używam

A * b[:, None]

(lub (A.T * b).T) ponieważ jest krótki.

wprowadź opis obrazu tutaj


Kod do odtworzenia fabuły:

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
Miły akcent podając kod fabuły. Dzięki.
rocksNwaves

17

Możesz także użyć mnożenia macierzy (inaczej iloczynu skalarnego):

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

numpy.dot(c,a)

To, co jest bardziej eleganckie, jest prawdopodobnie kwestią gustu.


2
miło, +1, o tym nie pomyślałem
jterrace

10
dotto naprawdę przesada. Po prostu robisz niepotrzebne mnożenie przez 0 i dodawanie do 0.
Bi Rico.

2
może to również powodować problemy z pamięcią w przypadku, gdy chcesz zmultiplikować wektor nx1 do macierzy nxd, gdzie d jest większe niż n.
Jonasson

Głosowanie w dół, ponieważ jest to powolne i zużywa dużo pamięci podczas tworzenia gęstej diagmatrycy.
Nico Schlömer

16

Kolejna sztuczka (od wersji 1.6)

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

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

Jestem biegły w numpy broadcasting ( newaxis), ale wciąż znajduję sposób na obejście tego nowego einsumnarzędzia. Więc trochę się pobawiłem, aby znaleźć to rozwiązanie.

Czasy (przy użyciu Ipython timeit):

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

Nawiasem mówiąc, zmiana a ina j, np.einsum('ij,j->ij',A,b)daje macierz, której Alex nie chce. I np.einsum('ji,j->ji',A,b)w efekcie dokonuje podwójnej transpozycji.


1
Jeśli dokonasz tego na komputerze z tablicami na tyle dużymi, że zajmie to co najmniej kilka milisekund i opublikujesz tutaj wyniki wraz z odpowiednimi informacjami o systemie, będzie to bardzo cenne.
Daniel,

1
przy większej tablicy (100x100) liczby względne są mniej więcej takie same. einsumm(25 mikro) jest dwa razy szybsze niż inne (diagnozy kropkowe spowalniają bardziej). To jest np 1.7, świeżo skompilowane z 'libatlas3gf-sse2' i 'libatlas-base-dev' (Ubuntu 10.4, pojedynczy procesor). timeitdaje to, co najlepsze z 10000 pętli.
hpaulj

1
To świetna odpowiedź i myślę, że należało ją zaakceptować. Jednak kod napisany powyżej w rzeczywistości daje macierz, której Alex próbował uniknąć (na moim komputerze). Ten, który hpaulj powiedział, że jest zły, jest w rzeczywistości właściwy.
Yair Daon

Czasy są tutaj mylące. dot-diag jest naprawdę znacznie gorszy niż pozostałe trzy opcje, a einsum też nie jest szybsze niż inne.
Nico Schlömer

@ NicoSchlömer, moja odpowiedź ma prawie 5 lat i wiele numpywersji wstecz.
hpaulj

1

Dla tych zagubionych dusz w google użycie numpy.expand_dimsthen numpy.repeatzadziała i będzie działać również w przypadkach o wyższych wymiarach (tj. Mnożenie kształtu (10, 12, 3) przez 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

Dlaczego po prostu tego nie zrobisz

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

??


6
To dokładne podejście jest już pokazane w zaakceptowanej odpowiedzi, nie widzę, jak to coś dodaje.
Baum mit Augen
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.