6.16. 曲線與積分¶
數學各頁涵蓋了輸入一個陣列、輸出一個陣列(或純量)的運算——算術、歸約、廣播。本頁則涵蓋另一類運算:把陣列視為一個 取樣後的函數,並針對該函數本身提出問題。在樣本之間進行內插、對其擬合一條曲線、計算其下方的積分,以及將其與另一個緩衝區進行卷積。
這些運算全都接受 ndarray 輸入,並傳回浮點數純量或浮點數 ndarray。
6.16.1. 內插¶
interp() 執行一維線性內插。xp 是一個單調遞增的一維自變數值陣列;fp 是對應的應變數值;x 則是內插函數應被求值的位置:
xp = np.array([0.0, 1.0, 2.0, 3.0])
fp = np.array([10.0, 20.0, 25.0, 30.0])
x = np.array([0.5, 1.5, 2.5])
np.interp(x, xp, fp)
# array([15.0, 22.5, 27.5])
在 [xp[0], xp[-1]] 範圍之外,結果會分別被裁剪為 fp[0] 和 fp[-1];left= 和 right= 關鍵字可覆寫這些端點值。
在相機上用於將校正表重新對應至任意樣本位置——例如熱敏電阻的溫度對電壓表、非線性的像素響應曲線,或逐通道的 gamma 查找表。單次函式庫呼叫、對輸入的單趟遍歷,無需 Python 迴圈。
6.16.2. 多項式擬合與求值¶
polyfit() 會以最小平方法將次數為 deg 的多項式擬合至資料點 (x, y),並傳回係數(最高次項在前):
x = np.array([0.0, 1.0, 2.0, 3.0, 4.0])
y = np.array([0.0, 1.1, 3.9, 9.1, 15.8])
coeffs = np.polyfit(x, y, 2)
# array([1.0, 0.0, 0.0]) (approximately)
當省略 x 時,會使用 range(len(y))——這對於為沒有對應 x 軸的規律取樣緩衝區快速擬合多項式很有用:
np.polyfit(y, 2)
polyval() 會在 x 處對係數為 p 的多項式求值。輸入 x 可以是純量(傳回浮點數)或 ndarray(傳回 ndarray):
p = np.polyfit(x, y, 2)
fitted = np.polyval(p, x)
自然的搭配方式是:在校正時呼叫一次 polyfit,儲存其係數,然後每一影格呼叫 polyval 來對所得的曲線求值。多項式求值這一步驟每個樣本只需少數幾次浮點運算,即使在最小的相機上也很便宜。
6.16.3. 卷積¶
convolve() 會傳回兩個一維陣列的完整長度離散線性卷積。僅實作了 full 模式;輸出長度為 len(a) + len(v) - 1。對結果進行切片即可達到桌面版 numpy 所提供的 same 與 valid 模式的相同效果:
a = np.array([1.0, 2.0, 3.0])
v = np.array([0.5, 0.5])
np.convolve(a, v)
# array([0.5, 1.5, 2.5, 1.5])
對於短的 FIR 濾波器與平滑核(方形、三角形、高斯)很有用,這些情況下建立 SOS 串接會顯得多餘。其執行時間與兩個陣列長度的乘積成正比——對於短核沒有問題,但對於長核很快就會比 FFT 卷積更昂貴。
6.16.4. 梯形積分¶
trapz() 會以複合梯形法則對取樣後的函數進行積分:
x = np.linspace(0, np.pi, num=128)
y = np.sin(x)
np.trapz(y, x) # ~ 2.0
當樣本間距均勻且只有步長有意義時,請傳入 dx=;當樣本不是等間距時,請傳入 x=。這是用於對已擷取的感測器資料進行積分的合適呼叫,因為這種情況下並沒有可用的解析形式。
對於已經過 帶限 的樣本資料(例如音訊緩衝區經過抗鋸齒濾波之後),梯形法則的收斂速度與樣本數的平方成正比,這表示緩衝區長度加倍可將誤差降為原本的四分之一。
當被積函數不是一個樣本緩衝區,而是應用程式可以在任意點上求值的 Python 函數時,另有一系列不同的求解器才是合適的選擇。