HOWTO · Seaborn

Seaborn BoxPlot

このチュートリアルでは、Python で seaborn モジュールを使用して箱ひげ図を作成する方法を示します

このページの内容

seaborn.boxplot()を使うと、数値データの分布を要約したり、カテゴリ間で値を比較したりできます。Seabornが各軸にどの変数を割り当てるか分かるよう、データ列を明示的に指定します。箱ひげ図は中央値と四分位範囲(IQR)を示します。既定のひげの規則では、ひげは箱から1.5 IQR以内にある最も遠い観測値まで伸び、それより離れた観測値は個別の点として表示されます。

箱ひげ図の各部に名前を付けた注釈付き図:箱は下位四分位数から上位四分位数までの範囲で中央値の線を内包し、ひげは下限と上限の極値まで伸び、ひげの外側の値は単一の外れ値として表示されます。

この記事のすべての例は同じ要素を使います。箱はデータの中央半分を覆い、その中の線が中央値、ひげは選択した規則に基づく範囲を示し、ひげの外側の点は個別に描画されます。

1つの分布を箱ひげ図で表示する

測定値をDataFrameの列に格納し、その列をyに割り当てます。こうするとデータの役割が明確になり、グラフの軸にも分かりやすいラベルが付きます。次の例では値を固定しているため、実行するたびに同じ入力データが使われます。

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

data = pd.DataFrame({"Score": [12, 14, 15, 16, 18, 19, 21, 24]})
ax = sns.boxplot(data=data, y="Score")
ax.set_title("Score distribution")
plt.show()

8 件の Score 値の Seaborn 箱ひげ図:内部に中央値の線を持つ 1 本の縦の箱で、ひげは最低と最高のスコアまで伸びています。

箱の中の線は中央値を示します。箱は第1四分位数から第3四分位数までを表すため、その高さには観測値の中央半分が含まれます。箱が長いほど、データの中央部分のばらつきが大きいことを示します。ひげは選択した規則に基づく範囲を示し、必ずしも最小値や最大値まで届くとは限りません。

カテゴリごとに分布を比較する

カテゴリをxに、数値の測定値をyに割り当てると、各カテゴリに個別の箱が表示されます。このデータセットでは、各箱が1つのQuantityの値におけるPriceを要約します。hue="Product"を追加すると、カテゴリが製品グループに分けられ、同じ数量で製品間を比較しやすくなります。

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

data = pd.DataFrame(
    {
        "Quantity": [5, 6, 7, 8, 5, 6, 7, 8, 5, 6, 7, 8, 5, 6, 7, 8],
        "Price": [9, 10, 15, 16, 13, 14, 15, 18, 11, 12, 14, 15, 16, 17, 18, 19],
        "Product": ["A", "A", "A", "A", "B", "B", "B", "B"] * 2,
    }
)
ax = sns.boxplot(data=data, x="Quantity", y="Price", hue="Product")
ax.set_title("Price by quantity and product")
plt.show()

Quantity レベル 5 から 8 について Price を Product A と Product B に分けた Seaborn 箱ひげ図。すべての数量で Product B の箱が Product A の箱より高い位置にあります。

追加のグループ分けが比較したい内容に役立つ場合にhueを使います。グループが多いと箱が混み合うことがあります。分布を読み取りやすくするには、カテゴリを絞るか、プロットを分けて表示してください。

ストリッププロットで個々の観測値を表示する

箱ひげ図はデータを要約統計量にまとめます。個々の観測値も読者に見せたい場合は、seaborn.stripplot()を重ねます。両方のレイヤーで同じ軸とデータの割り当てを使い、値の近い点が完全に重ならないよう、水平方向に少しジッターを加えます。

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

data = pd.DataFrame({"Score": [12, 14, 15, 16, 18, 19, 21, 24]})
ax = sns.boxplot(data=data, y="Score", color="lightgray")
sns.stripplot(data=data, y="Score", color="darkred", jitter=0.15, ax=ax)
ax.set_title("Scores with individual observations")
plt.show()

Score の箱ひげ図を薄いグレーで描き、濃い赤のストリッププロットを重ねた図。8 件の観測値それぞれが、箱の中または横にジッター付きの点として表示されます。

箱は引き続き分布の要約を示し、各マーカーはデータの1行を表します。データセットが大きいと点が重なることがあるため、点が密集した領域を読みやすくするには、マーカーのサイズや透明度を調整します。

箱ひげ図をカスタマイズする

関連する値の範囲に注目するにはMatplotlibの軸範囲設定メソッドを使い、箱やひげの境界線の太さはlinewidthで、カテゴリの色はpaletteで調整できます。orientパラメーターはプロットの向きを指定します。たとえば、データの割り当てが適合する場合、orient="h"で横向きのプロットを指定できます。軸範囲を変えると表示される領域だけが変わります。重要な観測値が隠れないよう、範囲と目盛りを分かりやすく設定してください。

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

data = pd.DataFrame(
    {
        "Quantity": [5, 6, 7, 8, 5, 6, 7, 8, 5, 6, 7, 8, 5, 6, 7, 8],
        "Price": [9, 10, 15, 16, 13, 14, 15, 18, 11, 12, 14, 15, 16, 17, 18, 19],
        "Product": ["A", "A", "A", "A", "B", "B", "B", "B"] * 2,
    }
)
ax = sns.boxplot(
    data=data,
    x="Quantity",
    y="Price",
    hue="Product",
    linewidth=2.5,
    palette="Set2",
)
ax.set_ylim(8, 20)
ax.set_title("Customized price comparison")
plt.show()

カスタマイズした価格比較:箱とひげの境界線を太くし、Product A と Product B に Set2 パレットの色を使い、y 軸を 8 から 20 の範囲に制限しています。

パレットの色はhueで分けたグループに対応します。横向きの箱ひげ図にするには、カテゴリと測定値の割り当てを入れ替えてorient="h"を設定します。その場合、軸範囲にはset_ylim()ではなくset_xlim()を使います。

catplot()で箱ひげ図を作成する

図全体を扱うインターフェイスには、kind="box"を指定したcatplot()を使います。同じデータの明示的な割り当てを使えます。また、別の変数で比較を分けたい場合は、colやrowでパネルを作成できます。単一のプロットには軸単位のseaborn.boxplot()を使い、複数のパネルに分ける図にはcatplot()が便利です。

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

data = pd.DataFrame(
    {
        "Quantity": [5, 6, 7, 8, 5, 6, 7, 8, 5, 6, 7, 8, 5, 6, 7, 8],
        "Price": [9, 10, 15, 16, 13, 14, 15, 18, 11, 12, 14, 15, 16, 17, 18, 19],
    }
)
grid = sns.catplot(data=data, x="Quantity", y="Price", kind="box")
grid.set_axis_labels("Quantity", "Price")
grid.fig.suptitle("Price by quantity")
plt.show()

kind を box に設定した図レベルの catplot:Quantity レベル 5 から 8 ごとに Price の箱を 1 つずつ単一パネルに描き、上部に図のタイトルがあります。

catplot() は図全体を構築するため、単一の軸ではなくグリッドオブジェクトを返します。例のように、ax.set_title() を呼ぶ代わりにグリッドに対してラベルとタイトルを設定してください。

列が見つからないエラーの対処とひげの範囲の設定

すべてのマッピングは DataFrame の列名を指定するため、名前のスペルを間違えると何も描画される前に失敗します。次の呼び出しは、データに含まれない列 Scroe を求めています:

import pandas as pd
import seaborn as sns

data = pd.DataFrame({"Score": [12, 14, 15, 16, 18, 19, 21, 24]})
ax = sns.boxplot(data=data, y="Scroe")

呼び出しは次のエラーで終了します:

ValueError: Could not interpret value `Scroe` for `y`. An entry with this name does not appear in `data`.

列と正確に一致するよう名前を修正すれば(ここでは y="Score")、同じ呼び出しでプロットが描画されます。正しいスペルが分からない場合は data.columns を出力し、そこから名前をコピーしてください。このチェックは x、y、hue のいずれにも同様に適用されます。

ひげは読者が出会うもう 1 つの境界です。スコアに遠く離れた値 60 を追加すると、四分位数はほとんど動きません(第 1 四分位数 15、中央値 18、第 3 四分位数 21)。デフォルトの whis=1.5 では上のひげは 24 で止まり、60 はその外側に孤立した点として描画されます。whis をパーセンタイルのペアとして渡すと、ひげは代わりにデータのそのパーセンタイルまで伸びます:

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

scores = pd.DataFrame({"Score": [12, 14, 15, 16, 18, 19, 21, 24, 60]})
ax = sns.boxplot(data=scores, y="Score", whis=(0, 100))
plt.show()

whis=(0, 100) とすると、ひげは最小値 12 と最大値 60 まで届き、外側に残る点はなくなります。全範囲そのものが伝えたい内容でない限り、デフォルトを優先してください。伸ばしたひげは、極端な値がどれほど異例かを隠してしまいます。また、空の列ではエラーは発生しません。Seaborn は空の軸を返すため、データがパイプライン由来の場合は描画前に行数を確認してください。