はじめに
matplotlibを使って以下のようなグラフを作成するためのコードをまとめてみました。
説明は少なめですが、何か参考になれば幸いです。
準備
パッケージ
使用するパッケージは以下の通りです。
from datetime import datetime
import pandas as pd
import matplotlib.pyplot as plt
import japanize_matplotlib
from adjustText import adjust_text
データセット
使用するデータセットはKaggleで公開されているSuperstore Sales Datasetです。このデータセットを少し前処理した上で、以下の列に着目してグラフを描画することにします。
# | 列名 | 説明 |
---|---|---|
1 | Order_ID | 注文ID(ユニーク数:4922) |
2 | Order_yyyymm | 注文日年月(2015年1月-2018年12月) |
3 | Segment | 注文者の業態(Consumer・Corporate・Home Office) |
4 | Sales | 売上 |
以下ではこのデータフレームがdf
という変数に格納されているとします。
数値付き棒グラフ
年月別の売上を棒グラフとして描画し、さらにその数値もグラフ内に表示させるためのコードを紹介します。
数値付き棒グラフのポイント
ax1.text()
を使ってテキスト表示させる。
もし文字が重なってしまう場合はadjustTextを使う。
以下のような出力用のデータフレームを作成し、グラフを描画します。
# | 列名 | 説明 |
---|---|---|
1 | Order_yyyymm | 注文日の年月 |
2 | Sales | 売上 |
# 出力用のデータフレームの作成
df_res = df.groupby('Order_yyyymm')[['Sales']].sum()
df_res = df_res.reset_index()
# グラフの描画
fig = plt.figure(figsize=(15, 7))
# カラーマップの設定
cmap = plt.colormaps.get_cmap('tab10')
# 1つ目の軸
ax1 = fig.add_subplot(111)
# 棒グラフの描画
texts = []
x = df_res.index
y = df_res['Sales']
ax1.bar(x, y, alpha=0.5, color=cmap(0))
for xdata, ydata in zip(x, y):
texts.append(ax1.text(xdata, ydata, f'${ydata/1000:,.0f}K', ha='center', va='bottom', color=cmap(0)))
adjust_text(texts)
# 軸の設定
ax1.set_xticks(df_res.index)
ax1.set_xticklabels(df_res['Order_yyyymm'], rotation=90)
ax1.set_xlabel('注文日')
ax1.set_ylabel('売上')
# グリッドの表示
ax1.grid(axis='y')
plt.show()
積み上げ棒グラフ
ここでは年月別に注文者の業態(Segment)毎の売上を積み上げ棒グラフで表示させるコードを紹介します。
積み上げ棒グラフのポイント
ax1.bar()
の引数でbottom=
を設定すること。
以下のような出力用のデータフレームを作成し、グラフを描画します。
# | 列名 | 説明 |
---|---|---|
1 | Order_yyyymm | 注文日の年月 |
2 | Consumer | Consumerの売上 |
3 | Corporate | Corporateの売上 |
4 | Home Office | Home Officeの売上 |
# 出力用のデータフレームの作成
df_res = df.groupby('Order_yyyymm')[['Sales']].sum().rename(columns={'Sales': 'all'})
# セグメント毎に集計
segments = ['Consumer', 'Corporate', 'Home Office']
for seg in segments:
df_res[seg] = df.loc[df['Segment']==seg, :].groupby('Order_yyyymm')['Sales'].sum()
df_res = df_res.reset_index()
# グラフの描画
fig = plt.figure(figsize=(15, 7))
# カラーマップの設定
cmap = plt.colormaps.get_cmap('tab10')
# 1つ目の軸
ax1 = fig.add_subplot(111)
# 棒グラフの描画
y_cumsum = 0
for i, seg in enumerate(segments):
x = df_res.index
y = df_res[seg]
ax1.bar(x, y, bottom=y_cumsum, alpha=0.5, color=cmap(i), label=seg)
y_cumsum += y
# 軸の設定
ax1.set_xticks(df_res.index)
ax1.set_xticklabels(df_res['Order_yyyymm'], rotation=90)
ax1.set_xlabel('注文日')
ax1.set_ylabel('売上[$]')
# 凡例の表示
ax1.legend()
# グリッドの表示
ax1.grid(axis='y')
plt.show()

このコードを少し調整することで、以下のように割合に直したグラフを作成することもできます。
df = df_data.copy()
# 出力用のデータフレームの作成
df_res = df.groupby('Order_yyyymm')[['Sales']].sum().rename(columns={'Sales': 'all'})
# セグメント毎に集計
segments = ['Consumer', 'Corporate', 'Home Office']
for seg in segments:
df_res[seg] = df.loc[df['Segment']==seg, :].groupby('Order_yyyymm')['Sales'].sum()
df_res[f'{seg}_rate'] = df_res[seg] / df_res['all']
df_res = df_res.reset_index()
# グラフの描画
fig = plt.figure(figsize=(15, 7))
# カラーマップの設定
cmap = plt.colormaps.get_cmap('tab10')
# 1つ目の軸
ax1 = fig.add_subplot(111)
# 棒グラフの描画
y_cumsum = 0
for i, seg in enumerate(segments):
x = df_res.index
y = df_res[f'{seg}_rate'] * 100
ax1.bar(x, y, bottom=y_cumsum, alpha=0.5, color=cmap(i), label=seg)
y_cumsum += y
# 軸の設定
ax1.set_xticks(df_res.index)
ax1.set_xticklabels(df_res['Order_yyyymm'], rotation=90)
ax1.set_xlabel('注文日')
ax1.set_ylabel('売上割合[%]')
# 凡例の表示
ax1.legend()
# グリッドの表示
ax1.grid(axis='y')
plt.show()
横並び棒グラフ
上で描画した積み上げ棒グラフを横並びにしたバージョンを表示させるコードを紹介します。
横並び棒グラフのポイント
ax1.bar()
の引数でwidth=
を設定すること。
# 出力用のデータフレームの作成
df_res = df.groupby('Order_yyyymm')[['Sales']].sum().rename(columns={'Sales': 'all'})
# セグメント毎に集計
segments = ['Consumer', 'Corporate', 'Home Office']
for seg in segments:
df_res[seg] = df.loc[df['Segment']==seg, :].groupby('Order_yyyymm')['Sales'].sum()
df_res = df_res.reset_index()
# グラフの描画
fig = plt.figure(figsize=(15, 7))
# カラーマップの設定
cmap = plt.colormaps.get_cmap('tab10')
# 1つ目の軸
ax1 = fig.add_subplot(111)
# 棒グラフの描画
width = 0.9 / len(segments)
for i, seg in enumerate(segments):
x = df_res.index
y = df_res[seg]
ax1.bar(x+(-0.5+i)*width, y, width=width, alpha=0.5, color=cmap(i), label=seg)
# 軸の設定
ax1.set_xticks(df_res.index)
ax1.set_xticklabels(df_res['Order_yyyymm'], rotation=90)
ax1.set_xlabel('注文日')
ax1.set_ylabel('売上[$]')
# 凡例の表示
ax1.legend()
# グリッドの表示
ax1.grid(axis='y')
plt.show()
2軸グラフ
これまでは注文者の業態(Segment)毎の売上に着目していましたが、それと同時に業態毎の1注文当たりの単価が気になる場合もあると思います。そこで売上を棒グラフ、単価を折れ線グラフで描画するコードを紹介します。
2軸グラフのポイント
ax2=ax1.twinx()で2軸目の設定をする。
以下のような出力用のデータフレームを作成し、グラフを描画します。
# | 列名 | 説明 |
---|---|---|
1 | Order_yyyymm | 注文日の年月 |
2 | Consumer_Sales | Consumerの売上 |
3 | Corporate_Sales | Corporateの売上 |
4 | Home Office_Sales | Home Officeの売上 |
5 | Consumer_Unit_Price | Consumerの単価 |
6 | Corporate_Unit_Price | Corporateの単価 |
7 | Home Office_Unit_Price | Home Officeの単価 |
# 出力用のデータフレームの作成
df_res = df.groupby('Order_yyyymm')[['Sales']].sum().rename(columns={'Sales': 'all_Sales'})
# セグメント毎に売上を集計
segments = ['Consumer', 'Corporate', 'Home Office']
for seg in segments:
df_res[f'{seg}_Sales'] = df.loc[df['Segment']==seg, :].groupby('Order_yyyymm')['Sales'].sum()
# セグメント毎に単価を集計
df_temp = df.groupby(['Order_yyyymm', 'Order_ID', 'Segment'])[['Sales']].sum()
df_temp = df_temp.reset_index()
for seg in segments:
df_res[f'{seg}_Unit_Price'] = df_temp.loc[df_temp['Segment']==seg, :].groupby('Order_yyyymm')['Sales'].mean()
df_res = df_res.reset_index()
# グラフの描画
fig = plt.figure(figsize=(15, 7))
# カラーマップの設定
cmap = plt.colormaps.get_cmap('tab10')
# 1つ目の軸
ax1 = fig.add_subplot(111)
# 棒グラフの描画
y_cumsum = 0
for i, seg in enumerate(segments):
x = df_res.index
y = df_res[f'{seg}_Sales']
ax1.bar(x, y, bottom=y_cumsum, alpha=0.5, color=cmap(i), label=seg)
y_cumsum += y
# 2つの目の軸
ax2 = ax1.twinx()
# 折れ線グラフの描画
for i, seg in enumerate(segments):
x = df_res.index
y = df_res[f'{seg}_Unit_Price']
ax2.plot(x, y, alpha=1, color=cmap(i))
# 軸の設定
ax1.set_xticks(df_res.index)
ax1.set_xticklabels(df_res['Order_yyyymm'], rotation=90)
ax1.set_xlabel('注文日')
ax1.set_ylabel('売上[$]')
ax2.set_ylabel('単価[$]')
# 凡例の表示
ax1.legend()
# グリッドの表示
ax1.grid(axis='y')
plt.show()
パレート図
2軸グラフの応用として、州毎の売り上げについてのパレート図を描画するコードを紹介します。
# 出力用のデータフレームの作成
df_res = df.groupby('State')[['Sales']].sum()
df_res = df_res.reset_index()
df_res = df_res.sort_values('Sales', ascending=False)
df_res = df_res.reset_index()
df_res['Sales_rate'] = df_res['Sales'] / df_res['Sales'].sum()
df_res['Sales_rate_cumsum'] = df_res['Sales_rate'].cumsum()
df_res = df_res.iloc[:40, :]
# グラフの描画
fig = plt.figure(figsize=(20, 7))
# カラーマップの設定
cmap = plt.colormaps.get_cmap('tab10')
texts = []
# 1つ目の軸
ax1 = fig.add_subplot(111)
# 棒グラフの描画
x = df_res.index
y = df_res['Sales']
ax1.bar(x, y, alpha=0.5, color=cmap(0))
for xdata, ydata in zip(x, y):
ax1.text(xdata, ydata, f'${ydata/1000:.0f}K', ha='center', va='bottom', color=cmap(0))
# 2つの目の軸
ax2 = ax1.twinx()
# 折れ線グラフの描画
x = df_res.index
y = df_res['Sales_rate_cumsum'] * 100
ax2.plot(x, y, marker='.', alpha=1, color=cmap(1))
# 軸の設定
ax1.set_xticks(df_res.index)
ax1.set_xticklabels(df_res['State'], rotation=90)
ax1.set_xlabel('州')
ax1.set_ylabel('売上[$]')
ax2.set_ylabel('売上割合')
ax2.set_ylim(0, 100)
# グリッドの表示
ax2.grid(axis='y')
plt.show()