機械学習における前処理4　データの変換

[Aidemy](https://aidemy.net/)　2020/10/29
#はじめに
　こんにちは、んがょぺです！バリバリの文系ですが、AIの可能性に興味を持ったのがきっかけで、AI特化型スクール「Aidemy」に通い、勉強しています。ここで得られた知識を皆さんと共有したいと思い、Qiitaでまとめています。以前のまとめ記事も多くの方に読んでいただけてとても嬉しいです。ありがとうございます！
　今回は機械学習の前処理の4つ目の投稿になります。どうぞよろしくお願いします。

＊本記事は「Aidemy」での学習内容を「自分の言葉で」まとめたものになります。表現の間違いや勘違いを含む可能性があります。ご了承ください。

今回学ぶこと
・連続値について
・カテゴリ形データの変換
・スケール調整について
・縦持ちデータと横持ちデータについて

#連続値について
##連続値のカテゴリ化
・データの前処理時に、連続値を分割してカテゴリ化したい場合がある。
・具体的には、年齢のデータ[10,15,18,20,27,32]があり、これを「10代、20代、30代」に分けたい（連続値のカテゴリ化）と考える。

・実行は、pandasの__cut(x,bins=[],labels=[],right=)__関数で行う。
・各引数について
　・__「x」__は渡すデータを表す。
　・__「bins」__には区切りになる数値を配列で渡す。
　・__「labels」__にはカテゴリごとの名前を配列で渡す。
　・__「right」__には区間の分け方を「Aより大きくB以下」にするならTrueを、「A以上B未満」にするならFalseにする。（Default=Trueだが、日本人的思考ならFalseで「A以上B未満」の形にした方が直感的に分かりやすい）

```python
x = [10,15,18,20,27,32]
bins = [10,20,30,40] #10~20,20~30,30~40という意味
labels = ['10代','20代','30代']
#カテゴリ化
pd.cut(x,bins=bins,labels=labels,right=False)
#[10代,10代,10代,20代,20代,30代]
```

##カテゴリ型データをone-hotエンコーディングする
・前項でカテゴリ化したデータを、__そのカテゴリに属していれば「1」、属していなければ「0」__というように変換する。
・例えば、「10代」のカテゴリなら[1,1,1,0,0,0]といった出力になるようにする。
・このような変換を__「one-hotエンコーディング」__という。
・例えば{'age':[10代,10代,10代,20代,20代,30代]}というデータがDataFrame（変数df）で与えられていた場合
__pd.get_dummies(df['age'])__で変換できる。
・また、'age'以外にも列があり、その全てを変換したいときは引数は「df」のみで良い。
・変換後の列に元の列の名前を残したい時は、第二引数に__prefix='age'__のように列名を渡せば良い。

```python
result = pd.cut(x,bins=bins,labels=labels,right=False)
df = pd.DataFrame({'age':result})
#one-hotエンコーディングの実行
pd.get_dummies(df['age'],prefix='age')
'''
age_10代	age_20代	age_30代
0	1	0	0
1	1	0	0
2	1	0	0
3	0	1	0
4	0	1	0
5	0	0	1
'''
```

#スケール調整について
・モデルに渡す数値型のデータ項目の中に、相対的に値が大きなデータが入っていると、学習の効率を下げてしまう恐れがある。
・このようなときに、全ての数値型のデータ項目を一定の基準に収まるように値を調整することを__「スケール調整」__という。
・一般的な基準は__「平均0、分散1」__である。この場合、scikit-learnの__preprocessing__モジュールの__scale()__関数を使うと良い。

・コード（平均50分散10の正規乱数をスケール調整）![スクリーンショット 2020-10-29 16.24.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698700/7c891221-4251-8f4b-e922-6f155fff7af3.png)


##Box-Cox変換
・データが__「正規分布」__に近づくように変換するとき、__Box-Cox変換__というものを行う。
・正規分布とは、データの個数が中央に行くほど多く、端に行くほど少なくなる分布のことである。
・Box-cox変換を行うとき、scipyの__stats__モジュールの__boxcox()__関数を使う。

・boxcox関数に対しては変数を二つ渡すが、一つ目には変換後の配列が、二つ目には、変換のときに使われるパラメータ「λ」が格納される。

・コード（自由度3のx^2をBox-Cox変換）![スクリーンショット 2020-10-29 16.26.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698700/dd6b1340-1eb4-9a14-8011-8b5b63084156.png)

・結果![スクリーンショット 2020-10-29 16.26.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698700/4291aee1-314c-7386-274a-dabcec09dcfd.png)

#縦持ちデータと横持ちデータ
##縦持ちデータと横持ちデータとは
・__縦持ちデータ__は、データの情報が増えたときに「縦に」データを増やす（__行の追加__）ような構造であり、__横持ちデータ__は「横に」データを増やす（__列の追加__）ような構造であるものを指す。
・例えば、会計報告として、「購入者」と「購入物」、「値段」のデータがあるとする。データが増えたとき、単純に__3つの列を持つデータに縦方向にデータを追加するのが縦持ちデータ__、__購入物を横方向に展開してそこにデータを追加するのが横持ちデータ__である。
・縦持ちデータは、列を追加する必要がないため、__データ構造の変更やロジックの変更に対応しやすい__というメリットがある。
・一方横持ちデータは確認の際に__直感的に理解しやすい__というメリットがある。
・モデルにデータを渡すときには、このどちらかの形式に変換する必要がある。

##縦持ちデータを横持ちデータに変換
・pandasの__pivot(index,columns,values)__関数を使う。
・各引数について、「index」は同じ要素を一行にまとめる元のDataFrameの列、「columns」は新しい列にするための元の列、「values」は列の値にする元の列を渡す。

・先ほどの例で言えば、「購入者」は同じ人が多く続くので「index」に、「購入物」は横方向に展開させたいので「columns」に、「値段」は列の値になるので「values」に格納する。
・さらに、変換後のdfのインデックスはこのとき渡したindexになっているので、これを普通のインデックス番号に戻したい時は、pivot()に「__.reset_index()__」をつける。また、この時、列のname属性が残ってしまうので、変換後のdfのcolumnsに__.columns.set_names(None)__をつける。

```python
df = pd.DataFrame({
      '購入者': ['佐藤','田中','加藤','田中','加藤'],
      '購入物': ['ペン','紙','飲料','ハサミ','のり']
      '値段': [500, 250, 250, 600, 200]
})
##縦持ちデータを横持ちデータに変換
pivoted_df = df.pivot(index='購入者',columns='購入物',values='値段').reset_index()
pivoted_df.columns = pivoted_df.columns.set_names(None)
'''
	購入者	ペン	紙	飲料	ハサミ	のり
0	佐藤	500				
1	田中		250		600	
2	加藤			250		200
'''
```

##横持ちデータを縦持ちデータに変換
・pandasの__melt(frame,id_vars,value_vars,var_name,value_name)__で変換できる。
・各引数について
　・frame:変換元のdf
　・id_vars:キーになる列(配列で渡す)
　・value_vars:変換後の値になる列(配列で渡す)
　・var_name:横方向に展開されている列をまとめる列の名前
　・value_name:変換後の値になる列の名前

・前項で作った横持ちデータなら、以下のようになる。
__pd.melt(pivoted_df,id_vars=['購入者'],value_vars=['ペン','紙','飲料','ハサミ','のり'],var_name='購入物',value_name='値段')__

#まとめ
・連続値をカテゴリ化したい時は、pandasの__cut(x,bins=[],labels=[],right=)__で行う。
・カテゴリ化したデータを、そのカテゴリに属していれば「1」、属していなければ「0」というように変換することを「one-hotエンコーディング」と言い、__pd.get_dummies()__で行う。
・数値型データの値を相対的に揃えることを「スケール調整」といい、__preprocessing.scale()__
で行う。
・データを正規分布に近づけるように変換することを「Box-Cox変換」と言い、__stats.boxcox()__で行う。
・縦持ちデータから横持ちデータに変換する時は__pivot(index,columns,values)__を使い、その逆を行う時は__melt(frame,id_vars,value_vars,var_name,value_name)__を使う。

今回は以上です。最後まで読んでいただき、ありがとうございました。