Moltiplicando in un array numpy


89

Sto cercando di moltiplicare ciascuno dei termini in un array 2D per i termini corrispondenti in un array 1D. Questo è molto semplice se voglio moltiplicare ogni colonna per l'array 1D, come mostrato nella funzione numpy.multiply . Ma io voglio fare il contrario, moltiplicare ogni termine nella riga. In altre parole, voglio moltiplicare:

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

e prendi

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

ma invece ottengo

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

Qualcuno sa se c'è un modo elegante per farlo con numpy? Grazie mille, Alex


3
Ah, l'ho capito proprio mentre facevo la domanda. Trasponi prima la matrice quadrata, moltiplica, quindi trasponi la risposta.
Alex S

È meglio trasporre la riga in una matrice di colonna, quindi non è necessario trasporre nuovamente la risposta. Se A * Bdevi eseguire la A * B[...,None]trasposizione Baggiungendo un nuovo asse ( None).
Askewchan

Grazie, è vero. Il problema è quando hai un array 1D che chiama .transpose () o .T su di esso non lo trasforma in un array di colonne, lo lascia come una riga, quindi per quanto ne so devi definirlo come una colonna su due piedi. Mi piace x = [[1],[2],[3]]o qualcosa del genere.
Alex S

Risposte:


119

Moltiplicazione normale come hai mostrato:

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

Se aggiungi un asse, si moltiplicherà nel modo desiderato:

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

Puoi anche trasporre due volte:

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

Con il nuovo metodo degli assi è possibile moltiplicare due array 1D e generare un array 2D. Ad esempio [a,b] op [c,d] -> [[a*c, b*c], [a*d, b*d]].
kon psych,

50

Ho confrontato le diverse opzioni per la velocità e ho scoperto che, con mia grande sorpresa, tutte le opzioni (tranne diag) sono ugualmente veloci. Io personalmente uso

A * b[:, None]

(o (A.T * b).T) perché è breve.

inserisci qui la descrizione dell'immagine


Codice per riprodurre la trama:

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(13)],
    xlabel="len(A), len(b)",
)

2
Bel tocco fornendo il codice per la trama. Grazie.
rocksNwaves

17

Puoi anche usare la moltiplicazione della matrice (noto anche come prodotto a punti):

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

numpy.dot(c,a)

Quale sia più elegante è probabilmente una questione di gusti.


2
bello, +1, non ci
avevo pensato

10
dotè davvero eccessivo qui. Stai solo facendo moltiplicazioni inutili per 0 e aggiunte a 0.
Bi Rico

2
questo potrebbe anche innescare problemi di memoria nel caso in cui si desideri moltiplicare un vettore nx1 in una matrice nxd dove d è maggiore di n.
Jonasson

Il downvoting poiché è lento e utilizza molta memoria durante la creazione della diagmatrice densa .
Nico Schlömer

16

Ancora un altro trucco (a partire dalla v1.6)

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

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

Sono abile con la trasmissione numpy ( newaxis), ma sto ancora cercando di orientarmi su questo nuovo einsumstrumento. Quindi ho dovuto giocare un po 'per trovare questa soluzione.

Tempi (utilizzando Ipython timeit):

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

Per inciso, cambiando un ia j, np.einsum('ij,j->ij',A,b)produce la matrice che Alex non vuole. E np.einsum('ji,j->ji',A,b)fa, in effetti, la doppia trasposizione.


1
Se cronometri questo su un computer con array abbastanza grandi da richiedere almeno alcuni millisecondi e pubblichi i risultati qui insieme alle informazioni di sistema pertinenti, sarebbe molto apprezzato.
Daniel

1
con un array più grande (100x100) i numeri relativi sono più o meno gli stessi. einsumm(25 micro) è due volte più veloce degli altri (dot-diag rallenta di più). Questo è np 1.7, appena compilato con "libatlas3gf-sse2" e "libatlas-base-dev" (Ubuntu 10.4, singolo processore). timeitdà il meglio di 10000 loop.
hpaulj

1
Questa è un'ottima risposta e penso che sia quella che avrebbe dovuto essere accettata. Tuttavia, il codice scritto sopra, in effetti, fornisce la matrice che Alex stava cercando di evitare (sulla mia macchina). Quello che Hpaulj ha detto è sbagliato è in realtà quello giusto.
Yair Daon

I tempi qui sono fuorvianti. dot-diag è davvero molto peggio delle altre tre opzioni e einsum non è nemmeno più veloce delle altre.
Nico Schlömer

@ NicoSchlömer, la mia risposta è vecchia di quasi 5 anni e molte numpyversioni precedenti.
hpaulj

1

Per quelle anime perse su Google, utilizzando numpy.expand_dims then numpy.repeatfunzionerà e funzionerà anche in casi di dimensioni superiori (cioè moltiplicando una forma (10, 12, 3) per 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

Perché non lo fai e basta

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

??


6
Questo approccio esatto è già mostrato nella risposta accettata, non vedo come questo aggiunga qualcosa.
Baum mit Augen
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.