聊聊 signal 模組


對於信號處理,Scipy 的 scipy.signal 提供了許多函式實作,信號處理是另一門專業,不在我涉獵範圍,不過倒是一些基本的函式可以聊一下。

例如,若原本有個已取樣的訊號,想要基於它重新取樣,可以使用 scipy.signalresample,例如,將已經取樣的原訊號,以 0.1 的比例重新取樣:

import numpy as np
from scipy import signal
import matplotlib.pyplot as plt

freq = 10           # 頻率
stop = .25          # 取樣範圍為 0 ~ stop
sample_rate = 800   # 取樣率,單位 x 取樣幾次
resample_rate = 0.1 # 重新取樣比例

x = np.linspace(0, stop, int(stop * sample_rate), endpoint = False)
y = np.sin(freq * 2 * np.pi * x) 

# 重新取樣
x1 = np.linspace(0, stop, int(stop * sample_rate * resample_rate), endpoint = False)
y1 = signal.resample(y, int(x.size * resample_rate))

plt.plot(x, y)
plt.plot(x1, y1)
plt.show()

畫出來的結果會如下,藍色是原訊號,橘色是重新取樣後的結果:

聊聊 signal 模組

相對地,如果你有個訊號,取樣率比較低,想要模擬為更連續的曲線,可以透過 scipy.signalqspline1dqspline1d_eval,或者是 cspline1dcspline1d_eval 來達到目的,它們實現了 B-spline 的二次(quadratic)、三次曲線(cubic)功能,簡單來說,給定的點會作為控制點,作為內插計算的依據。

對於曲線的模擬,透過二次的 qspline1dqspline1d_eval,或者是三次的 cspline1dcspline1d_eval,通常就足夠了,如果需要自行控制 B-spline 的更多細節,可以透過 bspline 函式,當然,你就必須對 B-spline 有更多認識,想入門這部份的話,可先從〈畫說曲線〉開始。

底下示範了二次、三次的曲線模擬:

import numpy as np
from scipy import signal
import matplotlib.pyplot as plt

freq = 10           # 頻率
stop = .25          # 取樣範圍為 0 ~ stop
sample_rate = 25    # 取樣率,單位 x 取樣幾次

x = np.linspace(0, stop, int(stop * sample_rate), endpoint = False)
y = np.sin(freq * 2 * np.pi * x) 

# 新模擬點的 x 座標
newx = np.linspace(0, stop, int(stop * sample_rate * 5), endpoint = False)

# 舊的資料間距
dx = 1 / sample_rate
newy1 = signal.qspline1d_eval(                  # 計算 y 值
            signal.qspline1d(y), newx, dx = dx) # 計算係數
newy2 = signal.cspline1d_eval(
            signal.cspline1d(y), newx, dx = dx)

plt.plot(x, y)
plt.plot(newx, newy1)
plt.plot(newx, newy2)

plt.show()

執行結果如下,藍色是原訊號,橘色是二次,綠色是三次:

聊聊 signal 模組

其實,對於資料的內插,scipy.interpolate 提供了更方便的相關函式,相對來說,使用上簡單許多,這樣就不另做說明了,需要範例的話,可參考官方文件的〈Interpolation (scipy.interpolate)〉。

在濾波的部份,scipy.signal 也提供了中值濾波等實作,來看看一個常見的簡單函式 detrend

import numpy as np
from scipy import signal
import matplotlib.pyplot as plt

t = np.linspace(0, 5, 100)
x = t + np.random.normal(size=100)
x_detrended = signal.detrend(x)

plt.plot(t, x)
plt.plot(t, x_detrended)

plt.show()

簡單來說,去除訊號中具有線性變化趨勢的部份,執行後會有如下,藍色是原訊號,呈現逐漸上升的結果,橘色是去除上升趨勢後的結果:

聊聊 signal 模組