簡體   English   中英

使用兩個numpy向量中的元素對的函數填充矩陣的最快方法?

[英]Fastest way to populate a matrix with a function on pairs of elements in two numpy vectors?

我有兩個1維numpy向量vavb ,它們用於通過將所有對組合傳遞給函數來填充矩陣。

na = len(va)
nb = len(vb)
D = np.zeros((na, nb))
for i in range(na):
    for j in range(nb):
        D[i, j] = foo(va[i], vb[j])

目前,由於va和vb相對較大(4626和737),這段代碼需要很長時間才能運行。 但是我希望這可以改進,因為使用cdist方法執行類似的程序並且具有非常好的性能。

D = cdist(va, vb, metric)

我顯然知道scipy有利於在C中運行這段代碼而不是在python中 - 但是我希望有一些不知道的numpy函數可以快速執行。

cdist很快,因為它是用高度優化的C代碼編寫的(正如您已經指出的那樣), 並且它只支持一組小的預定義metric

既然要申請一般的操作,任何給定的foo功能,你沒有選擇,只能調用該函數na -times- nb倍。 那部分不太可能進一步優化。

剩下要優化的是循環和索引。 嘗試一些建議:

  1. 使用xrange而不是range (如果在python2.x中,在python3中,范圍已經是類似於生成器)
  2. 使用enumerate ,而不是范圍+顯式索引
  3. 使用python速度“魔法”,例如cythonnumba ,來加速循環過程。

如果你可以對foo做進一步的假設,那么就有可能進一步加快它。

就像@ shx2所說,這一切都取決於什么是foo 如果你可以用numpy ufuncs來表達它,那么使用outer方法:

In [11]: N = 400

In [12]: B = np.empty((N, N))

In [13]: x = np.random.random(N)

In [14]: y = np.random.random(N)

In [15]: %%timeit
for i in range(N):
   for j in range(N):
     B[i, j] = x[i] - y[j]
   ....: 
10 loops, best of 3: 87.2 ms per loop

In [16]: %timeit A = np.subtract.outer(x, y)   # <--- np.subtract is a ufunc
1000 loops, best of 3: 294 µs per loop

否則你可以將循環推向cython級別。 上面繼續一個簡單的例子:

In [45]: %%cython
cimport cython
@cython.boundscheck(False)
@cython.wraparound(False)
def foo(double[::1] x, double[::1] y, double[:, ::1] out):
    cdef int i, j
    for i in xrange(x.shape[0]):
        for j in xrange(y.shape[0]):
            out[i, j] = x[i] - y[j]
   ....: 

In [46]: foo(x, y, B)

In [47]: np.allclose(B, np.subtract.outer(x, y))
Out[47]: True

In [48]: %timeit foo(x, y, B)
10000 loops, best of 3: 149 µs per loop

故意將cython示例過於簡單化:實際上,您可能需要添加一些形狀/步幅檢查,在函數內分配內存等。

對於文檔稱為函數式編程例程的最不為人知的numpy函數之一是np.frompyfunc 這會從Python函數創建一個numpy ufunc。 不是一些其他對象可以模擬一個numpy ufunc,而是一個帶有所有鈴聲和口哨的正確ufunc。 雖然行為在很多方面與np.vectorize非常相似,但它有一些明顯的優點,希望以下代碼可以強調:

In [2]: def f(a, b):
   ...:     return a + b
   ...:

In [3]: f_vec = np.vectorize(f)

In [4]: f_ufunc = np.frompyfunc(f, 2, 1)  # 2 inputs, 1 output

In [5]: a = np.random.rand(1000)

In [6]: b = np.random.rand(2000)

In [7]: %timeit np.add.outer(a, b)  # a baseline for comparison
100 loops, best of 3: 9.89 ms per loop

In [8]: %timeit f_vec(a[:, None], b)  # 50x slower than np.add
1 loops, best of 3: 488 ms per loop

In [9]: %timeit f_ufunc(a[:, None], b)  # ~20% faster than np.vectorize...
1 loops, best of 3: 425 ms per loop

In [10]: %timeit f_ufunc.outer(a, b)  # ...and you get to use ufunc methods
1 loops, best of 3: 427 ms per loop

因此,雖然它仍然明顯不如正確的矢量化實現,但它更快一些(循環在C中,但你仍然有Python函數調用開銷)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM