Multiplizieren in einem numpy Array

86

Ich versuche, jeden der Begriffe in einem 2D-Array mit den entsprechenden Begriffen in einem 1D-Array zu multiplizieren. Dies ist sehr einfach, wenn ich jede Spalte mit dem 1D-Array multiplizieren möchte, wie in der Funktion numpy.multiply gezeigt . Aber ich möchte das Gegenteil tun und jeden Begriff in der Reihe multiplizieren. Mit anderen Worten, ich möchte multiplizieren:

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

und bekomme

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

aber stattdessen bekomme ich

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

Weiß jemand, ob es einen eleganten Weg gibt, dies mit Numpy zu tun? Vielen Dank, Alex

Alex S.
quelle
3
Ah, ich habe es gerade herausgefunden, als ich die Frage eingereicht habe. Transponieren Sie zuerst die quadratische Matrix, multiplizieren Sie sie und transponieren Sie dann die Antwort.
Alex S
Es ist besser, die Zeile in eine Spaltenmatrix zu transponieren, als die Antwort nicht erneut zu transponieren. Wenn A * BSie tun müssten, A * B[...,None]was transponiert, Bindem Sie eine neue Achse hinzufügen ( None).
Askewchan
Danke, das stimmt. Das Problem ist, wenn ein 1D-Array, das .transpose () oder .T aufruft, es nicht in ein Spaltenarray umwandelt, sondern als Zeile belässt. Soweit ich weiß, müssen Sie es als Spalte definieren sofort. Wie x = [[1],[2],[3]]oder so.
Alex S

Antworten:

114

Normale Multiplikation wie Sie gezeigt haben:

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

Wenn Sie eine Achse hinzufügen, wird diese wie gewünscht multipliziert:

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

Sie können auch zweimal transponieren:

>>> (m.T * c).T
array([[ 0,  0,  0],
       [ 4,  5,  6],
       [14, 16, 18]])
jterrace
quelle
Mit der neuen Achsenmethode ist es möglich, zwei 1D-Arrays zu multiplizieren und ein 2D-Array zu generieren. ZB [a,b] op [c,d] -> [[a*c, b*c], [a*d, b*d]].
Kon Psych
47

Ich habe die verschiedenen Optionen für die Geschwindigkeit verglichen und festgestellt, dass - sehr zu meiner Überraschung - alle Optionen (außer diag) gleich schnell sind. Ich persönlich benutze

A * b[:, None]

(oder (A.T * b).T) weil es kurz ist.

Geben Sie hier die Bildbeschreibung ein


Code zur Reproduktion der Handlung:

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)",
)
Nico Schlömer
quelle
2
Nette Geste, die den Code für die Handlung liefert. Vielen Dank.
rocksNwaves
17

Sie können auch die Matrixmultiplikation (auch als Punktprodukt bezeichnet) verwenden:

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

numpy.dot(c,a)

Was eleganter ist, ist wahrscheinlich Geschmackssache.

James K.
quelle
2
nett, +1, habe nicht daran gedacht
jterrace
10
dotist hier wirklich übertrieben. Sie machen nur unnötige Multiplikation mit 0 und Additionen zu 0.
Bi Rico
2
Dies kann auch Speicherprobleme auslösen, wenn Sie einen nx1-Vektor mit einer nxd-Matrix multiplizieren möchten, wobei d größer als n ist.
Jonasson
Das Downvoting ist langsam und benötigt beim Erstellen der dichten diagMatrix viel Speicher .
Nico Schlömer
16

Noch ein Trick (ab v1.6)

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

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

Ich beherrsche die Numpy Broadcasting ( newaxis), finde mich aber immer noch in diesem neuen einsumTool zurecht . Also musste ich ein bisschen herumspielen, um diese Lösung zu finden.

Timings (mit Ipython timeit):

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

Übrigens erzeugt das Ändern von a izu j, np.einsum('ij,j->ij',A,b)die Matrix, die Alex nicht will. Und np.einsum('ji,j->ji',A,b)macht tatsächlich die doppelte Transponierung.

hpaulj
quelle
1
Wenn Sie dies auf einem Computer mit Arrays planen, die groß genug sind, dass es mindestens einige Millisekunden dauert, und die Ergebnisse hier zusammen mit Ihren relevanten Systeminformationen veröffentlichen, wäre dies sehr dankbar.
Daniel
1
Bei einem größeren Array (100 x 100) sind die relativen Zahlen ungefähr gleich. einsumm(25 Mikro) ist doppelt so schnell wie die anderen (Punktdiag verlangsamt sich mehr). Dies ist np 1.7, frisch kompiliert mit 'libatlas3gf-sse2' und 'libatlas-base-dev' (Ubuntu 10.4, Einzelprozessor). timeitgibt das Beste aus 10000 Schleifen.
hpaulj
1
Dies ist eine großartige Antwort, und ich denke, sie hätte akzeptiert werden müssen. Der oben geschriebene Code gibt jedoch tatsächlich die Matrix an, die Alex zu vermeiden versuchte (auf meinem Computer). Der eine, von dem hpaulj sagte, er sei falsch, ist tatsächlich der richtige.
Yair Daon
Die Zeiten sind hier irreführend. dot-diag ist wirklich weitaus schlechter als die anderen drei Optionen, und einsum ist auch nicht schneller als die anderen.
Nico Schlömer
@ NicoSchlömer, meine Antwort ist fast 5 Jahre alt und viele numpyVersionen zurück.
hpaulj
1

Für diese verlorenen Seelen bei Google funktioniert die Verwendung von numpy.expand_dimsthen numpy.repeatund auch in höherdimensionalen Fällen (dh Multiplizieren einer Form (10, 12, 3) mit 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]])
Christopher Pratt
quelle
-4

Warum tust du es nicht einfach?

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

??

Panos
quelle
6
Dieser genaue Ansatz ist bereits in der akzeptierten Antwort gezeigt, ich sehe nicht, wie dies etwas hinzufügt.
Baum mit Augen