2014-01-30 164 views
2

有沒有辦法在運行時確定函數是否需要numpy.vectorize()才能像預期的那樣工作?如何確定是否需要numpy.vectorize()?

對於背景,我問這是因爲我在程序中使用Numpy來根據文獻中提供的熱力學函數計算相圖(基於CALPHAD)。對於給定的溫度,人們評估自由能函數並確定接觸凹面(二階導數> 0)的公共切線曲線以定義相位共存的組成範圍。爲此,直接定義二階導函數是很好的。在我試圖用一個簡單的拋物線自由enrgy進行測試之前,所有的實驗自由能函數都很好(不難得到它的衍生物),它有一個常數二階導數。這使我的算法崩潰了,因爲我沒有預料到numpy廣播會在函數內部尋找並決定它不需要廣播。

困難歸結爲這種行爲:

import numpy as np 
def f(x): 
    return(x*x) 
def g(x): 
    return(3.0) 
def h(x): 
    return(0*x+3.0) 
def i(x): 
    return(x-x+3.0) 

x = np.linspace(1.0, 5.0, 5) 

在這些輸出中IPython的運行3.3.2結果:

F(X) - > 陣列([ (x) - > 3.0(注意只有1個元素) - 我期望的

g(x) - > 3.0 (不是天真的) - 不是天真的預期

h(x) - > array([3.,3.,3.,3.,3.]) - OK,愚弄了(x)→ array([3.,3.,3.,3.,3.]) - 與h(x)相同,但避免乘舍入問題

現在我可以用

gv = np.vectorize(g) 

並獲得

GV(x)的 - >陣列([3,3,3,3,3]) - 預期行爲

如果我的程序是(最終)接受任意用戶輸入的自由能函數,這將導致問題,除非所有用戶都理解numpy內部廣播魔術。或者,我可以自反地np.vectorize一切,以防止這種情況。如果函數在numpy中「正常工作」,問題是成本。

也就是說,使用IPython的timeit%,

h(x) -> 100000 loops, best of 3: 3.45 µs per loop 

如果我向量化H(X)不必要的(即HV = np.vectorize(H)),我得到

hv(x) -> 10000 loops, best of 3: 43.2 µs per loop 

所以,不必要的矢量化是一個巨大的懲罰(對於5個函數實例,40微秒)。

我想我可以對評估在小ndarray以查看是否返回類型是陣列或浮子上的函數的返回初始測試,然後定義一個新的功能,如果它是浮動,如:

def gv(x): 
    return(g(x)+0.0*x) 

這看起來像一個可怕的kludge。

所以 - 在這種情況下是否有更好的方法來'欺騙'numpy成有效的廣播?

+0

您可以檢查'g(x)'返回的值的形狀,並根據需要進行廣播。 'numpy'(和MATLAB)函數按摩輸入以將它們放入最佳形狀進行計算並不常見,然後進一步重塑輸出以匹配輸入形狀。例如看看'np.vectorize'的內部。 – hpaulj

+0

你爲什麼要使用numpy.vectorize?我假設你想做f(x)+ g(x)左右?但是當f(x)返回一個numpy.ndarray並且g(x)返回一個浮點數時,這也會起作用。 – usethedeathstar

+0

一般來說,我不想使用vectorize()。問題出現是因爲如果x是一個ndarray,y = f(x)返回一個ndarray,但y = g(x)返回一個單獨的float值,而不是len(x)的數組。因此,如果試圖編寫一個庫來處理函數,那麼當你沒有得到期望的數組時,最終可能會得到不尋常的結果。 numpy.vectorize()只是讓它起作用的雜湊。 –

回答

2

解決顯示的問題。如果你想要一個新的數組:

def g(x): 
    return np.ones_like(x)*3 

,或者如果你想在陣列中的所有元素設置爲3位:

def g(x): 
    x[:] = 3 

注意這裏沒有return語句,你僅僅升級陣列x以便所有元素都是3.

如圖所示,def g(x): return(3)的問題是函數內部沒有對numpy的引用。你聲明任何給定輸入返回3.說明x=3將遇到類似的問題,因爲您正在更新指針x指向3而不是numpy數組。儘管語句x[:]=3可以訪問類numpy.ndarray中稱爲視圖的內部函數,而不是通常使用僅更新指針的=語句。

+0

我喜歡這個,因爲它避免了我的一些其他kludge選項。如果需要,我會深入研究numpy代碼,但我仍然不清楚numpy爲什麼不將g()的返回值廣播到輸入的ndarray。謝謝。 –

+0

@JonCuster我已經更新了一些答案。我認爲你的問題來自於對python如何工作而不是numpy方面的誤解。 – Daniel

+0

我欣賞你的溫柔評論!我已經建立了一個基於f(x)的心理模型。但是,是的,通過一些模型的調整,我可以看到,如果函數中沒有任何內容表示numpy參與類型確定(並因此根據類型/大小問題進行廣播),那麼python本身會高興地返回似乎被要求。 –

0

正如其他人所建議的那樣,您可以包裝用戶提供的功能以確保輸出形狀正確。例如:

def wrap_user_function(func, x): 
    out = func(x) 
    if np.isscalar(out): 
    return np.zeros_like(x) + out 
    return out 

這僅處理標量輸出的情況下特別,但它至少應該照顧你g(x)問題,而不強加太大的性能損失。

相關問題