Pypotsを使って多変量時系列分析をしてみた

# はじめに
こんばんは。
駆け出しデータエンジニアのotoyaです。
今回はpypotsを使って多変量の時系列分析をしてみました。

==
多変量の時系列分析を行う際に、
・欠損値どうしよう、、、
・とりあえずこの日のデータは出せた。未来のデータ予測するときはどうしよう、、
・やれないことはないけど面倒だな、、
と思っていたそんなある日。
先輩「pypotsっていうのがあるよ」

自分「、、、何だそれ！」

==
となりちょっとだけ使ってみました。

(そしてこれ日本語の記事1個もないんじゃ、、)


# Pypotsとは？
![スクリーンショット 2024-12-14 22.29.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3680837/a976f790-6f0e-f0ad-dff9-0733c1f4dfd0.png)

pypotsとは、主に欠損値補完に特化したpythonライブラリです。

> Mission: PyPOTS (pronounced "Pie Pots") is born to become a handy toolbox that is going to make machine learning on POTS easy rather than tedious, to help engineers and researchers focus more on the core problems in their hands rather than on how to deal with the missing parts in their data. PyPOTS will keep integrating classical and the latest state-of-the-art machine learning algorithms for partially-observed multivariate time series. For sure, besides various algorithms, PyPOTS is going to have unified APIs together with detailed documentation and interactive examples across algorithms as tutorials.
(githubから引用: https://github.com/WenjieDu/PyPOTS)

> 日本語訳: PyPOTS（「Pie Pots」と発音します）は、POTS（部分的に観測された多変量時系列）の機械学習を面倒な作業ではなく簡単にする便利なツールボックスとして生まれました。これにより、エンジニアや研究者がデータの欠損部分に対処する方法ではなく、手元の核心的な課題により集中できるよう支援します。PyPOTSは、従来のアルゴリズムと最先端の機械学習アルゴリズムを統合し続け、部分的に観測された多変量時系列データに対応します。さらに、さまざまなアルゴリズムだけでなく、統一されたAPI、詳細なドキュメント、アルゴリズムごとのチュートリアルとしてのインタラクティブな例も提供する予定です。

しかし、欠損地補完だけでなく、分類、クラスタリング、予測や異常検出タスクなど様々なタスクに対応しています。

今回は欠損値補完と予測、異常検出を行なってみたのでやってみました。

# モデルについて
2024年12月14日時点では下記のモデルが使えてとても使えるものが充実しているイメージですね！

[LLM]
Time-Series.AI

[NN]
TEFN, FITS, TimeMixer, iTransformer, ModernTCN
ImputeFormer, SAITS FreTS, Koopa, Crossformer, TimesNet
PatchTST, ETSformer, MICN, DLinear, TiDE, CSAI
SegRNN, SCINet, Nonstationary, FiLM, RevIN_SCINet, Pyraformer, Raindrop,
FEDformer, Autoformer, CSDI, Informer, US-GAN, CRLI, BTTF
StemGNN, Reformer, GP-VAE, VaDER, M-RNN, BRITS, GRU-D
TCN, Transformer

[Naive]
Lerp, LOCF/NOCB, Mean, Median

# データについて
Pypotsをご利用する際には下記のイメージを忘れないようにしておくと扱いやすいはずです。
・3次元のデータを使います。
画像のように"系統(商品など), 特徴, タイムスタンプ"を使って3次元のデータがあるという感じです

![スクリーンショット 2024-12-14 22.58.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3680837/b46f286c-96de-7e7a-6e16-fb7a44ff2dd6.png)

・データを辞書のvalueに入れてよく使います。
辞書のKeyの名前とかも決まっているのでご注意を！

```
# ライブラリのimport
import numpy as np
from sklearn.preprocessing import StandardScaler
from pypots.data import load_specific_dataset # 今回はdefaultで用意してあるデータを使います。

# データのロードとpreprocess
data = load_specific_dataset('physionet_2012')
X = data['train_X']
num_samples = len(X)
X = StandardScaler().fit_transform(X.reshape(-1, X.shape[-1])).reshape(X.shape)
X_ori = X  # keep X_ori for validation
```


# データの欠損値補完
今回はSAITSというTransformer形式のモデルによって欠損値補完を実施します。
SAITSについては今論文読み読み中なのでお待ちを、、
整い次第展開予定です。

```
from pygrinder import mcar
from pypots.imputation import SAITS
from pypots.utils.metrics import calc_mae

X = mcar(X, 0.1)
dataset = {"X": X}  # 辞書に変えてます
print(X.shape)  # (7671, 48, 37), 7671 samples, 48 time steps, 37 features

# モデルの初期化
saits = SAITS(
    n_steps=48,
    n_features=37,
    n_layers=2,
    d_model=256,
    d_ffn=128,
    n_heads=4,
    d_k=64,
    d_v=64,
    dropout=0.1,
    epochs=10,
    saving_path="examples/saits", # set the path for saving tensorboard logging file and model checkpoint
    model_saving_strategy="best", # only save the model with the best validation performance
)

# fitさせる
saits.fit(dataset)
imputation = saits.impute(dataset)

# 結果の確認
indicating_mask = np.isnan(X) ^ np.isnan(X_ori)mae = calc_mae(imputation, np.nan_to_num(X_ori), indicating_mask)

```

何とコレだけでデータがimputeが出来るとは、、、恐るべし、、、！

# 予測
先ほどのデータとモデルを使って未来を予測させていきたいと思います！
また、今回の方法は、"ウォークフォワード法（Walk Forward Validation）"という手法での予測です。
1日予測して1日ずらしてってていう方法ですね。

```
# 1timestepずつ予測して5timestepまで
horizon = 5
current_input = dataset.copy()
predictions = []

for step in range(horizon):
    prediction_step = saits.predict(current_input)
    predictions.append(prediction_step['imputation'])
    current_input_array = np.concatenate([current_input['X'][:, 1:, :], prediction_step['imputation'][:, -1:, :]], axis=1)
    current_input = {"X": current_input_array}

preds = np.concatenate(predictions[:, 'imputation'])
```
コレだけで一応データの未来予測ができました。

これでどれだけ精度が出せるのかは今検証中です。

# 異常検知
異常検知はBRITSモデルというのを使っています。
これは先ほどのXデータに合わせて、このsampleは異常(1)か異常ではない(0)かのyデータが必要です。

```
# データのロード Keyが決まっているので注意を
class_dataset = {
    "X": dataset["X"],
    "y": data["train_y"]
    }

from pypots.classification import brits
brits = brits.BRITS(
    n_steps=48,
    n_features=37,
    n_classes=2,
    rnn_hidden_size=1,
    epochs=5
)
brits.fit(class_dataset) # datasetの中にYも必要になる

# 予測
pred = brits.predict(class_dataset)
pred
```

本当に楽だ、、、


# 最後に
モデルの精度を上げるために肉付けは必要ですが、
軽く使ってみて、コレで出来るのは非常に楽だなと感じました。

もし皆さんも機会があれば使ってみてください！
ありがとうございました！
(夜書いたので誤字多いかもしれません💦)