6.9. 通用函式¶
通用函式(ufunc)是一種數學函式,能在單次呼叫中套用至陣列的每一個元素。前一頁的算術運算子其實就是披著運算子語法的通用函式;本頁則是涵蓋三角函數、指數/對數、四捨五入及其他幾項運算的具名通用函式目錄。
每個 ufunc 都接受純量、Python 可迭代物件或 ndarray,並傳回單一浮點數(當輸入為純量時)或浮點數 ndarray:
from ulab import numpy as np
np.exp(2.0) # 7.389...
np.sin(range(4)) # 1-D float ndarray
np.sqrt([1, 4, 9, 16]) # array([1.0, 2.0, 3.0, 4.0])
a = np.arange(9).reshape((3, 3))
np.exp(a) # 3x3 float ndarray
6.9.1. 目錄¶
numpy 提供了嵌入式應用程式最常使用的數學函式:
三角函數 --
sin()、cos()、tan()、asin()、acos()、atan()、arctan2()、sinh()、cosh()、tanh()、asinh()、acosh()、atanh()、sinc()。
每個函式都在單次函式庫呼叫中處理整個陣列。相較於逐元素呼叫 math.sin() 的 Python 串列生成式,在一般的緩衝區上可獲得 10 到 30 倍的速度提升。
6.9.2. out= 關鍵字¶
每次 ufunc 呼叫通常都會配置一個全新的結果陣列來保存其輸出。在每秒執行多次的迴圈中,這些配置會累積起來並浪費 RAM。傳入 out=——一個已經存在、且形狀與輸入相同的浮點數陣列——就會把結果寫入該陣列,而不會配置新的陣列:
x = np.linspace(0, 2 * np.pi, num=256)
y = np.zeros(256)
while True:
np.sin(x, out=y)
# use y ...
若 out 的 dtype 或形狀不正確,函式會引發例外。本頁中的每個 ufunc 都支援這個關鍵字;它是讓串流訊號處理迴圈維持零配置的最簡潔方式。
6.9.3. 雙引數 ufunc¶
arctan2() 是上述清單中唯一真正的雙引數 ufunc——它會傳回 y / x 的象限感知反正切值,並對兩個運算元進行廣播:
y = np.array([1, 2.2, 33.33, 444.444])
np.arctan2(y, 1.0) # against a scalar
np.arctan2(1.0, y) # the other way
np.arctan2(y, y) # against another array
6.9.4. 組合通用函式¶
通用函式可以像任何其他陣列運算式一樣組合。以下是在相機上常會出現的幾種模式:
Gamma 校正(在浮點空間中):
gamma = 0.5
out = 255.0 * (frame / 255.0) ** gamma
簡單的低通平滑器(alpha 接近 1.0 表示更新較慢):
alpha = 0.95
filtered = alpha * filtered + (1.0 - alpha) * sample
Sigmoid:
sigmoid = 1.0 / (1.0 + np.exp(-x))
以 dB 表示的功率頻譜:
spectrum = 20.0 * np.log10(np.abs(real) + 1e-12)
6.9.5. np.vectorize¶
一般的 Python 函式可以透過 vectorize() 提升為 ufunc 形式的函式。產生的可呼叫物件可接受純量、可迭代物件或 ndarray 值:
def f(x):
return x * x
vf = np.vectorize(f)
vf(44.0) # array([1936.0])
vf(np.array([1, 2, 3, 4])) # array([1.0, 4.0, 9.0, 16.0])
vf([2, 3, 4]) # array([4.0, 9.0, 16.0])
預設情況下結果的 dtype 為 float。otypes= 可覆寫它:
vf_u8 = np.vectorize(f, otypes=np.uint8)
vf_u8([1, 2, 3, 4])
# array([1, 4, 9, 16], dtype=uint8)
該 Python 函式必須接受單一引數並傳回單一數值。
vectorize() 主要是 語法上 的便利——被包裝的 Python 函式仍然必須對每個元素各執行一次,因此真正的 ufunc 所避免的逐元素直譯器開銷大部分又回來了。預期相較於串列生成式只有約 30% 到 50% 的適度速度提升,而非真正通用函式的 30 倍。當同一個函式必須以相同名稱處理純量、串列 和 陣列時,它是合適的工具——而非以追求原始速度為目標時。
如需上述每個通用函式的完整呼叫簽章,請參閱 numpy --- 與 numpy 相容的陣列運算。