matplotlib で ヒストグラム

matplotlibで模擬試験の5教科・合計点の記録から、ヒストグラムを作りました。
・matplotib
・ヒストグラム(plt.hist)
・for文でグラフ出力
・patchesでヒストグラムの棒に対する色分け
      

## 模擬試験のヒストグラム  
・対象は国語、数学、英語、社会、理科、合計点。

・国語、数学、英語、社会、理科は各100点満点

・csv https://drive.google.com/file/d/1EzctLYN5-UvkmkOgZ7usPgtsQn7bdq5y/view?usp=sharing

・合計点は500点満点
![20200916000204.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698282/576d3717-f7b5-b1f5-0b81-4920e4f2d7db.png)

------

ライブラリを積み込んで

```python

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
```

  
データフレームを作る。1~6列目にnameを与える。
（csvはpythonの.ipynbと同じディレクトリにあれば「〜〜〜.csv」でいけます。）

```python
df = pd.read_csv("honmachi.csv", names=['国語','数学','英語','社会','理科','合計'])
```  
  
格納状況を確認。（これで先頭の行が見れる。）

```python
df.head()
```
  
![20200916000914.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698282/a8979b56-5166-b601-2137-4b62bb11c9b5.png)

  
今回は分析するわけではないが、describe()で全体像も。

```python
df.describe()
```

![20200916001311.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698282/3a68ad4c-1dfb-f732-50af-fb222b8d10d9.png)

  
**matplotlib**に、まずはデフォルトで df['国語'] のヒストグラムを書いてもらいます。

```python
plt.hist(df['国語'])
plt.title('国語')
plt.xlabel('得点')
plt.ylabel('人数')
plt.show()
```

![20200916002301.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698282/748a1550-0b86-004b-4e8d-9a1835767c5c.png)
  

デフォルトだと微妙。 
テストの点数という性質上
・0~100点の範囲 **range=(0, 100)**
・棒が10本 **bins=10**
くらいが見やすいか。  
  
というわけで、rangeとbinsを**matplotlib**のhistの()の中に注文する。

```python
# hist()内に追加
plt.hist(df['国語'], range=(0,100), bins=10,)
plt.title('国語')
plt.xlabel('得点')
plt.ylabel('人数')
plt.show()
```

![20200916003812.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698282/1d63c73e-75e6-aafa-3142-b5298e8e6ded.png)


次は軸。 
・x軸
0~100なので **plt.xlim(0, 100)** 
・y軸
科目によって高さが揺らぐと比較し辛い。
今回は15人分なので、とりあえず8人で **plt.ylim(0,8)**とします。
ここで指定しておけば、8人オーバーした際も、ここで調節可能。

```python
plt.hist(df['国語'], range=(0,100), bins=10,)
# ここに追加
plt.xlim(0,100)
plt.ylim(0,8)
plt.title('国語')
plt.xlabel('得点')
plt.ylabel('人数')
plt.show()
```
![20200916005618.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698282/363b37b0-eea0-801c-8fd2-1660c783c08c.png)

原型はこんな感じにします。

------
少し細かいデザインを調整します。
**1.目盛りを読むグリッド線が欲しい**
**2.点数が半分以下の色を変えてみる**


------
1.人数に横線を引く。
**plt.grid(True)**
 
```python
plt.hist(df['国語'], range=(0,100), bins=10)
plt.xlim(0,100)
plt.ylim(0,8)
# 追加
plt.grid(True)
plt.title('国語')
plt.xlabel('得点')
plt.ylabel('人数')
plt.show()
```
![20200916011819.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698282/f500ff0e-868d-5571-390a-9c40088e6bd0.png)

  
**2.点数が半分以下の色を変える。**
手こずりました。  
plt.hist()の中で
if（49点以下）: 
　　range=(0,50), bins=5
else（50点以上）:
　　range=(51,100), bins=5
で色分けを考えるも、ハードそう。  
  
データフレームの各科目に対して毎回50点以下と50点以上に分割し直すようなこともできるでしょうか。
<br />

ただ、今回は性質上、綺麗に固定の棒が生えるので、**棒に対して色分け**できないか。
つまり、**棒に対して、1~5本目を赤くしたい。**
ここでは、histにある戻り値を使いました。  
>  
[参考](https://www.tuyano.com/index3?id=5689792285114368&page=3)  
n, bins, patches = hist(○○)  
n ：Y軸の値のデータ  
bins ：X軸の値のデータ  
patches ：patchのリスト  
（patch = **ヒストグラムの各棒のオブジェクト**）  
>

この**patch**の１〜５本目に対して色分けがしたい。  

```python
# 1本目のpatch（棒）を対象にして、facecolor（棒の色）にredをset（塗る）
patches[0].set_facecolor('red')
```
これを１本目から5本目まで繰り返せばいいのでfor文を使いました。

```python
for i in range(0, 5):
    patches[i].set_facecolor('red')
```
これで色分けの準備が完了したので、このfor文を追記します。

```python
plt.hist(df['国語'], range=(0,100), bins=10)
plt.xlim(0,100)
plt.ylim(0,8)
plt.grid(True)
plt.title('国語')
plt.xlabel('得点')
plt.ylabel('人数')
# 追記
for i in range(0, 5):
    patches[i].set_facecolor('red')
plt.show()
```
![20200916020839.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698282/7a05e4d0-9704-e227-412c-7b76d992ac89.png)


patches が定義されていないと出てしまいます。
どこかに**pathches**を置く必要があるのか？  
先ほどのを拝借して
**n, bins, patches = hist(　)**　を付けたら上手くいきました。

```python
# ここに追加
n, bins, patches = plt.hist(df['国語'], range=(0,100), bins=10)
plt.xlim(0,100)
plt.ylim(0,8)
plt.grid(True)
plt.title('国語')
plt.xlabel('得点')
plt.ylabel('人数')
for i in range(0, 5):
    patches[i].set_facecolor('red')
plt.show()
```

![20200916021239.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698282/201dfd6a-d132-6ce7-237f-f9af5a33b38b.png)


完成。  
  
血のような赤というのも縁起が悪いので、透明度(alpha)を調整します。**alpha=0.5**
これはhist()の中に追加で注文しておきます。

```python
# hist()の中にalphaも追加
n, bins, patches = plt.hist(df['国語'], range=(0,100), bins=10, alpha=0.5)
plt.xlim(0,100)
plt.ylim(0,8)
plt.grid(True)
plt.title('国語')
plt.xlabel('得点')
plt.ylabel('人数')
for i in range(0, 5):
    patches[i].set_facecolor('red')
plt.show()
```
![20200916022138.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698282/f329afba-de62-e9d3-20bc-1aac6313969a.png)

  
------

あとはfor文で一気に回します。  

```python
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

df = pd.read_csv("honmachi.csv", names=['国語','数学','英語','社会','理科','合計'])
# subject という変数を設定して、１科目ずつ処理していきます。
for subject in ['国語','数学','英語','社会','理科']:
# df[ ]の中身はsubjectに合わせて変わるようにしています。
    n, bins, patches = plt.hist(df[subject], range=(0,100), bins=10, alpha=0.5)
    plt.xlim(0,100)
    plt.ylim(0,8)
    plt.grid(True)
#title( ) の中身もsubjectになっているとタイトルラベルも自動で変わります。
    plt.title(subject)
    plt.xlabel('得点')
    plt.ylabel('人数')
    for i in range(0, 5):
        patches[i].set_facecolor('red')
    plt.show()
```

![20200916024212.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698282/39e4e97f-93db-0416-af3b-5e4018cbd8f1.png)


これで５枚一気に出てこれました。

  
これで残りは、合計得点。500点満点にするだけです。
データフレームの合計を拾って
・**range=(0,500)**
・**plt.xlim(0,500)**
に変更して終わりです。  

最後に、注釈なしで今回の要件で使ったコードをまとめて置いておきます。  

```python
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

df = pd.read_csv("honmachi.csv", names=['国語','数学','英語','社会','理科','合計'])

for subject in ['国語','数学','英語','社会','理科']:
    n, bins, patches = plt.hist(df[subject], range=(0,100), bins=10, alpha=0.5)
    plt.xlim(0,100)
    plt.ylim(0,8)
    plt.grid(True)
    plt.title(subject)
    plt.xlabel('得点')
    plt.ylabel('人数')
    for i in range(0, 5):
        patches[i].set_facecolor('red')
    plt.show()

n, bins, patches = plt.hist(df['合計'], range=(0,500), bins=10, alpha=0.5)
plt.xlim(0,500)
plt.ylim(0,8)
plt.grid(True)
plt.title('合計')
plt.xlabel('得点')
plt.ylabel('人数')
for i in range(0, 5):
    patches[i].set_facecolor('red')
plt.show()
```
<br />
pythonでから出力するだけならば、これで問題なさそうですが、現実的に使ってもらうとなると、ユニバーサルなものにするにはネットワークで動く、最終的な実装の仕組みに出来ないとなと思いました。