2015-08-24 11 views
5

Mam tablicę numpy A, która ma kształt (10,).mnożenie macierzy szeregowej z tablicami o dowolnych wymiarach

Mam również, od tego momentu, tablicę numpy B o kształcie (10,3,5). Chcę wykonać pomnożenie między tymi dwoma, aby uzyskać C takie, że C [0,:,:] = A [0] * B [0,:,:], C [1] = A [1] * B [1 ,:,:], itp.

Nie chcę tego robić z pętlami, jednym z powodów jest estetyka rzeczy, a drugą jest to, że ten kod musi być bardzo ogólny. Chcę, aby użytkownik mógł wprowadzić dowolnie dowolny B o dowolnym kształcie, o ile wymiar wiodący wynosi 10. Na przykład, chcę, aby użytkownik mógł również wstawić kształt B (10,4).

Więc: Jak zaimplementować to mnożenie za pomocą numpy? Dzięki.

ADDENDUM: Zostałem poproszony na przykład. Zmniejszy się. Powiedzmy, że A to tablica numpy [1,2,3], a B to tablica numpy [[1,2], [4,5], [7,8]]. Chcę, aby pomnożenie tych dwóch wyników doprowadziło do [[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

Proszę podać, * mały *, przykładowe tablice i oczekiwany wynik. – wwii

+0

Jeśli "B" to '(3,5,10)', '' * B' działa.'numpy' automatycznie dodaje wymiary na początku w miarę potrzeb (MATLAB dodaje je na końcu). – hpaulj

+0

Jak wspomniano powyżej, rozgłaszanie par z najbardziej prawych wymiarów tablic. Oznacza to, że '(A * B.T) .T' będzie odpowiadać długości 10 s dla mnożenia i będzie ogólnym rozwiązaniem, ale myślę, że podejście' einsum' sugerowane przez DSM jest prawdopodobnie lepsze. –

Odpowiedz

4

Można użyć None (lub), aby rozwinąć np.newaxisA dopasować 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 

Ale to będzie działać tylko dla przypadku 2. Pożyczanie od @ajcr, z wystarczająco transponuje możemy uzyskać niejawny nadawać do pracy w ogólnym przypadku:

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

Alternatywnie, można użyć einsum dostarczenie ogólności. Z perspektywy czasu jest to prawdopodobnie przesada w porównaniu z trasą transpozycji, ale jest to przydatne, gdy multiplikacje są bardziej skomplikowane.

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

i

>>> 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, niektóre wyrafinowane rzeczy, IMHO! Właściwie to twoja pierwsza działa ładnie z edycją: >>> np.array ([A [i] * B [i] dla i w zakresie (len (A))]) Widzisz jakiś problem z tym? Dzięki! –

+1

@ bob.sacamento: nie, to powinno działać-- Właśnie kopiowałem twój przykład brutalnej siły. Ale ponieważ tworzy to tablicę z powolnego listcomp, będzie on powolny dla dużych kształtów i powinien być używany tylko do testowania. – DSM

+1

Jest nudny i nieelegancki, ale 'a [(slice (None),) + (None,) * (b.ndim - 1)] * b' również dostaje gówno zrobione ... – Jaime

1

Chociaż lubię notacji einsum, dodam trochę odmiany do mieszanki ....

Możesz dodać wystarczającą ilość dodatkowych wymiarów, tak że do a będzie broadcast przez b.

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

b ma więcej niż wymiary a

extra_dims = b.ndim - a.ndim 

Dodać dodatkowy wymiar (e), aby a

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

pomnożyć

new_a * b 

Jako funkcja:

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 
Powiązane problemy