はじめに
動的時間伸縮法(DTW: Dynamic Time Warping)とは2つの波形データの類似度を測定する手法です。
本記事では、Pythonで fastdtw
というライブラリを用いて、DTW を簡易的に計算します。
DTW のアルゴリズムについては次の記事で解説しています。
また、ライブラリを用いずに DTW の実装する方法については次の記事にコードを載せています。
サンプルコード
環境構築
以降にあるコードを実行するためには、 fastdtw
ライブラリをインストールする必要があります。
仮想環境の構築
Python 環境を特に気にしない場合は 次に 進んでください。
お手持ちの環境を変更したくない場合は、ターミナルで次のコマンドを実行して仮想環境を用意しましょう。
python
コマンドが通らない場合は、 python3
や py
をお試しください。
-
Windows の場合
仮想環境の作成と有効化python -m venv .venv .venv/Script/actvate
-
Mac / Linux の場合
仮想環境の作成と有効化python -m venv .venv . .venv/bin/activate
もしくは
仮想環境の作成と有効化python -m venv .venv source .venv/bin/activate
次のように、ターミナルに仮想環境名(上記のコマンドの場合 .venv
)が表示されるようになります。
(.venv) $
ライブラリのインストール
ターミナルで次のコマンドを実行してライブラリをインストールします。
pip install fastdtw scipy numpy matplotlib japanize-matplotlib
ライブラリ | 目的 |
---|---|
fastdtw 、scipy 、numpy
|
DTWを簡易的に実装するため |
matplotlib |
図を作成するため |
japanize-matplotlib |
図で日本語を表示させるため |
なお、VSCode でノートブック形式( .ipynb
)を実行する場合は ipykernel
ライブラリも必要です。
pip install ipykernel
波形データの用意
本記事では疑似的なデータとして、前回の記事 と同様の波形を用意します。
Q = [3, 4, 3, 3, 2, 3]
S = [1, 6, 4, 4, 1, 5]
次のコードで各波形データの形状を確認できます。
from matplotlib import pyplot as plt
import japanize_matplotlib
# 2つの時系列データをプロットする
plt.plot(Q, label='Q')
plt.plot(S, label='S')
# 凡例の表示
plt.legend()
# グラフタイトル
plt.title('波形データ')
# グラフを表示する
plt.show()
DTW距離の計算
次のコードで DTW 距離を計算します。
import numpy as np
from scipy.spatial.distance import euclidean
from fastdtw import fastdtw
# データの整形
Q = np.array(Q).reshape(-1, 1)
S = np.array(S).reshape(-1, 1)
dist, path = fastdtw(Q, S, dist=euclidean)
print(f"DTW距離: {dist}")
print(f"各点のパス:{path}")
次が出力されると成功です。
TW距離: 9.0
各点のパス:[(0, 0), (1, 1), (1, 2), (1, 3), (2, 3), (3, 3), (4, 4), (5, 5)]
次のコードでデータポイント間の対応関係を確認できます。
# 2つの時系列データをプロットする
plt.plot(Q, label='Q')
plt.plot(S, label='S')
# データポイントの対応関係をプロットする
for index_Q, index_S in path:
plt.plot([index_Q, index_S], [Q[index_Q], S[index_S]], color='gray', linestyle='dotted', linewidth=1)
# プロット
plt.legend()
plt.title(f'波形データ(DTW距離={dist})')
plt.show()
上記のコードを実行すると、次のグラフがプロットされます。
$(横軸, 縦軸) = (1, 4), (2, 4), (2, 3), (3, 4)$ の点にご注目ください。
$(1, 4), (2, 4)$ 間と $(2, 3), (3, 4)$ 間のパスについて、不自然な対応となっています。
この原因として fastdtw
は計算量を抑えるために近似的な処理をしていることが考えられます。
参考