Python で極値の検出
概要
信号の極値を検出する。
信号の生成
例として疑似的な信号を生成する。
- 3 [Hz] の信号 + 0.01 [Hz] の信号 + ノイズ
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from scipy import signal
t = np.arange(30 * 100) / 100
x = np.sin(2 * np.pi * t / 3) + np.random.randn(len(t)) * 0.2 + np.sin(2 * np.pi * t / 100)
df = pd.DataFrame({'Data': x}, index=pd.to_datetime(t, unit='s').time)
fig, ax = plt.subplots()
df.plot(ax=ax, xlim=['00:00:00', '00:00:10'])
plt.show()
信号の性質を調べる
def acorr(df: pd.DataFrame, ra: int = 3, fs=100):
x = np.correlate(df.Data.values, df.Data.values, mode='full')
t = (np.arange(len(x)) - len(x) / 2 + 0.5) / fs
x /= x.max()
fig, ax = plt.subplots()
ax.set_xlim(-ra, ra)
ax.set_ylim(0, 1)
ax.plot(t, x)
ax.set_title('Autocorrelation')
ax.grid(axis='x')
ax.set_xticks(np.linspace(-ra, ra, 2 * ra + 1))
plt.show()
acorr(df)
主な信号は 1/3 [Hz] であることがわかる。そのため、1/3 [Hz] でローパスフィルターをかける。
ローパスフィルターをかけ、極値の検出
ローパスフィルターをかけ、極値を検出できるようにする。
1/3 [Hz] でローパスフィルターをかけても正しく検出されない場合は、 周波数を下げていく。
fs = 10
lpf = 0.1
b, a = signal.butter(5, lpf / fs * 2, 'low')
df['Filter'] = signal.filtfilt(b, a, df.Data.values)
max_idx = signal.argrelmax(df['Filter'].values)
min_idx = signal.argrelmin(df['Filter'].values)
df['max_index'] = False
df.iloc[max_idx[0], 2] = True
df['min_index'] = False
df.iloc[min_idx[0], 3] = True
fig, ax = plt.subplots()
df.plot(ax=ax)
ax.set_xlim(['00:00:00', '00:00:30'])
ax.scatter(
df.loc[df['max_index'], ['Filter']].index,
df.loc[df['max_index'], ['Filter']],
color='tab:orange',
zorder=3)
ax.scatter(
df.loc[df['min_index'], ['Filter']].index,
df.loc[df['min_index'], ['Filter']],
color='tab:green',
zorder=3)
plt.show()