matplotlibを使ったグラフの作成

# はじめに
matplotlibを使って以下のようなグラフを作成するためのコードをまとめてみました。
- [数値付き棒グラフ](#数値付き棒グラフ)
- [積み上げ棒グラフ](#積み上げ棒グラフ)
- [横並び棒グラフ](#横並び棒グラフ)
- [2軸グラフ（棒グラフ+折れ線グラフ)](#2軸グラフ)
- [パレート図](#パレート図)

説明は少なめですが、何か参考になれば幸いです。

# 準備
## パッケージ
使用するパッケージは以下の通りです。
```Python:コード
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](https://www.kaggle.com/datasets/rohitsahoo/sales-forecasting)です。このデータセットを少し前処理した上で、以下の列に着目してグラフを描画することにします。

|#  |列名  |説明  |
|---|---|---|
|1  |Order_ID    |注文ID（ユニーク数：4922）|
|2  |Order_yyyymm  |注文日年月（2015年1月-2018年12月）  |
|3  |Segment     |注文者の業態（Consumer・Corporate・Home Office）|
|4  |Sales       |売上  |

以下ではこのデータフレームが`df`という変数に格納されているとします。

# 数値付き棒グラフ
年月別の売上を棒グラフとして描画し、さらにその数値もグラフ内に表示させるためのコードを紹介します。

:::note info
数値付き棒グラフのポイント
`ax1.text()`を使ってテキスト表示させる。
もし文字が重なってしまう場合はadjustTextを使う。
:::

以下のような出力用のデータフレームを作成し、グラフを描画します。

|#  |列名  |説明  |
|---|---|---|
|1  |Order_yyyymm    |注文日の年月|
|2  |Sales  |売上  |

```Python:コード
# 出力用のデータフレームの作成
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()
```

![output.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3985919/7c4f3ad0-6de5-6f79-3281-8ca0e8f2767c.png)



# 積み上げ棒グラフ
ここでは年月別に注文者の業態（Segment）毎の売上を積み上げ棒グラフで表示させるコードを紹介します。

:::note info
積み上げ棒グラフのポイント
`ax1.bar()`の引数で`bottom=`を設定すること。
:::

以下のような出力用のデータフレームを作成し、グラフを描画します。

|#  |列名  |説明  |
|---|---|---|
|1  |Order_yyyymm    |注文日の年月|
|2  |Consumer  |Consumerの売上  |
|3  |Corporate     |Corporateの売上|
|4  |Home Office |Home Officeの売上  |

```python:コード
# 出力用のデータフレームの作成
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()
```

<img width="100%" alt="output.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3985919/403499ee-a7ce-658f-94d5-694772111aed.png">

このコードを少し調整することで、以下のように割合に直したグラフを作成することもできます。

```Python:コード
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()
```
![output.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3985919/d15e33c1-2360-bb09-fcf9-925b9c1c2c83.png)


# 横並び棒グラフ
上で描画した積み上げ棒グラフを横並びにしたバージョンを表示させるコードを紹介します。

:::note info
横並び棒グラフのポイント
`ax1.bar()`の引数で`width=`を設定すること。
:::

```Python:コード
# 出力用のデータフレームの作成
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()
```

![output.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3985919/02f0509c-b695-a68d-0848-07076a0f6181.png)

# 2軸グラフ
これまでは注文者の業態（Segment）毎の売上に着目していましたが、それと同時に業態毎の1注文当たりの単価が気になる場合もあると思います。そこで売上を棒グラフ、単価を折れ線グラフで描画するコードを紹介します。

:::note info
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の単価  |

```Python:コード
# 出力用のデータフレームの作成
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()
```

![output.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3985919/bf6f2668-cae3-de5b-5091-40e8dd0bca6e.png)

# パレート図
2軸グラフの応用として、州毎の売り上げについてのパレート図を描画するコードを紹介します。

```Python:コード
# 出力用のデータフレームの作成
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()
```

![output.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3985919/b01aef6a-bea0-e169-49f4-d006238e475e.png)