時系列・スペクトルデータをre-samplingして次元削減【Python: SciPy】
PythonでDataFrame中の時系列・スペクトルデータを間引きたい時の方法です。
「pandas.DataFrame.resample」だと引数でD(日次)、W(週次)などの時間設定をしないといけません。日時の列がない時系列データやスペクトルデータの間引きたい時は以下の方法が使えます。
時系列・スペクトルデータとは
時系列・スペクトルデータとは、ある軸に沿って一定間隔ごとに観察されるデータ系列があてはまる。例として、株価の推移や吸光スペクトルなど。
時系列データの例 | スペクトルデータの例 |
・気温や降水状況の遷移 ・交通状態の変化 ・日々の売り上げ ・株価の推移 ・ビットコイン価格の推移 |
・音声データ ・化合物の吸収スペクトル(IR、UV) ・天体からのスペクトル |
それぞれ分類は異なるものの、データとして似たような以下の特徴を持つ。
- ある測定点と隣り合っている点は、近い値をとる
- 長期(広範囲)でみると、ノイズが含まれる
参照: https://kotobank.jp/word/%E6%99%82%E7%B3%BB%E5%88%97%E3%83%87%E3%83%BC%E3%82%BF-1329677 https://datachemeng.com/preprocessspectratimeseriesdata/
時系列・スペクトルデータのre-sampling
時系列・スペクトルデータを元に予測モデルを作成する際、すべての測定点におけるデータを用いると、膨大な数の特徴量になってしまう。これは過学習につながるので、汎化性能の向上にはダウンサンプリングによる次元削減が有効。
使うもの:SciPy(scipy.signal)
数学・科学・工学用のオープンソースソフトウェアのエコシステム(高度な科学計算ライブラリの詰め合わせ的なもの)。
NumPy より高度な数値演算処理ができ、物理定数,疎行列,確率分布から数値積分,信号処理、最適化、統計などが簡単に実行できるようになる。
scipy.signalは、scipyのうちの波形処理に関するモジュール。
-
scipy.signal.decimate
import numpy as np from scipy import signal # 基になる40点の波形データを作成 x = np.linspace(0, 10, 40, endpoint=False) y = np.cos(-x**2/6) # データをもとに20点にダウンサンプリング x_down = np.linspace(0, 10, 20, endpoint=False) y_down = signal.decimate(y, 2) # 2分の1にダウンサンプリング # 結果をグラフにプロット %matplotlib inline plt.plot(x, y, '.-', label='data') plt.plot(x_down, y_down, 'rs-', label='down-sampled', alpha=0.5) plt.legend() plt.show()
scipy.signal.decimateは、アンチエイリアス (anti-aliasing) 処理を行ってダウンサンプリングする(アンチエイリアス処理とは、連続データから一定間隔にサンプリングするときに生じると歪みをなくすための処理をいう)。そのままデータポイントを減らしたものに近い形でリサンプリングされる。
参照:https://docs.scipy.org/doc/scipy-1.2.1/reference/generated/scipy.signal.decimate.html
-
scipy.signal.resample
# 基になる40点の波形データを作成 x = np.linspace(0, 10, 40, endpoint=False) y = np.cos(-x**2/6) # データをもとに2.5倍の100点にアップサンプリング x_up = np.linspace(0, 10, 100, endpoint=False) y_up = signal.resample(y, 100) # データをもとに半分の20点にダウンサンプリング x_down = np.linspace(0, 10, 20, endpoint=False) y_down = signal.resample(y, 20) # 結果をグラフにプロット import matplotlib.pyplot as plt %matplotlib inline plt.plot(x, y, '.-', label='data') plt.plot(x_up, y_up, 'go-', label='up-sampled', alpha=0.3) plt.legend() plt.show() plt.plot(x, y, '.-', label='data') plt.plot(x_down, y_down, 'rs-', label='down-sampled', alpha=0.5) plt.legend() plt.show()
scipy.signal.resampleはフーリエ変換を用いたリサンプリングのため、信号は周期的であるとの仮定に基づいている。上のデータでいう末端部分など周期性を満たしていない場合は大きく値が外れてしまう。
ちなみにscipy.signal.resampleはup-samplingも可能。
青線が元のデータポイント、緑がアップサンプリング後、赤がダウンサンプリング後のプロット。
kaggleの音声解析コンペにも使用例があるので参考になります。
参照:https://docs.scipy.org/doc/scipy-0.18.1/reference/generated/scipy.signal.resample.html
DataFrameへの適用
行が各測定点、列が各サンプルのデータフレームで実施。
import pandas as pd # データフレームに格納するデータを作成 y1 = np.cos(-x**2/6)*1/2 y2 = np.cos(x)*1/3 df1 = pd.DataFrame({'y':y, 'y1':y1, 'y2':y2}) # ダウンサンプリング df1_down = signal.decimate(df1, 2, axis=0) df1_down = pd.DataFrame(df1_down, columns=['y_down','y1_down','y2_down'], index=np.linspace(0, 10, 20, endpoint=False)) # ダウンサンプリングしたデータを表示&プロット print(df1.head(10)) print(df1_down.head(10)) df1.plot(kind='line', marker='.') df1_down.plot(kind='line', marker='.')
もともとの40点から1/2の20点にダウンサンプリングしても、波形を精度良く維持しており、特徴量とするにも情報のロスは少なそうです。
なお、上のような行が各サンプル、列が各測定点のデータフレーム(いわゆる整然データ)の場合は、引数axis=1
とすればOK。
ディスカッション
コメント一覧
まだ、コメントがありません