76278
# 問題

以下のようなDataFrameがあるとします。

index | 都道府県 | 建物 | 価格  
---|---|---|---  
0 | 東京 | 木造 | 100  
1 | 東京 | 鉄骨 | 120  
2 | 東京 | 木造 | 100  
3 | 埼玉 | 鉄骨 | 80  
4 | 埼玉 | 木造 | 80  
5 | 埼玉 |  鉄骨 | 90  
6 | 東京 | 鉄骨 | 90  
7 | 東京 | 鉄骨 | 130  
8 | 埼玉 | 木造 | 100  
9 | 埼玉 | 木造 | 60  
  
このDataFrameを都道府県ごとにxを建物の種類yを価格にして箱ひげ図を描きたいです。  
しかし、sns.boxplotを利用してsubplotを利用する方法が調べてもいまいちわかりませんでした。

どのようにすればできるでしょうか？

最終的にはこの例の場合、東京で木造と鉄骨の2つの箱ひげ図が1つのグラフに、埼玉で木造と鉄骨の2つの箱ひげ図が1つのグラフに描かれた1×2のグラフが作成したいです。

以下のコードで上記のDataFrameは作成可能です。

```

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

私は以下のように行っていましたが、途中であきらめてしまいました。  
もっと良い方法がありそうです。

```

    pref_list = df["都道府県名"].tolist()
    pref_list = set(pref_list)
    
    structure_list = df["建物"].tolist()
    structure_list = set(structure_list)
    
    fig1, ax1 = plt.subplots(7, 7, figsize=(50, 50))
    j = 0
    for i, pref in enumerate(pref_list):
        for s in structure_list:
            if j == 7:
                j = 0
            ax = i // 7
            ax1[ax][j].set_title(pref)
            ax1[ax][j].boxplot(df[(df["都道府県名"]==pref) & (df["建物"] == s)]["取引価格（総額）_log"])
            ax1[ax][j].set_xlim(0, 3)
            ax1[ax][j].set_ylim(2, 10)
        
        j = j + 1
    plt.show()
    
```

これでは、`seaborn`が使えず困っています。subplotが7×7なのは都道府県が47都道府県本来あるので、そのようにしています。  
`このコードは動きません`

わかる方教えていただきたいです。よろしくお願いします。
CC BY-SA 4.0
2021-06-02T11:42:00.653
76272
2021-06-03T06:13:49.613
44467
question
python matplotlib
seabornで箱ひげ図を複数作成して表示したい
`seaborn.boxplot()` 内でデータをフィルタリングして(`data=df[df.都道府県==p]`)、東京と埼玉のデータを別々の `ax`
に振り分けます(`ax=ax[i]`)。

```

    sns.set(font=['IPAPGothic'])
    _, 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()
    
```

[![出力結果](https://i.stack.imgur.com/DA9wL.png)](https://i.stack.imgur.com/DA9wL.png)

### 追記

> subplotが7×7なのは都道府県が47都道府県本来あるので、そのようにしています。

subplot が `7x7` の場合、`ax` が 2 次元の配列(numpy.ndarray)になるので要素(subplot
のインスタンス)へのアクセスの仕方が異なります。具体的には tuple によるアクセスが可能なので `ax[divmod(i, cols)]`
の様にします。

```

    pref_list = df.都道府県.unique()
    
    # Japanese font
    sns.set(font=['IPAPGothic'])
    
    # subplots
    rows, cols = 7, 7
    _, ax = plt.subplots(rows, cols, figsize=(40, 40))
    ylim = (50, 150)
    
    # plot all
    for i, p in enumerate(pref_list):
      sns.boxplot(
            x='建物', y='価格',
            data=df[df.都道府県==p].sort_values('建物'),
            ax=ax[divmod(i, cols)])\
         .set(title=p, xlabel=None, ylim=ylim)
    
    # hide excess graphs
    for i in range(len(pref_list)-rows*cols, 0):
      ax[-1, i].axis('off')
    
    #plt.show()
    plt.savefig('boxplots.png', bbox_inches='tight', pad_inches=0.1)
    
```

[![boxplots](https://i.stack.imgur.com/QcPz5.png)](https://i.stack.imgur.com/QcPz5.png)
CC BY-SA 4.0
2021-06-02T15:03:31.293
76278
2021-06-02T20:04:38.850
2021-06-02T20:04:38.850
76272
answer
以下を参考にしてみました

  * (ja.stackoverflow.com) [pandasでグループごとの箱ひげ図を作る方法](https://ja.stackoverflow.com/questions/72535/)
  * (stackoverflow.com) [pandas-dataframe-groupby-plot](https://stackoverflow.com/questions/41494942/)

(フォント指定や, 余った部分の消し込み・その他がないのでコード量少なそうだけど, 処理加えるとそれなりの大きさになっていきます)  
`grouped.ngroups` を計算か何かに利用するかもしれないので, 先に group化し描画  
複数行の plotでもエラーにならないよう, `flatten()` してます

```

    grouped = df.sort_values('建物').groupby('都道府県')
    _, axes = plt.subplots(7, 7, figsize=(50, 50))
    for key,ax in zip(grouped.groups.keys(), axes.flatten()):
        sns.boxplot(x='建物', y='価格', data=grouped.get_group(key), ax=ax).set_title(key)
    
```
CC BY-SA 4.0
2021-06-03T06:13:49.613
76296
2021-06-03T06:13:49.613
43025
76272
answer