2013-03-25 12 views
14

Chcę obliczyć iloczyn rzędowy z dwóch macierzy tego samego wymiaru tak szybko, jak to możliwe. To tak jak ja to robię:Wektoryzowany sposób obliczania iloczynu wierszowego dwie macierze z Scipy

import numpy as np 
a = np.array([[1,2,3], [3,4,5]]) 
b = np.array([[1,2,3], [1,2,3]]) 
result = np.array([]) 
for row1, row2 in a, b: 
    result = np.append(result, np.dot(row1, row2)) 
print result 

i oczywiście wyjście jest:

[ 26. 14.] 
+3

Czy Twoja Kod Pythona, czego naprawdę chcesz? Bierzesz kropkę iloczynu pierwszego i drugiego rzędu "a" i iloczynu punktowego pierwszego i drugiego rzędu "b", a nie iloczynu punktowego każdego i-tego rzędu "a" i "b" '. – jorgeca

+0

jak powiedział Jorgeca, indeksowanie for jest złe: w tym fragmencie kodu robisz: kropka (a [0,:], a [1 ,:]), kropka (b [0 ,:], b [1 ,: ]), zobacz http://stackoverflow.com/questions/1663807/how-can-i-terate-through-two-lists-in-parallel-in-python – lib

+0

Dzięki za wyjaśnienie, ale nie, naprawdę szukałem tego, co ja napisał, tj. dwa rzędy mnożenia z tym samym indeksem. – Cupitor

Odpowiedz

18

Wyjazd numpy.einsum innej metody:

In [52]: a 
Out[52]: 
array([[1, 2, 3], 
     [3, 4, 5]]) 

In [53]: b 
Out[53]: 
array([[1, 2, 3], 
     [1, 2, 3]]) 

In [54]: einsum('ij,ij->i', a, b) 
Out[54]: array([14, 26]) 

Wygląda einsum jest nieco szybciej niż inner1d:

In [94]: %timeit inner1d(a,b) 
1000000 loops, best of 3: 1.8 us per loop 

In [95]: %timeit einsum('ij,ij->i', a, b) 
1000000 loops, best of 3: 1.6 us per loop 

In [96]: a = random.randn(10, 100) 

In [97]: b = random.randn(10, 100) 

In [98]: %timeit inner1d(a,b) 
100000 loops, best of 3: 2.89 us per loop 

In [99]: %timeit einsum('ij,ij->i', a, b) 
100000 loops, best of 3: 2.03 us per loop 
+1

Bardzo lubię einsum i to prawda, że ​​można z nim ominąć pętle. Jednakże, jeśli najważniejszą sprawą jest wydajność, a nie styl kodowy, możesz nadal być lepiej z kropką i pętlą (w zależności od twoich danych i środowiska systemowego). W przeciwieństwie do einsum, kropka może wykorzystywać BLAS i często będzie automatycznie wielowątkowa. http://mail.scipy.org/pipermail/numpy-discussion/2012-October/064277.html – PiQuer

4

Zrobisz lepiej unikając append, ale nie mogę myśleć o sposób aby uniknąć pętli Pythona. Może niestandardowy Ufunc? Nie sądzę, że numpy.vectorize pomoże ci tutaj.

import numpy as np 
a=np.array([[1,2,3],[3,4,5]]) 
b=np.array([[1,2,3],[1,2,3]]) 
result=np.empty((2,)) 
for i in range(2): 
    result[i] = np.dot(a[i],b[i])) 
print result 

EDIT

podstawie this answer, wygląda inner1d może działać, jeśli wektory w swoim problemem w świecie rzeczywistym są 1D.

from numpy.core.umath_tests import inner1d 
inner1d(a,b) # array([14, 26]) 
12

prosty sposób na to jest:

import numpy as np 
a=np.array([[1,2,3],[3,4,5]]) 
b=np.array([[1,2,3],[1,2,3]]) 
np.sum(a*b, axis=1) 

który unika pętlę Python i jest szybsza w przypadkach takich jak:

def npsumdot(x, y): 
    return np.sum(x*y, axis=1) 

def loopdot(x, y): 
    result = np.empty((x.shape[0])) 
    for i in range(x.shape[0]): 
     result[i] = np.dot(x[i], y[i]) 
    return result 

timeit npsumdot(np.random.rand(500000,50),np.random.rand(500000,50)) 
# 1 loops, best of 3: 861 ms per loop 
timeit loopdot(np.random.rand(500000,50),np.random.rand(500000,50)) 
# 1 loops, best of 3: 1.58 s per loop 
8

bawił się z tym i okazało inner1d najszybciej:

enter image description here

Działka powstała z perfplot (mały projekt kopalni)

import numpy 
from numpy.core.umath_tests import inner1d 
import perfplot 

perfplot.show(
     setup=lambda n: (numpy.random.rand(n, 3), numpy.random.rand(n, 3)), 
     n_range=[2**k for k in range(1, 17)], 
     kernels=[ 
      lambda data: numpy.sum(data[0] * data[1], axis=1), 
      lambda data: numpy.einsum('ij, ij->i', data[0], data[1]), 
      lambda data: inner1d(data[0], data[1]) 
      ], 
     labels=['np.sum(a*b, axis=1)', 'einsum', 'inner1d'], 
     logx=True, 
     logy=True, 
     xlabel='len(a), len(b)' 
     ) 
Powiązane problemy