時系列のクラスタリングの場合、tslearnに実装されているのでそれを用います。
時系列と非時系列のクラスタリングの違い
非時系列の場合、1行がidとなり、列が変数でその変数でidをクラスタ化します。対して時系列の場合、(よく見る形は)行が時間であり、列が観測対象で列を時系列する形が多いです。
▼なお、非時系列のクラスタはこれ
やりかた
sklearnと同じインターフェイスで扱えます。
from tslearn.clustering import TimeSeriesKMeans km = TimeSeriesKMeans(n_clusters=3, metric="dtw") km.fit(データ)
試してみる
テストデータの生成
テストデータをつくって試してみます。
import random import math #データを生成する関数(4つ) base = { "a": lambda x : random.random(), "b": lambda x : x * 0.01 + random.random() * 0.1, "c": lambda x : math.sin(x + random.random()) + random.random(), "d": lambda x : math.sin(x + random.random()) + random.random() } #テストデータと種類を格納 test_cls = [] #正解のクラスター test_X = [] for i in range(20): cls,f = random.choice(list(base.items())) test_X.append([f(i) for i in range(100)]) test_cls.append(cls)
aは完全にランダム
bは経過とともに上がっていくデータ
cはsin波
dはcos波
ここからランダムに選んで20のデータをつくります。
import matplotlib.pyplot as plt for d in test_X: plt.plot(d,c='b')
クラスタリングしてみる
まずtslearnで扱えるデータに変換します
from tslearn.utils import to_time_series_dataset ts = to_time_series_dataset(test_X) #型の確認 type(ts) #numpy.ndarray #形の確認 ts.shape #(20, 100, 1)
専用のクラスとかではなく、numpyのarrayのようです。
長さの異なるデータを入れると最長になるように調整したりするのが目的のようです。
というわけでクラスタリングします
今回は正解が4つなのを知っているので、4つに分類します。
km = TimeSeriesKMeans(n_clusters=4, metric="dtw") labels = km.fit_predict(ts)
結果の確認
目で見ておきます。
from collections import defaultdict import matplotlib.pyplot as plt res = defaultdict(list) for data,label in zip(test_X,labels): res[str(label)].append(data) for k,v in res.items(): for row in v: plt.plot(row) plt.show()
目で見る限りうまく分けられているようなかんじがします。
クロス集計でもみておきます。
import pandas as pd pd.crosstab(pd.Series(test_cls),pd.Series(labels))
col_0 0 1 2 3 row_0 a 0 0 7 0 b 3 0 0 0 c 0 0 0 2 d 0 2 0 6
かねがねうまく分けられているようです。
ただし、どうやらcとdが分けられていないという結果でした。
と、ここまできちんとコードを読んでいる人は気づいたと思うのですが、実は最初のデータ生成の際にcとdが実は両方sinになっちゃってるんですね。
分けられないのはむしろ正しい結果でした。
クラスターを三つに分けなおして、再度実行すると。
col_0 0 1 2 row_0 a 7 0 0 b 0 0 3 c 0 2 0 d 0 8 0
ちゃんとc,dはおなじ1というクラスターになりました。