はじめに
最近、seabornというpythonでグラフを作るためのライブラリを使い始めたのですが、積み上げ棒グラフを作るのに少し手間かかったので、この機会にまとめてみることにしました。
サンプルソースを作成したので、困っている方の参考になれば幸いです。
(22/12/20 19:33追記)
コメント欄にて、簡潔な積み上げ棒グラフの作り方を教えて頂きました。ありがとうございます!
積み上げ棒グラフを出力
作成したサンプルソースは次のようになっています。
# パッケージのインポート
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import rc
import seaborn as sns
# グラフの日本語対応
rc('font', family='BIZ UDGothic')
# データの用意
study_history = pd.DataFrame({'年': [2021, 2021, 2021, 2022, 2022, 2022],
'学習内容': ['英語', 'プログラミング', '資格', '英語', 'プログラミング', '資格'],
'学習時間': [30, 150, 100, 150, 100, 100]})
# 勉強時間の全期間の合計が多い順に積み上げるための工夫
df = study_history.groupby('学習内容', as_index=False) \
.agg({ '学習時間': 'sum' }) \
.sort_values('学習時間', ascending=False) \
.drop(columns='学習時間')
df['積み上げ順序'] = range(1, df.shape[0]+1)
study_history = pd.merge(study_history, df, on=['学習内容'], how='left')
study_history.sort_values(['年', '積み上げ順序'], inplace=True)
# メモリ開放
del df
# 棒グラフを積み上げたときのtopの高さを作成
study_history['top'] = study_history.groupby('年')['学習時間'].cumsum()
# カラーパレットの作成
colors = sns.color_palette(n_colors=3)
# 学習内容で分割してリストに格納
labels = study_history['学習内容'].unique()
dfs = [study_history.loc[study_history['学習内容']==label] for label in labels]
# 積み上げ棒グラフの表示
fig, ax = plt.subplots(1, 1, figsize=(10, 5))
for i, _ in enumerate(labels):
if i == 0:
sns.barplot(x='年', y='学習時間', data=dfs[i], ax=ax, label=labels[i], color=colors[i])
else:
sns.barplot(x='年', y='学習時間', data=dfs[i], ax=ax, bottom=dfs[i-1]['top'], label=labels[i], color=colors[i])
ax.legend(loc='upper left')
上記サンプルソースを実行すると次のグラフが生成されます。
上から順番に解説します。
必要なパッケージをインポートしています。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import rc
import seaborn as sns
今回用意したデータに日本語を含めました。
デフォルトだとグラフが文字化けするので、日本語の文字化け対応で次のコードを書いています。
rc('font', family='BIZ UDGothic')
積み上げ棒グラフに使うデータとして、架空のA君の学習履歴(年度と学習内容で勉強時間がサマリされたデータ)を用意してみました。
study_history = pd.DataFrame({'年度': [2021, 2021, 2021, 2022, 2022, 2022],
'学習内容': ['英語', 'プログラミング', '資格', '英語', 'プログラミング', '資格'],
'学習時間': [30, 150, 100, 150, 100, 100]})
学習履歴をデータフレームで表示すると次のようになっています。
年 | 学習内容 | 学習時間 | |
---|---|---|---|
0 | 2021 | 英語 | 30 |
1 | 2021 | プログラミング | 150 |
2 | 2021 | 資格勉強 | 100 |
3 | 2022 | 英語 | 150 |
4 | 2022 | プログラミング | 100 |
5 | 2022 | 英語 | 100 |
積み上げ棒グラフでは、学習時間の大きい棒グラフから積み上がってほしいので、順位を付与した後、昇順で並び替えてみました。
# 勉強時間の全期間の合計が多い順に積み上げるための工夫
df = study_history.groupby('学習内容', as_index=False) \
.agg({ '学習時間': 'sum' }) \
.sort_values('学習時間', ascending=False) \
.drop(columns='学習時間')
df['積み上げ順序'] = range(1, df.shape[0]+1)
study_history = pd.merge(study_history, df, on=['学習内容'], how='left')
study_history.sort_values(['年', '積み上げ順序'], inplace=True)
# メモリ開放
del df
sns.barplotの引数「bottom」に渡すためのデータを作成します。
対象の学習内容を含めが棒グラフの高さを出しておき、あとの話になりますが、bottomにはひとつ前の学習内容の棒グラフの高さを渡すようにします。
# 棒グラフを積み上げたときのtopの高さを作成
study_history['top'] = study_history.groupby('年')['学習時間'].cumsum()
色を指定しないと異なる学習内容にも拘わらず、同色になってしまうので、カラーパレットを作っておきます。
# カラーパレットの作成
colors = sns.color_palette(n_colors=3)
学習内容ごとリストに格納し、学習内容のリストを使って学習内容別にデータを分割してリストに格納しておきます。
データをリストに格納しておくことで、ひとつ前の学習内容の棒グラフの高さ(top)に簡単にアクセスできるようにしています。
# 学習内容で分割してリストに格納
labels = study_history['学習内容'].unique()
dfs = [study_history.loc[study_history['学習内容']==label] for label in labels]
for文で回してグラフを積み上げていきます。
一つ目はbottomが不要なので、処理を分けています。
# 積み上げ棒グラフの表示
fig, ax = plt.subplots(1, 1, figsize=(10, 5))
for i, _ in enumerate(labels):
if i == 0:
sns.barplot(x='年', y='学習時間', data=dfs[i], ax=ax, label=labels[i], color=colors[i])
else:
sns.barplot(x='年', y='学習時間', data=dfs[i], ax=ax, bottom=dfs[i-1]['top'], label=labels[i], color=colors[i])
ax.legend(loc='upper left')
以上になります。
おわりに
今回、積み上げ棒グラフを作るだけにも拘わらず、コードが長くなってしまいました。
簡潔な書き方がわかり次第追記しようと思います。