Python学習記録_6日目.統計学入門・matplotlib・決定木

# 元記事
[Python学習記録_プログラミングガチ初心者がKaggle参加を目指す日記](https://qiita.com/noway6064/items/14ab494dc65d3040f733)
6日目です。

GW挟んで色々記憶が薄れている部分もあるので前回までにやったことを読み直してみたんですが
やっぱり記事という形で残しておくと振り返りがしやすくて良い感じだなぁと思いました。

今回は決定木について学べるということで楽しみです。
ではさっそく行きましょう。

### 統計学入門（平均、分散、標準偏差) 15m …
##### 統計学とは
>統計学というのはどのような学問なのでしょうか。
統計学は、ある1つのデータ群に対し、データの性質を調べたり、未来のデータを推測するための学問です。
統計学には大きく3種類に分類することができます。
>
>1.記述統計学
2.推測統計学
3.ベイズ統計学
>
>この章では、統計学における「平均」「分散」「標準偏差」「偏差値」に関しての最低限の知識を学びます。またPythonのnumpyライブラリを使用します。

numpyはデータの前処理で使いそうな気配がしていたライブラリですね。
4日目の内容は見直しておいた方が良さそうです。

##### 平均値
>まずは、平均値について学んでいきましょう。
平均値を求めるには、複数の数値(N)に対して、1つ1つ全て足しあわせた後、全体の数値の個数で割ることで求められます。
文字式で表す時、平均はμ(ミュー)と表したりします。
平均値の長所は計算が簡単だったり、意味がわかりやすいなどが挙げられます。
一方で平均値の短所は外れ値に影響を受ける点です。
外れ値は、他の値から大きく外れた値のことです。
つまり極端に大きな数値や小さな数値が含まれていると、平均値がその値へ引っ張られてしまう点です。

平均値の短所についてはいやって程苦しめられた記憶がありますね…
ABテストやって雑に平均値だけで比較をして良い結果だったので、
実運用に組み込んだら次のタームで検証したらクソ負け、みたいな苦い経験があります…

```python
scores = [90,80,40,60,90]
s = sum(scores)
N = len(scores)
mean = s / N
print("平均:", mean) #72
```
単純に総和をlenの要素の数で割ってる形ですね。



##### 偏差
>偏差とは、個々の数値と平均値との差のことです。
個々のデータが平均値から偏っている程度を表しています。
偏差は、個々の数値 - データの平均値で求められます。
```python
import numpy as np
scores = [90,80,40,60,90]


mean = np.mean(scores)

diff = []
for n in scores:
  diff.append(n - mean)

print("偏差", diff)
 # 偏差 [18.0, 8.0, -32.0, -12.0, 18.0]
```
こちらが例文。
やってることはシンプルですがこれだと元のスコアがわからないので1列目にスコア、2列目に偏差のデータ作ってみたいなと思ったので書いてみます。
習ったものを積極的に使ってみるスタイル。

```python
import pandas as pd
scores={"score":[90,80,40,60,90]}
score_frame=pd.DataFrame(scores)
print(score_frame)

a=sum(score_frame.score)
b=len(score_frame.score)
Ave=a/b
print(Ave)


for n in score_frame:
  print(list(score_frame.score-Ave))


score_frame["dev"]=[18,8,-32,-12,18]
print(score_frame)
```
```
   score
0     90
1     80
2     40
3     60
4     90
72.0
[18.0, 8.0, -32.0, -12.0, 18.0]
   score  dev
0     90   18
1     80    8
2     40  -32
3     60  -12
4     90   18
```
DataFrame、大文字もちゃんと打たないと機能しないんですね…
そしてかなーり不格好な感じになってしまいました。
スコアから平均を引いたもの、を自動で2列目に入れてほしいんですけどエラーになって解消法もわからずでした
なのでとりあえず出力したものをベタ打ちで要素追加してみましたがこの上なく原始的な方法ですよねコレ。
うーむ…これappendとか使えばできそうな気もするんだけどなぁ…
最初に書いたコードはこんな感じ
```python
import pandas as pd
scores={"score":[90,80,40,60,90]}
score_frame=pd.DataFrame(scores)
print(score_frame)

a=sum(score_frame.score)
b=len(score_frame.score)
Ave=a/b
print(Ave)

for n in score_frame:
  score_frame["dev"]=[score_frame.score-Ave]

print(score_frame)
```
調べてみたら要素数が違うと出るエラーとか。
ちょっと悔しいですが本筋ではないのでいったん先に進めます。後で絶対追記します。

追記:
コメントにてアドバイスいただきました。
以下のコードでいけるようです。
```python
import pandas as pd
scores={"score":[90,80,40,60,90]}
score_frame=pd.DataFrame(scores)
print(score_frame)

a=sum(score_frame.score)
b=len(score_frame.score)
Ave=a/b
print(Ave)

score_frame["dev"]=score_frame.score-Ave

print(score_frame)
```
イメージとして
①新しいカラムを作る(値は全てnull)
②スコア-平均の値に更新
③これを要素の数だけ繰り返す
という感じだったのですが、
単純に新しいカラムを作る際にどのような値を入れるかを指定する形で作成できる、という理解です。

もともと僕が書いたものだとdevっていうカラムを作る動作が繰り返されるからエラーになったのかな…？
まだまだ甘いですね…


##### 分散(variance)
>分散とは数値データのばらつき具合を表すための指標です。
分散は、偏差（それぞれの数値と平均値の差）を二乗し、平均を取ることで求められます。
分散は文字式$s^2$で表します。

```python
import numpy as np
scores = [90,80,40,60,90]

v = np.var(scores)
print("分散", v)
 # 分散 376.0
```
どれくらい数字にムラがあるか、を表すのが分散ですね。
これが大きすぎるとよくないし、逆に0に近いとそもそもデータとして意味がないしって感じの指標だったと記憶してます。

##### 標準偏差
>標準偏差とは、分散の平方根をとることによって計算される値で、データの散らばりの度合いを示します。
データの散らばりが大きいと標準偏差も大きくなります。
また、散らばりが小さいと標準偏差は 0 に近づきます。
文字式では、分散の文字式から２乗を取って、「$s$」や「$σ$」などと表されます。
標準偏差を求めるには4つのステップを順に踏みます。
>1. 平均値を求める
>2. 偏差（数値 － 平均値）を求める
>3. 分散（偏差の二乗平均）を求める
>4. 分散の正の平方根を求める

```python
import numpy as np
scores = [90,80,40,60,90]

std = np.std(scores)
print("標準偏差", std)
 #標準偏差 19.390719429665317
```
そんな面倒な4つのステップをまとめてすっとばして標準偏差を出してくれるnumpyさんはすごいですね。
こちらの標準偏差もデータにどれくらいムラがあるのかを表す指標みたいなイメージです。

##### 偏差値
>偏差値とは、自身の学力や順位を把握するために用いられる指標になります。
テストの点数同士を比較した場合ですと、問題の難易度によってバラツキが出てしまいます。
そのため、偏差値を用いることが一般的です。
偏差値は、テストを受験した人数やそのテストの難易度に左右されることなく、多くの集団の中から自身の位置を把握することが出来ます。
偏差値は個人の得点から平均点を引き、その後標準偏差で割り、10で掛け合わせ、最後に50を足すことで求める事が出来ます。統計学的に説明すると平均値50、標準偏差10の分布に従うように変形した値が偏差値であると言えます。数式で表すと以下の通りです。
```math
偏差値=\frac{点数‐平均点}{標準偏差}×10+50 \
```
学生時代にさんざん苦しめられた偏差値の正体がこちら。
コードで書くと以下の通り
```python
import numpy as np
scores = [90,80,40,60,90]

def mean(scores):
    s = sum(scores)
    N = len(scores)
    mean = s // N
    return mean

mean = mean(scores)

diff = []
for n in scores:
  diff.append(n-mean)

print("偏差", diff)

v = np.var(scores)
print("分散", v)

std = np.std(scores)
print("標準偏差", std)

def deviation_value(score, mean, std):
    return int((score - mean) / std * 10 + 50)

for score in scores:
    dv = deviation_value(score, mean, std)
    print("偏差値: ", dv)
```
偏差値を出すメソッドはないんですね、そんなに汎用性高くないのかな…

追記:
こちらに関しても別のライブラリに実装されているみたいです。
```python
>>> import scipy.stats
>>> scores = [90,80,40,60,90]
>>> scipy.stats.zscore(scores) * 10 + 50
array([59.28279122, 54.12568499, 33.49726006, 43.81147252, 59.28279122])
```
scipy.stats…また知らない名前がでてきました…
実際に使えるようになるにはどんなライブラリがあるのか勉強しないとなあ…

##### 演習問題
>1. データセット[3,5,6,7,9,12]をnumpyを使わず、平均値を求めてください。
>2. 1で扱ったデータセットの標準偏差をmathライブラリのsqrt(平方根のメソッド)のみで求めてください。
```python
 #1
data=[3,5,6,7,9,12]
a=sum(data)
b=len(data)
ave=a/b
print(ave)

 #2
import math as mt
diff=[]
for n in data:
    diff.append(n-ave)
c=[]
for n in diff:
    c.append(n**2)
d=sum(c)
e=len(c)
f=d/e
g=mt.sqrt(f)
print(g)

 #7.0
 #2.886751345948129
```


### Matplotlib入門 35m …
##### Matplotlibとは
>Matplotlib はPythonのグラフ描画のためのライブラリです。
Matplotlibを使うことで、グラフの描画やデータの可視化が簡単に行えます。
折れ線グラフ、ヒストグラムや散布図などグラフ描画する事が可能です。

##### 折れ線グラフ
>折れ線グラフは、数量の大きさを表す位置を線で結んだグラフです。
時間とともに変化する数量を示すときに利用します。
pyplotモジュールのplot()で描画することが出来ます。
```python
import matplotlib.pyplot as plt
data = [2, 4, 6, 3, 5, 8, 4, 5]
plt.plot(data)
plt.show()

plt.plot([1,2,3,4],[1,5,10,15],"r--") 
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252820/9334b82d-fe37-3d78-58fc-cd31d13a4af5.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252820/ec25c490-c474-9ee5-55ff-45a6cd4f563a.png)
第1引数にX軸第2引数にY軸の値を指定して使う感じで、
引数？オプション？には赤ならr、青ならb、緑ならgで点線にしたい時は‐‐をつけることで線の書式を変えられる。
上の例の1つ目はY軸の値を設定していないリストを使ってるけどこの場合は単純にインデックスがX軸になって値がそのままY軸の値になるのかな？


##### 棒グラフ

```python
import matplotlib.pyplot as plt
import numpy as np
a = range(0, 7)
b = [55,21,61,98,85,52,99]
plt.bar(a, b)
plt.show()

m = ("1", "2", "3", "4", "5", "6", "7","8","9","10","11","12")
y_pos = np.arange(len(m)) 

sales = [10 ,18,32,54,65,96,120,140,145,162,188,202]
plt.bar(x=y_pos, height=sales, alpha=0.5)
plt.ylabel("Usage")
plt.title("Sales Trends") 
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252820/0c2079ab-3313-43d1-14cf-4bccbdeea1fb.png)

基本的にほとんど折れ線と同じですね、第1引数がX軸で第2引数がY軸。
第3引数のalphaは透明度を表しているとのことです。
横棒で表したい時はbarをbarhに変えるだけ。

##### 散布図
```python
import matplotlib.pyplot as plt
x = [10, 51, 44, 23, 55, 95]
y = [5, 125, 2, 55, 19, 55]
plt.scatter(x,y)
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252820/739ec422-ac20-d7f9-a1ae-3b1979bc17ee.png)
散布図はscatter。


##### 円グラフ
```python
import matplotlib.pyplot as plt
labels = ["A", "B", "C", "D", "E"]
data = [54, 32, 18, 44, 29]
ex = [0.1, 0, 0, 0, 0]
plt.pie(data, explode=ex, labels=labels, autopct="%1.1f%%", counterclock=False)
plt.show()

label_lists = ["Python", "Ruby", "Java", "C++", "PHP"]
size_lists  = [40, 12, 24, 16, 9]
color_lists =["navy", "yellow", "blue", "gold", "tomato"]
plt.pie(x=size_lists, labels=label_lists, colors=color_lists)
plt.axis("equal") 
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252820/7e53c6d3-bdb6-5420-2a10-f3008819ab87.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252820/f05e7620-fb6b-a303-4684-4f0a298918ab.png)

円グラフはpieで表す。
pieは引数に色んな設定ができて
explode…1つ目の要素を目立たせる
autopct…比率の小数点以下何桁まで表示するか指定
counterclock…False指定しないと要素の順番が反時計回りになる
と色々指定できるようです。

##### ヒストグラム
```python
import matplotlib.pyplot as plt
import numpy as np

x=np.random.normal(10, 10, 1000)

plt.hist(x)
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252820/265c08e6-7edc-07ac-4ec0-aedcae2584ed.png)

ヒストグラムはhist。まんまですね。

##### 箱ひげ図
```python
import matplotlib.pyplot as plt
import numpy as np

math = [162, 168, 172, 181, 176, 168, 173, 175, 162, 169]
x = np.array(math)
plt.title('height')
plt.grid() # 横線ラインを入れることができます。

plt.boxplot(x)
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252820/81b9957e-fc9b-4f6b-05d7-8fc64629ea23.png)
箱ひげ図はboxplot。
あんまり実務で使ったことないんですけどこれも便利そうではあります

### 決定木 25m …
##### 決定木とは
>回帰などの線形モデルやSVMでは、そのモデルがどのように分類されたかわからない分類過程がブラックボックスの手法でした。
しかし、ときには分類過程を知ることが必要になります。
例えば、メールがスパムメールかそうでないかを分類する例を考えると
今までの学習手法だとどの単語によってスパムメールと判断されたかがわかりません。
なので、スパムメールに含まれる単語を見つけるには分類過程を知る必要があります。
今回学習する決定木の一番の特徴は、分類過程が明瞭ということです。
決定木はとても単純な手法ですが、分類と回帰で用いる事が出来、非常に効果的な教師あり学習手法です。
決定木のデメリットですが、線形分離可能な問題が不得意です。
不得意な理由として、決定木では決定境界を直線ではなく、領域の分割を繰り返し決定境界を作るためです。
ですので、線形分離可能な問題に適用するより、非線形分離可能な問題に適用する方が良いです。
決定木の簡単な例を示します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252820/5453f027-b470-3252-c59b-2b0b883d4dbe.png)
>最初に、そのメールの文書に「無料」という単語が含まれているかを問います。
もし、含まれているならそのメールはスパムと分類されます。
含まれていないなら、さらにそのメールは連絡先に入っている人から来たかという質問をします。
それが、Yesならばスパムメールではない、Noならスパムメールと分類されます。
このように決定木は人が理解するのに容易で、何がその分類を決定づけたかが分かります。

とても単純な手法と書かれてるだけあってどんなことしてるかの理解ができました。
二値分類で使いやすい、というようなことを以前言われたことがあった気がしますがそれが非線形分離可能な～～のくだりですかね。
シンプルで分類の過程が人にも理解しやすいけど使えないものもあるモデル、という認識でいこうと思います。

##### 決定木分析
>scikit-learnのアイリスのデータセットを利用します。
```python
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
import matplotlib.pyplot as plt
from sklearn.tree import plot_tree

iris = load_iris()
x = iris.data[:, 2:] 
y = iris.target
model = DecisionTreeClassifier().fit(x,y)

 #ここまでが学習

fig = plt.figure(figsize=(15, 8))
ax = fig.add_subplot()
plot_tree(model, feature_names=iris.feature_names[2:] , ax=ax, filled=True, class_names=['setosa', 'versicolor', 'virginica']);
 #ここまでが描写
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252820/96d35cd4-c63e-db36-72c9-d14201f417fd.png)
…なるほど、よくわからん……
アイリスのデータセットはさらっと見たことあるんですが…
x=　のところでどの変数を使うのか宣言してる、みたいな感じですかね…？
実装のパートがあるのでそこで細かい解説はあると信じて、
とりあえず今はsklearnにDecisionTreeClassifierというメソッドがあってそれを使うんだな、くらいの理解で進めます。

# 6日目の感想
標準偏差をnumpy使ってサクッと計算する方法と決定木についてのお勉強がメインでした。
matplotlibも使えるようになれば非常に便利そうな…
現状だと可視化する時にはSQLでデータ落として、エクセルに貼って、エクセル上で作業してみたいな手間を踏んでるので
これをcsvファイル落としてくるだけでできるようになればかなり時短につながりそうです。
徐々に実務に取り入れていけそうなイメージがわいてきたのでモチベーション回復。
明日もがんばれ