Time Series K-meansのデータ加工と3種類のクラスタリング方法を徹底解説【時系列クラスタリング】

Time Series K-meansのデータ加工と3種類のクラスタリング方法を徹底解説【時系列クラスタリング】
目次
はじめに
こちらの記事に時系列クラスタリングをTime Series K-meansでやってみたという記事がありますが、今回はTime Series K-meansで時系列クラスタリングを行う前にどのようにデータ加工をしたらいいのか、またTime Series K-meansにある3種類のクラスタリング方法を解説したいと思います。
【AI初学者向け】Time Series K-meansで時系列データをクラスタリングしてみた | Data Driven Knowledgebase (since2020.jp)
ではさっそくデータ加工の方法について解説します!
Time Series K-means用のデータ加工と前処理
Time Series K-meansにデータを渡す前に以下の加工が必要です。
私が参考にしたのは公式のコードです。
k-means — tslearn 0.6.3 documentation
<Time Series K-meansにデータを渡す前に必要な加工>
①日付とクラスタリングしたい変数の2変数のみ残す
②時系列クラスタリング用にスケーリング
③numpy配列に変換
④(複数時系列の数,時系列の日付の数,1)の3次元ベクトルに変換
③について詳しく解説すると、numpy配列に変換するときに、行が複数時系列になり、カラムが日付になるイメージです。
例:
公式コード
!pip install tslearn
import numpy
import pandas as pd
import matplotlib.pyplot as plt
from tslearn.clustering import TimeSeriesKMeans
from tslearn.datasets import CachedDatasets
from tslearn.preprocessing import TimeSeriesScalerMeanVariance, \
TimeSeriesResampler
seed = 0
numpy.random.seed(seed)
X_train, y_train, X_test, y_test = CachedDatasets().load_dataset("Trace")
X_train = X_train[y_train < 4] # Keep first 3 classes
numpy.random.shuffle(X_train)
# Keep only 50 time series
X_train = TimeSeriesScalerMeanVariance().fit_transform(X_train[:50])
# Make time series shorter
X_train = TimeSeriesResampler(sz=40).fit_transform(X_train)
sz = X_train.shape[1]
# Soft-DTW-k-means
print("DTW k-means")
# data_3d の形状を確認
print(X_train.shape)
sdtw_km = TimeSeriesKMeans(n_clusters=3,
metric="softdtw",
metric_params={"gamma": .01},
verbose=True,
random_state=seed)
こちらに公式のコードのTime Series K-meansを行う前のデータを載せます。
このデータだと、(50,40,1)という50種類の時系列データと40分の日付データをクラスタリングしていることが分かります。
ちなみに私はこのようにクラスタリングしたいデータを加工しました。
def df_to_DSK(df):
# データフレームをピボットして numpy 配列に変換
pivot_df = df.pivot(index='ID', columns='date', values='y')
print(pivot_df)
numpy_array = pivot_df.to_numpy()
# 2次元から3次元に変換
numpy_array = numpy_array [:, :, numpy.newaxis]
# data_3d の形状を確認
print(numpy_array .shape)
return numpy_array
Time Series K-meansにある3種類のバリアント
引用:k-means — tslearn 0.6.3 documentation
Time Series K-meansには3種類あります。
<Time Series K-meansの3種類>
①Euclidean K-means
②DBA-K-means
③Soft-DTW-K-means
それぞれの意味はこちらに記されています。
[1] F. Petitjean, A. Ketterlin & P. Gancarski. A global averaging method for dynamic time warping, with applications to clustering. Pattern Recognition, Elsevier, 2011, Vol. 44, Num. 3, pp. 678-693 [2] M. Cuturi, M. Blondel “Soft-DTW: a Differentiable Loss Function for Time-Series,” ICML 2017.
一般的なDTWによるクラスタリングはDBA-K-meansと呼ばれているものですね。
3種類のTime Series K-meansのコード
さて、これら3種類の方法で時系列クラスタリングを行うにはどうしたらよいでしょうか?
以下のコードに記しておきました。
# Euclidean k-means
print("Euclidean k-means")
km = TimeSeriesKMeans(n_clusters=3, verbose=True, random_state=seed)
y_pred = km.fit_predict(X_train)
# DBA-k-means
print("DBA k-means")
dba_km = TimeSeriesKMeans(n_clusters=3,
n_init=2,
metric="dtw",
verbose=True,
max_iter_barycenter=10,
random_state=seed)
y_pred = dba_km.fit_predict(X_train)
# Soft-DTW-k-means
print("Soft-DTW k-means")
sdtw_km = TimeSeriesKMeans(n_clusters=3,
metric="softdtw",
metric_params={"gamma": .01},
verbose=True,
random_state=seed)
y_pred = sdtw_km.fit_predict(X_train)
時系列クラスタリングの結果の可視化方法
以下のコードのようにして可視化を行いました。
y_pred = sdtw_km.fit_predict(X_train)
for yi in range(3):
plt.subplot(3, 3, 7 + yi)
for xx in X_train[y_pred == yi]:
plt.plot(xx.ravel(), "k-", alpha=.2)
plt.plot(sdtw_km.cluster_centers_[yi].ravel(), "r-")
plt.xlim(0, sz)
plt.ylim(-4, 4)
plt.text(0.55, 0.85,'Cluster %d' % (yi + 1),
transform=plt.gca().transAxes)
if yi == 1:
plt.title("Soft-DTW $k$-means")
plt.tight_layout()
plt.show()
実行後はこのように表示されます。
Time Series K-meansの全コード
公式のコードはこちらにありますので、ご参照下さい。こちらのコードをコピーするだけでも実行できます。
k-means — tslearn 0.6.3 documentation
私のクラスタリングしたかったものを時系列クラスタリングしたいときは以下のように行いました。
#TimeSeriesKMeansを実行
#!pip install tslearn
import pandas as pd
import numpy
import time
import matplotlib.pyplot as plt
import seaborn as sns
from tslearn.clustering import TimeSeriesKMeans
from tslearn.datasets import CachedDatasets
from tslearn.preprocessing import TimeSeriesScalerMeanVariance, \
TimeSeriesResampler
def df_to_DSK(df):
# データフレームをピボットして numpy 配列に変換
pivot_df = df.pivot(index='ID', columns='date', values='y')
print(pivot_df)
numpy_array = pivot_df.to_numpy()
# 2次元から3次元に変換
numpy_array = numpy_array [:, :, numpy.newaxis]
# data_3d の形状を確認
print(numpy_array .shape)
return numpy_array
def TimeSeriesKmeans(df,n):
df_train = df[df['date'] < '2022-11-25']
numpy_array=df_to_DSK(df_train)
X_train = numpy_array
#スケーリング
X_train_scaled = TimeSeriesScalerMeanVariance().fit_transform(X_train)
# Define a seed value
seed = 42
# DBA-k-means
print("DBA k-means")
dba_km = TimeSeriesKMeans(n_clusters=n,
n_init=2,
metric="dtw",
verbose=True,
max_iter_barycenter=10,
random_state=seed)
y_pred = dba_km.fit_predict(X_train_scaled)
return y_pred,X_train_scaled,n,dba_km
def show_result(X_train_scaled,n,dba_km):
sz = X_train_scaled.shape[1]
for yi in range(n):
plt.subplot(3, 3, 4 + yi)
for xx in X_train_scaled[y_pred == yi]:
plt.plot(xx.ravel(), "k-", alpha=.2)
plt.plot(dba_km.cluster_centers_[yi].ravel(), "r-")
plt.xlim(0, sz)
#plt.ylim(-4, 4)
plt.text(0.55, 0.85,'Cluster %d' % (yi + 1),
transform=plt.gca().transAxes)
if yi == 1:
plt.title("DBA $k$-means")
plt.tight_layout()
plt.show()
def show_Cluster(mergerd_df,X_train_scaled):
X_train_reshaped = X_train_scaled.reshape(1059, 74, 1)
X_train_flattened = X_train_reshaped.reshape((-1, 1))
df = pd.DataFrame(X_train_flattened, columns=['scaled_y'])
df_result = pd.concat([mergerd_df, df], axis=1)
subset_df = df_result
# グラフ描画
plt.figure(figsize=(10, 6))
sns.lineplot(x='date', y='scaled_y', hue='cluster_label', style='ID', data=subset_df, markers=False, palette='viridis')
# プロットの設定
plt.xlabel('Date')
plt.ylabel('Scaled y')
plt.title('Line Plot with Cluster Labels')
#plt.ylim(0,500)
plt.show()
return df_result
#結果をpandas型に変換
def DSK_to_df(df,y_pred):
result = pd.DataFrame()
result['ID'] = df['ID'].drop_duplicates()
result['cluster_label'] = y_pred
mergerd_df = pd.merge(df,result,on='ID',how='inner')
print(mergerd_df)
return mergerd_df
def main():
df = pd.read_csv('保存ファイル名')
# 計測開始
start_time = time.time()
y_pred,X_train_scaled,n,dba_km = TimeSeriesKmeans(df,3)
mergerd_df=DSK_to_df(df,y_pred)
# 計測終了
end_time = time.time()
elapsed_time = end_time - start_time
print(f"経過時間: {elapsed_time}秒")
#結果を描画
show_result(X_train_scaled,n,dba_km)
show_Cluster(mergerd_df,X_train_scaled)
return mergerd_df
result_df = main()