2015-08-24 20 views
5

Ich habe eine Reihe Array A, die Form (10,) hat.numpy Array Multiplikation mit Arrays von beliebigen Dimensionen

Ich habe auch, ab diesem Moment, ein numpy Array B mit Form (10,3,5). Ich möchte eine Multiplikation zwischen diesen beiden machen, um C so zu erhalten, dass C [0,:,:] = A [0] * B [0,:,:], C [1] = A [1] * B [1 ,:,:], etc.

Ich möchte das nicht mit Schleifen ausarbeiten, ein Grund ist die Ästhetik der Sache, der andere ist, dass dieser Code sehr generisch sein muss. Ich möchte, dass der Benutzer in der Lage ist, so ziemlich jedes beliebige B beliebiger Form einzugeben, solange die führende Dimension 10 ist. Zum Beispiel möchte ich, dass der Benutzer auch ein B mit der Form (10,4) eingeben kann.

Also: Wie kann ich diese Multiplikation mit numpy implementieren? Vielen Dank.

Nachtrag: Wurde zum Beispiel gefragt. Wird kleiner werden. Nehmen wir an, A ist das numplige Array [1,2,3] und B ist das numpy Array [[1,2], [4,5], [7,8]]. Ich möchte, dass die Multiplikation der beiden zu [[1,2], [8,10], [21,24]] führt.

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

Bitte nehmen, * kleine *, zB Arrays und die erwartete Ausgabe. – wwii

+0

Wenn 'B'' (3,5,10) 'ist, funktioniert' A * B'.'numpy' fügt bei Bedarf automatisch Dimensionen am Anfang hinzu (MATLAB fügt sie am Ende hinzu). – hpaulj

+0

Wie oben erwähnt, werden Paare von den am weitesten rechts liegenden Dimensionen der Arrays ausgesendet. Dies bedeutet, dass '(A * B.T) .T' die Länge 10s für die Multiplikation angleichen würde und eine allgemeine Lösung wäre, aber ich denke, dass der'Einsum'-Ansatz, wie er von DSM vorgeschlagen wird, wohlkleiner ist. –

Antwort

4

könnten Sie verwenden None (oder np.newaxis) A zu erweitern B zum Spiel:

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

Das wird aber nur für den Fall 2 arbeiten. Angelehnt an @ajcr, mit genug transponiert können wir impliziten Rundfunk bekommen für den allgemeinen Fall zu arbeiten:

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

Alternativ können Sie einsum verwenden, um die Allgemeinheit zur Verfügung zu stellen. Im Nachhinein ist es wahrscheinlich etwas zu viel verglichen mit der Transponierungsroute, aber es ist praktisch, wenn die Multiplikationen komplizierter sind.

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

und

>>> 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, einige anspruchsvolle Sachen, IMHO! Eigentlich funktioniert deine erste gut mit einer Bearbeitung: >>> np.array ([A [i] * B [i] für i in range (len (A))]) Siehst du irgendein Problem damit? Vielen Dank! –

+1

@ bob.sacamento: Nein, das sollte funktionieren-- Ich habe nur dein Brute-Force-Beispiel kopiert. Da dies jedoch das Array aus einem langsamen Listcomp erstellt, ist es für große Shapes langsam und sollte nur für Testzwecke verwendet werden. – DSM

+1

Es ist langweilig und unelegant, aber 'a [(Scheibe (None),) + (None,) * (b.ndim - 1)] * b 'wird auch Scheiße gemacht ... – Jaime

1

Obwohl ich die einsum Notation mag, werde ich ein wenig Abwechslung in den Mix ....

Sie genug Extra-Dimensionen zu a so hinzufügen können, dass es wird broadcast über b.

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

b hat mehr Dimensionen als a

extra_dims = b.ndim - a.ndim 

Fügen Sie die zusätzliche Dimension (en) zu a

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

Multiply

new_a * b 

Als Funktion:

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