Próbuję pomnożyć każdy z warunków w tablicy 2D przez odpowiadające im terminy w tablicy 1D. Jest to bardzo proste, jeśli chcę pomnożyć każdą kolumnę przez tablicę 1D, jak pokazano w funkcji numpy.multiply . Ale chcę zrobić odwrotnie, pomnożyć każdy wyraz w wierszu. Innymi słowy, chcę pomnożyć:
[1,2,3] [0]
[4,5,6] * [1]
[7,8,9] [2]
i dostać
[0,0,0]
[4,5,6]
[14,16,18]
ale zamiast tego dostaję
[0,2,6]
[0,5,12]
[0,8,18]
Czy ktoś wie, czy istnieje elegancki sposób na zrobienie tego z numpy? Wielkie dzięki, Alex
A * B
musiałbyś zrobić,A * B[...,None]
co transponujeB
, dodając nową oś (None
).x = [[1],[2],[3]]
czy coś.Odpowiedzi:
Normalne mnożenie, jak pokazałeś:
>>> 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]])
Jeśli dodasz oś, pomnoży się tak, jak chcesz:
>>> m * c[:, np.newaxis] array([[ 0, 0, 0], [ 4, 5, 6], [14, 16, 18]])
Możesz również transponować dwukrotnie:
>>> (m.T * c).T array([[ 0, 0, 0], [ 4, 5, 6], [14, 16, 18]])
źródło
[a,b] op [c,d] -> [[a*c, b*c], [a*d, b*d]]
.Porównałem różne opcje szybkości i stwierdziłem, że - ku mojemu zdziwieniu - wszystkie opcje (z wyjątkiem
diag
) są równie szybkie. Osobiście używamA * b[:, None]
(lub
(A.T * b).T
) ponieważ jest krótki.Kod do odtworzenia fabuły:
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)", )
źródło
Możesz także użyć mnożenia macierzy (inaczej iloczynu skalarnego):
a = [[1,2,3],[4,5,6],[7,8,9]] b = [0,1,2] c = numpy.diag(b) numpy.dot(c,a)
To, co jest bardziej eleganckie, jest prawdopodobnie kwestią gustu.
źródło
dot
to naprawdę przesada. Po prostu robisz niepotrzebne mnożenie przez 0 i dodawanie do 0.diag
matrycy.Kolejna sztuczka (od wersji 1.6)
A=np.arange(1,10).reshape(3,3) b=np.arange(3) np.einsum('ij,i->ij',A,b)
Jestem biegły w numpy broadcasting (
newaxis
), ale wciąż znajduję sposób na obejście tego nowegoeinsum
narzędzia. Więc trochę się pobawiłem, aby znaleźć to rozwiązanie.Czasy (przy użyciu Ipython timeit):
einsum: 4.9 micro transpose: 8.1 micro newaxis: 8.35 micro dot-diag: 10.5 micro
Nawiasem mówiąc, zmiana a
i
naj
,np.einsum('ij,j->ij',A,b)
daje macierz, której Alex nie chce. Inp.einsum('ji,j->ji',A,b)
w efekcie dokonuje podwójnej transpozycji.źródło
einsumm
(25 mikro) jest dwa razy szybsze niż inne (diagnozy kropkowe spowalniają bardziej). To jest np 1.7, świeżo skompilowane z 'libatlas3gf-sse2' i 'libatlas-base-dev' (Ubuntu 10.4, pojedynczy procesor).timeit
daje to, co najlepsze z 10000 pętli.numpy
wersji wstecz.Dla tych zagubionych dusz w google użycie
numpy.expand_dims
thennumpy.repeat
zadziała i będzie działać również w przypadkach o wyższych wymiarach (tj. Mnożenie kształtu (10, 12, 3) przez 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]])
źródło
Dlaczego po prostu tego nie zrobisz
>>> m = np.array([[1,2,3],[4,5,6],[7,8,9]]) >>> c = np.array([0,1,2]) >>> (m.T * c).T
??
źródło