HOWTO · Python
Python でローパスフィルターを実装する
SciPy のバターワースフィルター、移動平均、ガウシアン平滑化を使って Python でサンプリングデータをフィルタリングする方法と、カットオフ周波数やサンプリングの限界について学びます。
このページの内容
ローパスフィルターは低周波成分を通過させ、高周波成分を減衰させます。カットオフ周波数より上の成分がすべて取り除かれるとは限りません。サンプリングデータでは、サンプリング周波数と同じ単位でカットオフ周波数を指定します。このガイドでは、NumPy と SciPy を使って1次元信号をフィルタリングまたは平滑化する3つの方法を紹介します。
以下の例はそれぞれ単独で実行できる形ですが、ここでは実行しておらず、結果も示していません。実行する前に、Python 環境へ NumPy、SciPy、Matplotlib をインストールしてください。
定義したカットオフ周波数で SciPy のバターワースフィルターを使う
バターワースフィルターは、通過帯域が最大限平坦になるよう設計されています。カットオフ周波数は、サンプリング周波数の半分にあたるナイキスト周波数より低く設定します。この例では、fs は毎秒500サンプルで、カットオフ周波数は100 Hzです。scipy.signal.butter に fs を渡すと、カットオフ周波数を同じ単位で指定できます。次数は遷移の急峻さを決めます。次数を上げるとロールオフは急になりますが、過渡応答や数値的な挙動の影響を受けやすくなります。
この例では二次セクションを使い、sosfiltfilt で順方向と逆方向にフィルタリングします。これによりオフライン解析で位相遅延を避けられますが、信号の前後両方のデータが必要で、有限長の信号の端点に影響することがあります。ストリーミングやリアルタイム処理では、sosfilt のような因果フィルターを使い、位相遅延と開始時の過渡応答を考慮してください。
import matplotlib.pyplot as plt
import numpy as np
from scipy.signal import butter, sosfiltfilt
fs = 500.0
cutoff = 100.0
order = 4
time = np.arange(500) / fs
signal = np.sin(2 * np.pi * 10 * time) + 0.5 * np.sin(2 * np.pi * 180 * time)
sections = butter(order, cutoff, btype="lowpass", fs=fs, output="sos")
filtered = sosfiltfilt(sections, signal)
plt.plot(time, signal, label="input")
plt.plot(time, filtered, label="filtered")
plt.xlabel("Time (seconds)")
plt.ylabel("Amplitude")
plt.legend()
plt.tight_layout()
plt.show()
信号の特性や残したい周波数に合わせて、cutoff と order を調整してください。sosfiltfilt はオフラインデータ向けです。その結果は、リアルタイム処理に使う因果フィルターの結果と同じものではありません。
移動平均ローパスフィルターを適用する
移動平均では、各点を固定サイズの窓に含まれる値の平均で置き換えます。これは単純な平滑化方法であり、カットオフ応答を指定するバターワースフィルターとは異なります。NumPy の valid 畳み込みモードでは、完全な窓に対する結果だけが返されるため、出力は入力より window_size - 1 サンプル短くなります。プロット時には、それに合わせて時刻を対応させてください。
import matplotlib.pyplot as plt
import numpy as np
fs = 100.0
window_size = 5
time = np.arange(100) / fs
signal = np.sin(2 * np.pi * 3 * time) + 0.2 * np.sin(2 * np.pi * 25 * time)
weights = np.ones(window_size) / window_size
smoothed = np.convolve(signal, weights, mode="valid")
smoothed_time = time[window_size - 1:]
plt.plot(time, signal, label="input")
plt.plot(smoothed_time, smoothed, label="moving average")
plt.xlabel("Time (seconds)")
plt.ylabel("Amplitude")
plt.legend()
plt.tight_layout()
plt.show()
窓サイズは、信号の長さ以下の正の値にしてください。窓を大きくすると、より多くのサンプルが平均され、平滑化が強まりますが、変化がよりなだらかになり、窓処理後の結果の位置もずれます。中央に位置合わせする場合は、時刻を解釈する際に窓の遅延を考慮してください。
ガウシアンフィルターでサンプリングデータを平滑化する
1次元ガウシアンフィルターは、ガウス形状のカーネルで近傍のサンプルに重みを付けます。scipy.ndimage.gaussian_filter1d の sigma は秒やヘルツではなく、サンプル数で指定します。必要に応じて、サンプリング周波数を使って目的の時間スケールをサンプル数に換算してください。ガウシアン平滑化は全般的な滑らかさを得るのに適していますが、明確で急峻なカットオフ周波数を作るものではありません。
import matplotlib.pyplot as plt
import numpy as np
from scipy.ndimage import gaussian_filter1d
fs = 100.0
sigma_seconds = 0.04
time = np.arange(100) / fs
signal = np.sin(2 * np.pi * 3 * time) + 0.2 * np.sin(2 * np.pi * 25 * time)
smoothed = gaussian_filter1d(signal, sigma=sigma_seconds * fs)
plt.plot(time, signal, label="input")
plt.plot(time, smoothed, label="Gaussian smoothed")
plt.xlabel("Time (seconds)")
plt.ylabel("Amplitude")
plt.legend()
plt.tight_layout()
plt.show()
この例では、0.04秒の平滑化スケールを4サンプルに換算しています。既定の動作では信号を反射させて端点を処理します。データに別の端点モデルが適している場合は、異なる mode を選び、端点付近の結果は慎重に解釈してください。
フィルターを選び、限界を理解する
明確な周波数応答が必要で、サンプリング周波数が分かっている場合は、バターワースフィルターを使います。単純な窓ベースの平滑化には移動平均を、局所的な重み付けで滑らかにしたい場合はガウシアン平滑化を使います。これらの方法では応答、遅延、端点の挙動が異なります。結果を元のデータと比較し、タスクに重要な特徴が残っていることを確認してください。
サンプリングデータのナイキスト周波数は、サンプリング周波数の半分です。ローパスフィルターのカットオフ周波数はナイキスト周波数より低くする必要があり、サンプリング周波数も対象の周波数を表現できるだけ十分に高くなければなりません。サンプリング時に記録されなかった情報をフィルターで復元することはできません。オフラインの順方向・逆方向フィルタリングでは位相シフトを抑えられます。一方、因果フィルタリングはストリーミングに適していますが、遅延や開始時の挙動が生じます。また、有限長の記録では、端点付近の結果がパディングや初期条件の影響を受けやすくなります。
よくある質問
ローパスフィルターにはどのような働きがありますか?
フィルターの応答に応じて低周波成分を通過させ、高周波成分を減衰させます。カットオフ周波数は応答が遷移する位置を示すもので、それより高い周波数がすべて完全に消えることを意味しません。
どのような場合にローパスフィルターを使いますか?
タスクにとって高周波の変動がノイズであり、サンプリング周波数とカットオフ周波数が分かっている場合に使います。平滑化によって信号の意味のある急変まで失われないか確認してください。
バターワースフィルターと移動平均はどう違いますか?
バターワースフィルターは、カットオフ周波数、サンプリング周波数、次数を基に設計します。移動平均は窓ごとの固定サンプル数を使い、周波数応答も異なります。窓の長さをバターワースフィルターのカットオフ周波数の代わりとして直接使うことはできません。
これらのフィルターはリアルタイムデータに使えますか?
因果フィルターは到着したサンプルを順次処理できますが、位相遅延と開始時の過渡応答を考慮する必要があります。バターワースの例にある順方向・逆方向の方法は記録全体を必要とするため、オフライン処理向けです。移動平均は、窓と位置合わせを適切に管理すれば、サンプルの到着に合わせて更新できます。
Python ではどのライブラリを使えますか?
NumPy は配列と畳み込みを提供し、SciPy はバターワースフィルターの設計やガウシアン平滑化の関数を提供します。Matplotlib を使うと、入力信号とフィルター後の信号をプロットして確認できます。