代表的な図の使い分け
ここでは、単一の系列の推移だけでは見えない、変数同士の関係とカテゴリ間のばらつきを、散布図・ヒートマップ・箱ひげ図の使い分けで見ていきます。
散布図
散布図とは、2つの数値の関係を点の分布で示す図です。まず、広告費と売上の関係を全体像としてつかみます。
import seaborn as sns
df = xl("A1:E19", headers=True)
sns.scatterplot( # 散布図を描画する
data=df, x="広告費", y="売上",
hue="商品カテゴリ") # (1)
セルには画像が返り、18点が並びます。(1)hue指定により商品カテゴリごとに色分けされ、凡例も自動で付きます。複合機は広告費120~320・売上980~1880と幅広く散らばり、プリンターとシュレッダーはそれぞれ狭い帯にまとまります。
3つのカテゴリはいずれも、おおむね右肩上がりに並んでいるため、今回のデータでは広告費と売上に正の関係があることがひと目でわかります。色分けをせず、全体の傾向を回帰線で確認したいときは、次のようにregplotを使えます。
以降のコードは、前のセルで作ったdfとimportをそのまま引き継ぎます。ブックを開き直した直後は、先に前のセルを実行してください。
sns.regplot(data=df, x="広告費", y="売上")
セルには画像が返り、18点の散布図に回帰直線と、その周囲の陰影(既定の95%信頼区間)が重なって表示されます。広告費と売上の相関係数は約0.98(相関行列は後述)と非常に強く、点は回帰直線の近くに分布しています。ただし、これは18件のデータを3カテゴリまとめて計算した結果です。
regplotはカテゴリ分けをしないため、3つの帯が一体として描かれる点がscatterplotとの違いです。ただしこの0.98は3カテゴリを混ぜた値です。シュレッダーは広告費も売上も低く、複合機は両方高い、といった商品自体の違いも数字に含まれます。
ヒートマップ
ヒートマップとは、マトリクス状に並んだ数値を色の濃淡で示す図です。まず、pivot_tableで商品カテゴリ×月の売上をマトリクスに集計します。
pivot = df.pivot_table(
index="商品カテゴリ", columns="月",
values="売上", sort=False)
pivot
pivotを実行すると集計結果が表として返ります。続くセルでは、直前のセルで作ったpivotをsns.heatmapに渡します。
ax = sns.heatmap(pivot, annot=True, fmt=".0f", # (1)
cmap="Blues")
ax.tick_params(axis="x", labelrotation=45) # (2)
ax
セルには最後のax(sns.heatmapの戻り値)が画像として返ります。3行×6列のマスにannot=True(1)で売上の数値が重ねて描かれ、cmap="Blues"で値が大きいマスほど濃い青になります。売上額の大きな複合機の3月~5月が最も濃く、シュレッダーの行は全体に薄い色でそろいます。x軸ラベル(月)はlabelrotation=45(2)で回転させて重なりを防ぎます。
色の濃淡によって、「複合機は3月~5月に売上が大きくなっている」といった傾向をひと目で確認できます。同じheatmapは集計表だけでなく相関行列にも使えます。まず相関行列を作ります。
corr = df[
["売上", "広告費", "問い合わせ件数"]
].corr()
corr.round(3)
corr.round(3)を実行すると、相関係数が表として返ります。続くセルでも同様に、直前のセルで作ったcorrをheatmapに渡します。
sns.heatmap(corr, annot=True, cmap="coolwarm", # (1)
vmin=-1, vmax=1)
セルにはsns.heatmap(...)の戻り値が画像として返ります。3行×3列のマスに相関係数が重ねて描かれ、対角線(自分自身との相関=1.00)と売上×広告費のマスが濃い赤になります。問い合わせ件数が絡むマスは0.5前後で、淡い赤にとどまります。
先の集計表は「商品カテゴリ×月」の業務軸、こちらは「変数×変数」の相関行列という別の用途です。vmin/vmax(1)で色付けする値の範囲を-1~1に固定すると、相関係数の値を同じ基準で比較できます。coolwarmでは、正の値が赤系、負の値が青系、0付近が白系で表示されるため、負の相関が出た場合も読み取りやすくなります。
箱ひげ図
前節のエラーバーとは別の方法で、データのばらつきを箱ひげ図で確認します。箱ひげ図は、中央値・四分位範囲・ひげ・外れ値などを1つの図にまとめ、データの分布を比較するためのグラフです。なお、箱ひげ図とエラーバーは表している統計量が異なります。
import seaborn as sns
df = xl("A1:E19", headers=True)
sns.boxplot(data=df, x="商品カテゴリ", y="売上")
sns.boxplot(...)の戻り値(Axes)が画像としてセルに返り、x軸の並びは前節の棒グラフと同じで、元データの出現順(複合機・プリンター・シュレッダー)です。
3つの箱の位置と高さは次のとおりです(describe()による検算値)。
- 複合機:箱(第1四分位~第3四分位)は1067.5~1617.5、中央値の線は1320。ひげは980(最小)~1880(最大)まで伸びる。
- プリンター:箱は1252.5~1347.5、中央値は1300。ひげは1180~1420。
- シュレッダー:箱は627.5~695、中央値は665。ひげは610~720。
1.5×四分位範囲を超える外れ値は、今回のデータにはありません。
注目したいのは、複合機とプリンターの中央値(1320と1300)がほぼ同じ高さに並ぶ一方、箱の高さ(四分位範囲)は複合機550・プリンター95と大きく異なる点です。平均値だけを見ると分かりにくい「データのばらつき」の違いを、箱ひげ図では確認できます。今回のデータでは、複合機の売上の変動がプリンターより大きいことが読み取れます。
[コラム]レポート提出用に体裁をそろえる
直前の箱ひげ図はseabornの既定の見た目です。社外や上司に提出する図は、第1節の棒グラフと同じmatplotlibの指定に戻すと、軸ラベル・タイトル・グリッドをそろえられます。同じデータをmatplotlibだけで書くと、次のようになります。
import matplotlib.pyplot as plt
plt.rcParams["font.sans-serif"] = [
"Yu Gothic", "Meiryo", "MS Gothic", "Hiragino Sans", "sans-serif"
]
plt.rcParams["axes.unicode_minus"] = False
df = xl("A1:E19", headers=True)
names, data = [], [] # カテゴリごとに売上を切り出す
for name, sales in df.groupby(
"商品カテゴリ", sort=False)["売上"]:
names.append(name)
data.append(sales)
fig, ax = plt.subplots()
ax.boxplot(data, patch_artist=True, widths=0.8, # (1)
boxprops=dict(facecolor="steelblue"))
ax.set_xticklabels(names)
ax.set_xlabel("商品カテゴリ")
ax.set_ylabel("売上(千円)")
ax.set_title("商品カテゴリ別 売上のばらつき")
ax.grid(axis="y", linestyle="--")
fig
最後のfigが画像としてセルに返ります。直前と同じ3つの箱ですが、箱の色は第1節の棒グラフと同じsteelblueになり、軸ラベルとタイトル、横方向のグリッド線が加わります。
(1)ax.boxplotは列名を直接受け取れないため、groupby(..., sort=False)で出現順を保った売上をリストに切り出して渡し、set_xticklabelsで軸に載せます。seabornの1行に対して準備が増えるのは、細部を自分で制御する代わりです。patch_artist=Trueは箱を塗りつぶす指定です。
このように、散布図・ヒートマップ・箱ひげ図は「関係」「濃淡」「ばらつき」という別の切り口を示す図で、体裁を整えれば業務レポートに耐える形になります。
まとめ
本稿では、セルに図を表示する方法から、代表的な図の使い分けまでを確認しました。FigureやAxesをPythonセルの最後の式として返すことで、作成したグラフをセルの出力として表示できます。matplotlibは細部の制御に、seabornは統計データを少ないコードで可視化するときに向いています。散布図は変数同士の関係、ヒートマップは数値の大小や相関の全体像、箱ひげ図は分布やばらつき、といった形で、平均値や中央値だけでは見えにくい特徴を図から読み取れます。
本稿はデータの関係やばらつきを「見る」ための内容でした。次回第4回では、scikit-learnを使い、「予測する」段階に進む予定です。
