vaexでどこまでデータ前処理ができるのか試してみる

## はじめに
「Pythonでデータ前処理」と言ったら、多くの方が「...Pandas!」となると思います。しかしPandasは大きなデータを扱うときに処理速度が遅いなんてことがあります。
そこで色々と調べていたら「[Pandasで行うデータ処理を100倍高速にするOut-of-CoreフレームワークVaex](https://blog.ikedaosushi.com/entry/2019/04/14/003916)」という、あまりにもキャッチーなタイトルのサイトがあったので、Vaexを少し触ってみました。
以下では簡単に、

- Vaexとは何か？
- Pandasと比較してどのぐらい早いの?
- 結局PandasとVaexどっち使うの?<br>
を記載します。

## Vaexとは何か？
必要なデータを適宜読み出して処理したり(アウトオブコア)、分散処理(マルチコア)を行って、データ処理を行えるのがVaexらしいです。
確かにPandasだとメモリに乗らないほどの大規模データの処理が大変だったりするので、早いだけでなく大規模データを扱うにも適しているんですね。

あとVaexではデータファイルをhdf5に書き換えて読みます。hdf5というのは、[フォルダ+データファイル]をひとまとめにしたファイルのことだそうです。([HDF5形式のファイル (1) HDF5って？](http://villageofsound.hatenadiary.jp/entry/2014/10/29/021605)より引用)
hdf5に書き換えて読むので、必要なデータを適宜読み出して処理する(アウトオブコア)ことができるんですね。

## Pandasと比較してどのぐらい早いの?
結論から述べます。Vaexで実行できる処理なら処理速度は「(感覚的に)1億倍早い」です。処理を待っている時間は、通常よりも長く感じるので、本当に「(感覚的に)1億倍早い」です。
では実際に試した処理について、どの程度時間がかかったか記載していきます。
今回は[ASHRAE-Great Energy Predictor III](https://www.kaggle.com/c/ashrae-energy-prediction)のtrain.csvとbuilding_metadata.csvを利用していきます。
train.csvは20,216,100行4列で686.8 MB、 building_metadata.csvは1,449行6列で49KBです。

|実行処理  |Pandas  |Vaex  |
|---|---|---|
|データの読み込み	  |10.2  |0.0695  |
|文字列をtimestampに変換  |1.62  |0.00368  |
|データの結合  |6.22  |0.00102  |
|timestampから年/月/日/曜日の情報抽出  |5.55  |0.0101  |
|列同士の演算  |3.93  |0.00822  |
|(仮)可視化 - ヒストグラム  |0.919  |0.575  |
|GROUPBY(一部データで実行)  |0.0032  |0.00907  |
|欠損値の確認  |6.71  |328!?  |
(※時間は全て秒)
(※Vaexでのデータの読み込みでは、hdf5ファイルを読み込んでいます。)

上記確認していただくとお分かりのように、大体の処理が事実2兆倍ぐらい早いことがわかります。
しかし欠損値の確認はヤバイです。何が起きたんだという感じです。Vaexで全列の欠損を確認する際は、forループを回さなければいけなそうだったので、これはこれでしょうがないのですかね。(何かいい方法あったら教えてください。)
ということで以下にコードを記載しておきます。

データの読み込み

```python
%%time
pd_train = pd.read_csv('train.csv')
pd_buildingmeta = pd.read_csv('building_metadata.csv')
```

```python
%%time
vx_train = vx.open('train.hdf5')
vx_buildingmeta = vx.open('building_metadata.hdf5')
```

文字列をtimestampに変換

```python
%%time
pd_train['timestamp'] = pd.to_datetime(pd_train["timestamp"])
```

```python
# vaexにはpd.to_datetime関数みたいな便利機能がないのでdatetimeモジュールを利用
%%time
vx_train['timestamp'] = vx_train["timestamp"].apply(lambda x: datetime.strptime(x, '%Y-%m-%d %H:%M:%S'))
```

データの結合

```python
%%time
pd_train_buildingmeta = pd_train.merge(pd_buildingmeta, left_on='building_id',
             right_on='building_id')
```

```python
%%time
vx_train_buildingmeta = vx_train.join(vx_buildingmeta, left_on='building_id',
              right_on='building_id', lprefix='train', rprefix='building')
```

timestampから年/月/日/曜日の情報抽出

```python
%%time
pd_train_buildingmeta["year"] = pd_train_buildingmeta["timestamp"].dt.year
pd_train_buildingmeta["month"] = pd_train_buildingmeta["timestamp"].dt.month
pd_train_buildingmeta["day"] = pd_train_buildingmeta["timestamp"].dt.day
pd_train_buildingmeta["hour"] = pd_train_buildingmeta["timestamp"].dt.hour
pd_train_buildingmeta["minute"] = pd_train_buildingmeta["timestamp"].dt.minute
pd_train_buildingmeta["weekofyear"] = pd_train_buildingmeta["timestamp"].dt.weekofyear
```

```python
%%time
vx_train_buildingmeta["year"] = vx_train_buildingmeta["timestamp"].dt.year
vx_train_buildingmeta["month"] = vx_train_buildingmeta["timestamp"].dt.month
vx_train_buildingmeta["day"] = vx_train_buildingmeta["timestamp"].dt.day
vx_train_buildingmeta["hour"] = vx_train_buildingmeta["timestamp"].dt.hour
vx_train_buildingmeta["minute"] = vx_train_buildingmeta["timestamp"].dt.minute
vx_train_buildingmeta["weekofyear"] = vx_train_buildingmeta["timestamp"].dt.weekofyear
```

列同士の演算

```python
%%time
pd_train_buildingmeta['age_of_a_building'] = pd_train_buildingmeta['year'] - pd_train_buildingmeta['year_built']
```

```python
%%time
vx_train_buildingmeta['age_of_a_building'] = vx_train_buildingmeta['year'] - vx_train_buildingmeta['year_built']
```

(仮)可視化 - ヒストグラム

```python
%%time
pd_train_buildingmeta["meter_reading"].hist()
```
![download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207724/6f9d62fb-8d72-6b1d-5e89-94ea016a622a.png)


```python
%%time
vx_train_buildingmeta.plot1d(vx_train_buildingmeta["meter_reading"], what="count(*)")
```

![download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207724/37ac8e78-2b78-aab9-9dd2-5fddce199db7.png)
(...これは微妙)

GROUPBY（10,000行抽出して実行）

```python
%%time
selected_pd_train.groupby("meter").agg({"meter_reading": "mean"})
```

```python
%%time
selected_vs_train.groupby("meter").agg({"meter_reading": "mean"})
```


欠損値の確認

```python
%%time
pd_buildingmeta.isnull().sum()
```

```python
# forループを実装する以外の方法が見つからなかったため、forループを利用
%%time
for col in vx_buildingmeta.column_names:
    print(vx_buildingmeta[col].isna().sum())
```


## 結局PandasとVaexどっち使うの?
まあ列同士の演算とか文字列操作等の限られた操作&大規模データならVaexはいいかも。

しかし
- multiple keyでのデータ結合ができない
- 可視化が微妙
- 欠損値の確認遅い　　
など一部歯痒いところがあったので、これからの改善に期待します。

参考サイト：
https://vaex.readthedocs.io/en/latest/
https://blog.ikedaosushi.com/entry/2019/04/14/173307
https://blog.ikedaosushi.com/entry/2019/04/14/003916
http://villageofsound.hatenadiary.jp/entry/2014/10/29/021605
http://www.nakl.t.u-tokyo.ac.jp/~masuda/papers/pnt07Symp.pdf