2015-08-24 15 views
5

Ho una matrice numpy A, che ha forma (10,).moltiplicazione di array numpy con matrici di dimensioni arbitrarie

Ho anche, a partire da questo momento, una matrice numpy B con forma (10,3,5). Voglio fare una moltiplicazione tra questi due per ottenere C tale che C [0,:,:] = A [0] * B [0,:,:], C [1] = A [1] * B [1 ,:,:], ecc.

Non voglio risolvere questo problema con i loop, una ragione è l'estetica della cosa, l'altra è che questo codice deve essere molto generico. Voglio che l'utente sia in grado di inserire praticamente qualsiasi B di qualsiasi forma purché la dimensione iniziale sia 10. Ad esempio, voglio che l'utente possa anche inserire una B di forma (10,4).

Quindi: come posso implementare questa moltiplicazione usando numpy? Grazie.

ADDENDUM: è stato chiesto per esempio. Andrà più piccolo. Diciamo che A è la matrice numpy [1,2,3] e B è la matrice numpy [[1,2], [4,5], [7,8]]. Voglio che la moltiplicazione dei due dia come risultato [[1,2], [8,10], [21,24]]. ...

>>> a 
array([1, 2, 3]) 
>>> b 
array([[1, 2], 
     [4, 5], 
     [7, 8]]) 
>>> #result 
>>> c 
array([[ 1, 2], 
     [ 8, 10], 
     [21, 24]]) 
>>> 
+1

Si prega di includere, * piccolo *, esempio gli array e l'output previsto. – wwii

+0

Se 'B' è' (3,5,10) ', il' A * B' funziona.'numpy' aggiunge automaticamente le dimensioni all'inizio come necessario (MATLAB le aggiunge alla fine). – hpaulj

+0

Come notato sopra, le coppie di radiodiffusione si allontanano dalle dimensioni più a destra degli array. Ciò significa che '(A * B.T) .T' corrisponderebbe alla lunghezza 10s per la moltiplicazione e sarebbe una soluzione generale, ma penso che l'approccio' einsum' come suggerito da DSM sia discutibilmente migliore. –

risposta

4

Si potrebbe utilizzare None (o np.newaxis) per espandere A per abbinare B:

>>> A = np.arange(10) 
>>> B = np.random.random((10,3,5)) 
>>> C0 = np.array([A[i]*B[i,:,:] for i in range(len(A))]) 
>>> C1 = A[:,None,None] * B 
>>> np.allclose(C0, C1) 
True 

Ma questo funziona solo per il caso 2. Prendendo in prestito da @ajcr, con abbastanza traspone possiamo ottenere la trasmissione implicita a lavorare per il caso generale:

>>> C3 = (A * B.T).T 
>>> np.allclose(C0, C3) 
True 

In alternativa, è possibile utilizzare einsum di fornire le generalità. In retrospettiva è probabilmente eccessivo qui rispetto alla rotta di trasposizione, ma è utile quando le moltiplicazioni sono più complicate.

>>> C2 = np.einsum('i,i...->i...', A, B) 
>>> np.allclose(C0, C2) 
True 

e

>>> B = np.random.random((10,4)) 
>>> D0 = np.array([A[i]*B[i,:] for i in range(len(A))]) 
>>> D2 = np.einsum('i,i...->i...', A, B) 
>>> np.allclose(D0, D2) 
True 
+0

Whoa, alcune cose sofisticate, IMHO! In realtà, il tuo primo funziona bene con un edit: >>> np.array ([A [i] * B [i] per i in range (len (A))]) Vedi qualche problema con questo? Grazie! –

+1

@ bob.sacamento: no, dovrebbe funzionare ... stavo solo copiando il tuo esempio di forza bruta. Ma dal momento che questo crea l'array da un listcomp lento, sarà lento per le forme grandi e dovrebbe essere usato solo a scopo di test. – DSM

+1

È noioso e inelegante, ma 'a [(slice (None),) + (None,) * (b.ndim - 1)] * b' fa anche merda ... – Jaime

1

Anche se mi piace la notazione einsum, io aggiungo un po 'di varietà al mix ....

È possibile aggiungere abbastanza dimensioni extra a a in modo che sarà broadcast attraverso b.

>>> a.shape 
(3,) 
>>> b.shape 
(3,2) 

b ha più dimensioni rispetto a

extra_dims = b.ndim - a.ndim 

aggiungere la dimensione in più (s) per a

new_shape = a.shape + (1,)*extra_dims # (3,1) 
new_a = a.reshape(new_shape) 

Moltiplicare

new_a * b 

In funzione:

def f(a, b): 
    '''Product across the first dimension of b. 

    Assumes a is 1-dimensional. 
    Raises AssertionError if a.ndim > b.ndim or 
    - the first dimensions are different 
    ''' 
    assert a.shape[0] == b.shape[0], 'First dimension is different' 
    assert b.ndim >= a.ndim, 'a has more dimensions than b' 

    # add extra dimensions so that a will broadcast 
    extra_dims = b.ndim - a.ndim 
    newshape = a.shape + (1,)*extra_dims 
    new_a = a.reshape(newshape) 

    return new_a * b