【データ処理】boxcox変換で正規分布に近づける

# はじめに

機械学習ではデータの分布を正規分布に近づけると、予測精度が改善する場合が多々あります。正規分布に近づける手法の一つとして、boxcox変換が存在します。

# boxcox変換
boxcox変換は対数変換などと異なり、多様な分布を正規分布に近づけることが可能であるため、様々なデータに応用可能です。本記事では以下の処理をPythonで実行します。
- Pythonでboxcox変換を行う
- boxcox変換後のデータをもとに戻す

boxcox変換の詳細な説明は省略しますが、下記のサイトで分かりやすく説明されています。
[Box-cox変換を用いて正規分布に従わないデータを解析をしてみよう！](https://toukei-lab.com/box-cox%E5%A4%89%E6%8F%9B%E3%82%92%E7%94%A8%E3%81%84%E3%81%A6%E6%AD%A3%E8%A6%8F%E5%88%86%E5%B8%83%E3%81%AB%E5%BE%93%E3%82%8F%E3%81%AA%E3%81%84%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E8%A7%A3%E6%9E%90)
[Box-Cox変換を理解してみる](https://qiita.com/dyamaguc/items/b468ae66f9ce6ee89724)

# 環境
- Google Colaboratory Pro

# コード
### モジュールのimport・変換前のデータ
```python:
import pandas as pd
from sklearn.datasets import load_boston
import seaborn as sns
from statistics import mean, median, variance, stdev
from scipy.stats import boxcox
from scipy.special import inv_boxcox
```

データとしてscikit-learnのBoston house-prices (ボストン市の住宅価格)を使用します。

```python:
boston = load_boston()
y = boston.target
```

変換前の各指標を計算します。
```python:
m = mean(y)
med = median(y)
vari = variance(y)
std = stdev(y)
print("平均: ", m)
print("中央値: ", med)
print("分散: ", vari)
print("標準偏差: ", std)
```

```:
#実行結果
平均:  22.532806324110673
中央値:  21.2
分散:  84.58672359409854
標準偏差:  9.197104087379817
```
変換前のpriceの分布をヒストグラムで描画します。
```python:
sns.distplot(y)
```
![変換前.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2657466/f309cd0d-6824-8e3c-8842-7cf1c9b7876e.png)

### boxcox変換・変換後のデータ
boxcox変換を実行します。
```python:
y_boxcox = boxcox(y)
```
boxcox()の戻り値について調べます。
```python:
print(type(y_boxcox),"要素数:",len(y_boxcox))
print("変換後のデータ: \n",y_boxcox[0][:10])
print("λ値: ",y_boxcox[1])
```
```
#実行結果
<class 'tuple'> 要素数: 2
変換後のデータ: 
 [4.57293637 4.36558166 5.33694273 5.25495447 5.42860689 4.93592663
 4.48001683 4.81796329 3.85654088 4.10949441]
λ値:  0.2166209012915364
```
boxcox()の戻り値は要素数2のタプルです。1つ目の要素が変換後の値（リスト）で、2つ目の要素がλ値というboxcox変換にて使用されるパラメータです。このλ値を参照することで変換前の分布に戻すことも可能です。

変換後の各指標を計算します。
```python:
m_boxcox = mean(y_boxcox[0])
med_boxcox = median(y_boxcox[0])
vari_boxcox = variance(y_boxcox[0])
std_boxcox = stdev(y_boxcox[0])
print("平均: ", m_boxcox)
print("中央値: ", med_boxcox)
print("分散: ", vari_boxcox)
print("標準偏差: ", std_boxcox)
```
```:
#実行結果
平均:  4.326198793674132
中央値:  4.329286356920582
分散:  0.6132144144256096
標準偏差:  0.7830800817449066
```
平均値≒中央値となり正規分布に近づいています。

変換後の分布をヒストグラムで描画します。
```python:
sns.distplot(y_boxcox[0])
```
![変換後.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2657466/4ecbbca1-7ba4-99d4-b3d4-40aaabcc49c3.png)
中央値を中心として左右対称となり、正規分布に近づいています。

### inv_boxcox変換・変換後のデータ
変換後のデータを変換前の分布に戻します。戻すにはinv_boxcoxモジュールを使用します。（invはinverseの略で反対の意味です。）
```python:
y_inv = inv_boxcox(y_boxcox[0],y_boxcox[1])
```
inv_boxcox変換後の各指標を計算します。
```python:
m_inv = mean(y_inv)
med_inv = median(y_inv)
vari_inv = variance(y_inv)
std_inv = stdev(y_inv)
print("平均: ", m_inv)
print("中央値: ", med_inv)
print("分散: ", vari_inv)
print("標準偏差: ", std_inv)
```
```:
#実行結果
平均:  22.532806324110673
中央値:  21.199999999999992
分散:  84.58672359409854
標準偏差:  9.197104087379817
```
boxcox変換前の値に戻っています。

inv_boxcox変換後の分布をヒストグラムで描画します。
```python:
sns.distplot(y_inv)
```
![inv変換後.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2657466/61b84484-b590-809d-5eee-4c3115d2b889.png)
分布もboxcox変換前に戻っています。