Seabornの箱ひげ図を都道府県ごとに表示したかったので詳しい人に聞いた話。

# はじめに

私はいまデータ分析コンペ[Nishika](https://www.nishika.com/)を利用して勉強しています。
その中で、`Matplotlib`を使った可視化は普段あまり行うことがなかったため、わからないことが多く、少し複雑なグラフになるとどうやって描画したらよいかわからなくなってしまいます。

今回私が行いたいと思っていた可視化は、以下のDataFrameに対して、

![bandicam 2021-06-03 19-52-09-434.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/900d3240-62bc-67f3-2a9c-1241decc1a41.jpeg)

都道府県ごとにx="建物の構造"、y="取引価格"で`箱ひげ図`を描画することです。

`seaborn`をあまり使った経験がなく、`subplot`を使ってどのようにするかがわからず、詳しい方に聞きましたので、そちらについてまとめたいと思います。

# 可視化の手順

## 1. 都道府県名のリストを用意

```
pref_list = df["都道府県名"].tolist()
pref_list = set(pref_list)
```

`to_list`で都道府県名の列の要素をリストに変換して、`set`で重複を除いています。

こうすることで、47都道府県の名前が入ります。

```
print(len(pref_list)) 
# => 47
```

## 2. グラフを描画する準備

```
# 箱ひげ図につかうライブラリの用意
import seaborn as sns

# 47都道府県なので、70×50のグラフに7×7(=49)の枠を用意
fig, ax = plt.subplots(7, 7, figsize=(70, 50), sharey=True)
```

## 3. 箱ひげ図の描画

```
for i, pref in enumerate(pref_list):
    sns.boxplot(
        x='建物の構造', y='取引価格（総額）_log',
        data=df[df.都道府県名==pref].sort_values('建物の構造'),
        ax=ax[divmod(i, 7)])\
     .set(title=pref, xlabel=None, ylim=(2,10))
```

都道府県名のリストの要素1つずつ(pref)を使ってfor分で処理をします。
`sns.boxplot`で箱ひげ図を作ることができます。
xに建物の構造、yに取引価格
dataに都道府県名がprefと同じ行をすべて抽出して、建物の構造でソートしたものを渡します。
渡されたdataの"建物の構造"と"取引価格"のカラムを利用して箱ひげ図を作るように設定しました。

axの引数にどの枠にグラフを描画するかを選択します。
axはax[7][7]の49枠があり、各枠に各県ごとに入れていきたいです。

そこで、`divmod(i,7)`を利用します。
divmodは割り算の商と余りをタプルで返す関数です。
また、axはタプルを利用することが可能です。

たとえば、1つ目の県は
divmod(0, 7) = (0, 0)となりax[0][0]に入ります。
2つ目の県は、divmod(1,7) = (0, 1)となり、ax[0][1]にはいります。
このようにすることで各県をそれぞれの枠に描画することが可能です。

そのあとで`set`を利用することで、それぞれのグラフに`タイトル`と`ylim`でy軸の範囲を決めています。

## 4. 不要な枠を削除する

47都道府県に対して、49個の枠が用意されているため最後の2つを描画しないように設定します。

```
for i in range(len(pref_list)-7*7, 0):
  ax[-1, i].axis('off')
```

最後から、2個目までを非表示にします。

すると以下のようなグラフができました。

```
plt.show()
```

![bandicam 2021-06-03 20-23-26-903.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/4166b8ec-44ab-1e63-efb0-061cecd01940.jpeg)

最後の2つも描画されていません。

![bandicam 2021-06-03 20-31-29-210.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/0bdd17b0-1e12-b366-c31d-fe785317b439.jpeg)


# サンプルコード

最後にこの手順を簡単な例で試せるようにコードを載せます。

```
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import seaborn as sns

# データフレームの準備
pref = ["東京", "東京", "東京", "埼玉", "埼玉", "埼玉", "東京", "東京", "埼玉", "埼玉"]
house = ["木造", "鉄骨", "木造", "鉄骨", "木造", "鉄骨", "鉄骨", "鉄骨", "木造", "木造"]
value = [100, 120, 100, 80, 80, 90, 90, 130, 100, 60]
df = pd.DataFrame({'都道府県':pref, '建物': house, '価格':value})

```


|index| 都道府県 | 建物 |価格 |
|---|---|---|---|
| 0 | 東京 | 木造|100|
|1 | 東京 | 鉄骨 |120|
| 2 | 東京 | 木造 |100|
| 3 | 埼玉 | 鉄骨 |80|
| 4 | 埼玉 | 木造 |80|
| 5 | 埼玉 |　鉄骨 |90|
| 6 | 東京 | 鉄骨 | 90|
|7 | 東京 | 鉄骨 |130|
| 8 | 埼玉 | 木造 |100|
| 9 | 埼玉 | 木造 |60|

このデータフレームに対して、都道府県ごとに、x=建物、y=価格で箱ひげ図を作ります。

```
fig, ax = plt.subplots(1, 2, figsize=(10, 5))
ylim = (50, 150)

df = df.sort_values(['都道府県', '建物'])
for i, p in enumerate(('東京', '埼玉')):
  sns.boxplot(
        x='建物', y='価格', data=df[df.都道府県==p], ax=ax[i])\
     .set(xlabel=p, ylim=ylim)

plt.show()
```

すると以下のような箱ひげ図が描画されます。

![DA9wL.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/236ba95e-22a2-4703-7ba2-fdda1e0b90df.png)



# おわりに

今回はseabornで複数描画する方法について説明しました。
このやり方は[teratail](https://teratail.com/questions/341803)や[stackoverflow](https://ja.stackoverflow.com/questions/76272/seaborn%e3%81%a7%e7%ae%b1%e3%81%b2%e3%81%92%e5%9b%b3%e3%82%92%e8%a4%87%e6%95%b0%e4%bd%9c%e6%88%90%e3%81%97%e3%81%a6%e8%a1%a8%e7%a4%ba%e3%81%97%e3%81%9f%e3%81%84/76296#76296)で回答をいただき、それをいいところ取りしたものになります。
他にもやり方はあり、多くの回答をいただいていますのでぜひとも参考にしてみてください。

## 参考記事

[tratail:seabornで箱ひげ図を複数作成して表示したい](https://teratail.com/questions/341803)

[stackoverflow:seabornで箱ひげ図を複数作成して表示したい](https://ja.stackoverflow.com/questions/76272/seaborn%e3%81%a7%e7%ae%b1%e3%81%b2%e3%81%92%e5%9b%b3%e3%82%92%e8%a4%87%e6%95%b0%e4%bd%9c%e6%88%90%e3%81%97%e3%81%a6%e8%a1%a8%e7%a4%ba%e3%81%97%e3%81%9f%e3%81%84/76296#76296)