Numpy快速處理資料--ufunc運算(三)
ufunc是universal function的縮寫,它是一種能對陣列中每個元素進行操作的函式。Numpy內建的許多ufunc函式都是C語言實現的,計算速度非常快,簡單看個例子:
上述程式碼中先用linspace( )產生一個從0到2*PI的等差陣列,然後將其傳給np.sin( )計算每個元素的正弦值。函式計算完成後,返回一個計算結果的陣列。如果通過out引數指定計算結果的儲存位置,則計算結果將儲存在out引數中,如:>>> x = np.linspace(0,2*np.pi,10) >>> y=np.sin(x) >>> y array([ 0.00000000e+00, 6.42787610e-01, 9.84807753e-01, 8.66025404e-01, 3.42020143e-01, -3.42020143e-01, -8.66025404e-01, -9.84807753e-01, -6.42787610e-01, -2.44929360e-16]) >>>
對於計算單個元素,則建議採用Math庫裡對應的函式,np.sin( )對應math.sin( ),因為對於np.sin( )為了實現陣列計算,底層做了很多複雜的處理,因此對於單個元素,math庫裡的函式計算速度快得多;而對於陣列元素,則採用numpy庫裡的函式計算。>>> np.sin(x, out =y) array([ 0.00000000e+00, 6.42787610e-01, 9.84807753e-01, 8.66025404e-01, 3.42020143e-01, -3.42020143e-01, -8.66025404e-01, -9.84807753e-01, -6.42787610e-01, -2.44929360e-16]) >>> y array([ 0.00000000e+00, 6.42787610e-01, 9.84807753e-01, 8.66025404e-01, 3.42020143e-01, -3.42020143e-01, -8.66025404e-01, -9.84807753e-01, -6.42787610e-01, -2.44929360e-16]) >>>
在讀取計算結果時,通過下標獲取陣列元素的型別為Numpy中定義的型別,將其轉換為python的標準型別需耗時間。針對此問題,可以採用陣列提供的item( )方法,用來獲取陣列中單個元素,並直接返回標準的python資料型別,如:
一、陣列的四則運算>>> a = np.arange(6.0).reshape(2,3) >>> a.item(1,2) #與a[1,2]類似 5.0 >>> type(a.item(1,2)) <class 'float'> >>> type(a[1,2]) <class 'numpy.float64'> >>>
二、比較和布林運算
使用“==”、“>”等比較運算子比較兩個陣列,將返回一個布林陣列,它的每個元素的值是兩個陣列對應元素比較的結果,如:
>>> np.array([1,2,3])==np.array([3,2,1])
array([False, True, False], dtype=bool)
>>> np.array([1,2,3])<np.array([3,2,1])
array([ True, False, False], dtype=bool)
>>>
由於Python的布林運算使用and,or和Not等關鍵字,無法被過載,因此陣列的布林運算智慧通過ufunc對應的函式來操作,這些函式以"logical_"開頭,如下:
以“bitwise_"開頭的函式是位元運算函式,包括bitwise_and、bitwise_not、bitwise_or和bitwise_xor等,也可以使用&、~、|、^等操作符來計算。對於布林陣列來說,位運算和布林運算的結果相同,但是位運算子的優先順序高於比較運算子。
自定義ufunc函式
通過Numpy提供的標準ufunc函式可以滿足大多要求,但有些特殊情況需要自定義函式來實現。這時,可以採用python來實現,然後使用frompyfunc( )函式將一個計算單個元素的函式轉換為ufunc函式。例如,用一個分段函式來描述三角波,它的樣子如圖所示:
Python函式定義如下:
def triangle_wave(x,c,c0,hc):
x = x - int(x) #週期為1,取小數部分計算
if x>=c:
r = 0.0
elif x<c0:
r = x/c0*hc
else:
r = (c-x)/(c-c0)*hc
return r
1)通過列表推導計算
先使用列表推導計算一個列表,然後用array( )轉換為陣列,這種方法每次都要使用列表推導,而且對於多維陣列,比較複雜,計算如下:
x = np.linspace(0,2,1000)
y1 = np.array([triangle_wave(t,0.6,0.4,1.0) for t in x])
2)fromnpyfunc( )函式計算
通過frompyfunc( )可以將計算單個值的函式轉換為一個能對陣列中每個元素計算的ufunc函式。frompyfunc( )的呼叫格式為:
frompyfunc(func,nin,nout)
其中,ufunc是計算單個元素的函式,nin是輸入引數的個數,nout是func返回值的個數。如:
triangle_wave_ufunc = np.frompyfunc(triangle_wave,4,1)
y2 = triangle_wave_ufunc(x,0.6,0.4,1.0)
程式碼簡潔高效。值得注意:triangle_wave_ufunc( )所返回陣列的元素型別是object,因此還需要呼叫陣列的astype()方法將其轉換為雙精度浮點陣列:
3)vectorize( )函式計算
使用vectorize( )可以實現和frompyfunc( )類似的功能,但他可以通過otypes引數指定返回陣列的元素型別。otypes引數可以是一個表示元素型別的字串,也可以是一個型別列表,使用列表可以描述多個返回陣列的元素型別,如將上面的程式碼改成vectorize( ),則為:
triangle_wave_vec = np.vectorize(triangle_wave, otypes[np.float])
y3 = triangle_wave_vec(x,0.6,0.4,1.0)
到這裡,ufunc函式的強大自不必言,如果將其與影象處理、訊號處理等陣列形式的應用結合起來,可以發揮強大作用。