tsfreshにおける特徴量の調整（追加・削除・オリジナル特徴量の追加）

# はじめに
時系列データに対して機械学習手法を適用する際に、sliding windowなどでデータを小区間に区切り特徴量を抽出などすると思いますが、pythonで特徴量探索を行う場合に良く利用されるライブラリの一つに**tsfresh**があります。ここでは、tsfreshについてデータから抽出する特徴量をユーザが追加・削除、また、独自に定義・実装したオリジナル特徴量を追加する方法を見ていきます。

今回使用するコードは[こちら](https://github.com/niwashing/tsfresh-customize-features)にあります。

# サンプルデータのロード
初めに解説で利用するために、tsfreshで用意されているサンプルデータをダウンロードします。


```python
from tsfresh.examples.robot_execution_failures import (
    download_robot_execution_failures,
    load_robot_execution_failures,
)

download_robot_execution_failures()
timeseries, _ = load_robot_execution_failures()
timeseries.head(5)
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>id</th>
      <th>time</th>
      <th>F_x</th>
      <th>F_y</th>
      <th>F_z</th>
      <th>T_x</th>
      <th>T_y</th>
      <th>T_z</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>1</td>
      <td>0</td>
      <td>-1</td>
      <td>-1</td>
      <td>63</td>
      <td>-3</td>
      <td>-1</td>
      <td>0</td>
    </tr>
    <tr>
      <th>1</th>
      <td>1</td>
      <td>1</td>
      <td>0</td>
      <td>0</td>
      <td>62</td>
      <td>-3</td>
      <td>-1</td>
      <td>0</td>
    </tr>
    <tr>
      <th>2</th>
      <td>1</td>
      <td>2</td>
      <td>-1</td>
      <td>-1</td>
      <td>61</td>
      <td>-3</td>
      <td>0</td>
      <td>0</td>
    </tr>
    <tr>
      <th>3</th>
      <td>1</td>
      <td>3</td>
      <td>-1</td>
      <td>-1</td>
      <td>63</td>
      <td>-2</td>
      <td>-1</td>
      <td>0</td>
    </tr>
    <tr>
      <th>4</th>
      <td>1</td>
      <td>4</td>
      <td>-1</td>
      <td>-1</td>
      <td>63</td>
      <td>-3</td>
      <td>-1</td>
      <td>0</td>
    </tr>
  </tbody>
</table>
</div>



# tsfreshを使った特徴量の抽出
まずdefaultの設定で特徴量を計算し、特徴量の数を見てみます。


```python
from tsfresh.feature_extraction import extract_features

features = extract_features(
    timeseries,
    column_id="id",
    column_sort="time",
).reset_index(drop=True)
```

    Feature Extraction: 100%|██████████| 88/88 [00:01<00:00, 60.17it/s]



```python
print("{}種類の特徴量が抽出されました。".format(len(features.columns)))
```

    4722種類の特徴量が抽出されました。

## プリセットされた特徴量のセットを使う
defaultの設定ではとても多くの特徴量が抽出されるため、特徴量を調整してみます。
tsfreshには以下のように幾つかのパラメータのセットとその設定がカテゴリごとに用意されており、ユーザが簡単に抽出する特徴量を選択することができます。

用意されているパラメータセットの設定

- ComprehensiveFCParameters
- EfficientFCParameters
- IndexBasedFCParameters
- MinimalFCParameters
- TimeBasedFCParameters

ここでは一例として`MinimalFCParameters`を使って特徴量を抽出します。`MinimalFCParameters`は合計、平均などシンプルな特徴量を集めたもので、各列に対し9個の特徴量をそれぞれ抽出します。
`extract_features`の`default_fc_parameters`に設定を指定することでプリセットされた特徴量を抽出できます。


```python
from tsfresh.feature_extraction import (
    MinimalFCParameters,
    extract_features,
)

settings = MinimalFCParameters()

features = extract_features(
    timeseries,
    column_id="id",
    column_sort="time",
    default_fc_parameters = settings,
).reset_index(drop=True)
```

    Feature Extraction: 100%|██████████| 88/88 [00:00<00:00, 768.93it/s]



```python
print("{}種類の特徴量が抽出されました。".format(len(features.columns)))
print("各変数に対しの以下の特徴量が抽出されました。")
[c for c in features.columns[features.columns.str.startswith('F_x')]]
```
    54種類の特徴量が抽出されました。
    各変数に対しの以下の特徴量が抽出されました。

    ['F_x__sum_values',
     'F_x__median',
     'F_x__mean',
     'F_x__length',
     'F_x__standard_deviation',
     'F_x__variance',
     'F_x__root_mean_square',
     'F_x__maximum',
     'F_x__minimum']



`extrace_features`への設定は辞書型で定義されており、key：モジュール名、value：モジュールへの入力パラメータとなっています。これらを追加または削除することで、より細かく特徴量抽出をコントロールできます。

ここで使用できる特徴量は`tsfresh.feature_extraction.feature_calculators`に定義されており、内容については[ドキュメント](https://tsfresh.readthedocs.io/en/latest/text/list_of_features.html)を見ながら、所望の特徴量を追加・削除します。


```python
settings = MinimalFCParameters()
print('settingsは辞書型となっています。')
settings
```
    settingsは辞書型となっています。

    {'sum_values': None,
     'median': None,
     'mean': None,
     'length': None,
     'standard_deviation': None,
     'variance': None,
     'root_mean_square': None,
     'maximum': None,
     'minimum': None}

```python
# 不要な特徴量を削除
del settings['sum_values']
del settings['median']
del settings['mean']

# 必要な特徴量を追加（key：モジュール名、value：パラメータ（リストで複数渡すことものできる））
settings["ar_coefficient"] = [
    {"coeff": 0, "k": 4},
    {"coeff": 1, "k": 4},
    {"coeff": 2, "k": 4},
    {"coeff": 3, "k": 4},
]
settings
```

    {'length': None,
     'standard_deviation': None,
     'variance': None,
     'root_mean_square': None,
     'maximum': None,
     'minimum': None,
     'ar_coefficient': [{'coeff': 0, 'k': 4},
      {'coeff': 1, 'k': 4},
      {'coeff': 2, 'k': 4},
      {'coeff': 3, 'k': 4}]}

```python
features = extract_features(
    timeseries,
    column_id="id",
    column_sort="time",
    default_fc_parameters = settings,
).reset_index(drop=True)
```

    Feature Extraction: 100%|██████████| 88/88 [00:00<00:00, 701.41it/s]


```python
print("{}種類の特徴量が抽出されました。".format(len(features.columns)))
print("各変数に対しの以下の特徴量が抽出されました。")
[c for c in features.columns[features.columns.str.startswith('F_x')]]
```
    60種類の特徴量が抽出されました。
    各変数に対しの以下の特徴量が抽出されました。

    ['F_x__length',
     'F_x__standard_deviation',
     'F_x__variance',
     'F_x__root_mean_square',
     'F_x__maximum',
     'F_x__minimum',
     'F_x__ar_coefficient__coeff_0__k_4',
     'F_x__ar_coefficient__coeff_1__k_4',
     'F_x__ar_coefficient__coeff_2__k_4',
     'F_x__ar_coefficient__coeff_3__k_4']

# オリジナル特徴量の追加
さらにtsfreshでは、あらかじめ用意された特徴量だけでなく自分で特徴量を実装し追加することができます。tsfreshを使用しなくても個別に特徴量を計算し追加することもできますが、tsfreshの枠組みに乗せることで例えば以下のような利点が得られます。

- 変数ごとに特徴量を計算してくれる（名前をつけてくれる）
- 検定で特徴量が削減できる

独自特徴量は主に以下の3ステップで追加できます。

1. 特徴量を計算する関数の作成
1. `tsfresh.feature_extraction.feature_calculators`に属性を追加
1. 設定を`extract_features`に渡して特徴量を追加

## 特徴量を計算する関数の作成
tsfreshにオリジナルの特徴量を追加するには、デコレータ（`@set_property`）をつけた関数を作ります。デコレータのパラメータは、単一の特徴量を返すか、複数の特徴量を返すかで以下のように異なります。

- 単一特徴量を返す場合：@set_property("fctype", "simple")
- 複数有徳跳梁を返す場合：@set_property("fctype", "combiner")

ここでは、単一特徴量を返す場合の例として、最大値と最小値の差を特徴量として追加してみようと思いますので、以下のように関数を定義しました。


```python
from tsfresh.feature_extraction.feature_calculators import set_property

@set_property("fctype", "simple")
def amplitude(x):
    # 最大値、最小値を返す
    return max(x) - min(x)
```

## `tsfresh.feature_extraction.feature_calculators`に属性を追加
tsfreshの中で特徴量計算モジュールは`feature_calculators`の中で定義されているため、以下のように新しく作った関数も`feature_calculators`に追加する必要があります。


```python
from tsfresh.feature_extraction import feature_calculators

setattr(feature_calculators, amplitude.__name__, amplitude)
```

## 設定を`extract_features`に渡して特徴量を追加
`default_fc_parameters`に定義した関数の名前を追加した辞書型変数を渡し、特徴量を抽出します。


```python
# 定義した関数名の辞書型変数を用意する。入力パラメータがない場合はvalueはNone
settings = {'amplitude': None}

# 特徴量を抽出する
features = extract_features(
    timeseries,
    column_id="id",
    column_sort="time",
    default_fc_parameters = settings,
).reset_index(drop=True)
```

    Feature Extraction: 100%|██████████| 88/88 [00:00<00:00, 823.76it/s]



```python
print("以下の特徴量が抽出されました。")
[c for c in features.columns]
```
    以下の特徴量が抽出されました。
    ['F_x__amplitude',
     'F_y__amplitude',
     'F_z__amplitude',
     'T_x__amplitude',
     'T_y__amplitude',
     'T_z__amplitude']



# まとめ
ここではtsfreshを使った時系列データの特徴量抽出について、抽出する特徴量を追加・削除さらに自分で定義した特徴量を追加する方法を見てきました。tsfreshの枠組みに載せることにより、色々な機能が使えたり、チームで活用できると思います。お試しください！