2013-03-25 34 views
14

Tôi muốn tính toán sản phẩm chấm theo hàng của hai ma trận có cùng kích thước nhanh nhất có thể. Đây là cách tôi đang làm nó:Cách tính véc-tơ hóa của hai hàng ma trận với 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 

và tất nhiên kết quả là:

[ 26. 14.] 
+3

là của bạn Python mã những gì bạn thực sự muốn? Bạn đang lấy sản phẩm dấu chấm của hàng đầu tiên và thứ hai của 'a' và sản phẩm dấu chấm của hàng đầu tiên và hàng thứ hai của' b', không phải là sản phẩm chấm của mọi hàng thứ i của 'a' và' b '. – jorgeca

+0

như jorgeca đã nói, việc lập chỉ mục sai: trong đoạn mã bạn đang thực hiện: dấu chấm (a [0,:], a [1 ,:]), dấu chấm (b [0 ,:], b [1 ,: ]), xem http://stackoverflow.com/questions/1663807/how-can-i-iterate-through-two-lists-in-parallel-in-python – lib

+0

Cảm ơn lời giải thích nhưng không có tôi thực sự đang tìm kiếm những gì tôi đã viết, tức là hai hàng nhân với cùng một chỉ mục. – Cupitor

Trả lời

18

Check-out numpy.einsum cho phương pháp khác:

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

Hình như einsum nhanh hơn inner1d một chút:

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

Tôi thích einsum rất nhiều và nó là sự thật rằng bạn có thể tránh các vòng với nó. Tuy nhiên, nếu hiệu suất và không phải là kiểu mã là mối quan tâm chính của bạn, bạn có thể vẫn tốt hơn với dấu chấm và vòng lặp (tùy thuộc vào dữ liệu và môi trường hệ thống cụ thể của bạn). Ngược lại với einsum, dấu chấm có thể tận dụng BLAS và thường sẽ tự động đa luồng. http://mail.scipy.org/pipermail/numpy-discussion/2012-October/064277.html – PiQuer

4

Bạn sẽ làm tốt hơn tránh append, nhưng tôi không thể nghĩ ra một cách để tránh vòng python. Một Ufunc tùy chỉnh có lẽ? Tôi không nghĩ rằng numpy.vectorize sẽ giúp bạn ở đây.

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

Dựa trên this answer, nó trông giống như inner1d có thể làm việc nếu các vectơ trong vấn đề thực tế của bạn là 1D.

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

cách đơn giản để làm điều đó là:

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) 

mà tránh vòng lặp python và nhanh hơn trong những trường hợp như:

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

Chơi xung quanh với điều này và thấy inner1d nhanh nhất:

enter image description here

Cốt truyện đã được tạo ra với perfplot (một dự án nhỏ của tôi)

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