HOWTO · Python

Python에서 저역 통과 필터 구현

SciPy 버터워스 필터, 이동 평균, 가우시안 평활화로 Python에서 샘플링된 데이터를 필터링하는 방법과 차단 주파수 및 샘플링 한계를 이해하세요.

이 페이지의 내용

저역 통과 필터는 낮은 주파수 성분은 통과시키고 높은 주파수 성분은 감쇠합니다. 차단 주파수보다 높은 모든 성분을 반드시 제거하는 것은 아닙니다. 샘플링된 데이터에서는 샘플링 주파수와 같은 단위로 차단 주파수를 설정하세요. 이 가이드에서는 NumPy와 SciPy를 사용해 1차원 신호를 필터링하거나 평활화하는 세 가지 방법을 살펴봅니다.

아래 예제는 각각 독립적으로 실행할 수 있지만, 여기에서는 실행하지 않으며 결과도 표시하지 않습니다. 실행하기 전에 Python 환경에 NumPy, SciPy, Matplotlib을 설치하세요.

SciPy로 지정된 차단 주파수의 버터워스 필터 적용하기

버터워스 필터는 통과 대역의 응답이 최대한 평탄하도록 설계됩니다. 샘플링 주파수의 절반인 나이퀴스트 주파수보다 낮게 차단 주파수를 설정하세요. 예제에서는 fs가 초당 500개 샘플이고 차단 주파수는 100 Hz입니다. scipy.signal.butter에 fs를 전달하면 차단 주파수를 같은 단위로 지정할 수 있습니다. 차수는 전이 구간의 기울기를 조절합니다. 차수가 높을수록 감쇠가 더 가파르지만, 과도 현상과 수치적 동작에 더 민감해집니다.

이 예제는 2차 섹션과 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을 사용하면 입력 신호와 필터링한 신호를 그래프로 그려 확인할 수 있습니다.