階層構造のインデックスを持ったMatplotlibのデータフレームを、そのままプロットすると、軸ラベルがタプルそのままの表示となって、やや残念な感じになります。
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
a = pd.DataFrame(np.random.random([6, 2]),
index=pd.MultiIndex.from_product([['group1', 'group2'],['item1', 'item2', 'item3']]),
columns=['data1', 'data2'])
a.plot.bar()
階層構造のデータであることが見やすいような軸ラベルが付けたいと思って調べてみましたが、とりあえず下記のような方法でできる模様。
def set_hierarchical_xlabels(index, ax=None,
bar_xmargin=0.1, # 線の左右両端のマージン、X軸のスケール
bar_yinterval=0.1, # 線の上下の間隔、Y軸の長さを1とした相対値?
):
from itertools import groupby
from matplotlib.lines import Line2D
ax = ax or plt.gca()
assert isinstance(index, pd.MultiIndex)
labels = ax.set_xticklabels([s for *_, s in index])
for lb in labels:
lb.set_rotation(0)
transform = ax.get_xaxis_transform()
for i in range(1, len(index.codes)):
xpos0 = -0.5 # 対象グループの左側の座標
for (*_, code), codes_iter in groupby(zip(*index.codes[:-i])):
xpos1 = xpos0 + sum(1 for _ in codes_iter) # 対象グループの右側の座標
ax.text((xpos0+xpos1)/2, (bar_yinterval * (-i-0.1)),
index.levels[-i-1][code],
transform=transform,
ha="center", va="top")
ax.add_line(Line2D([xpos0+bar_xmargin, xpos1-bar_xmargin],
[bar_yinterval * -i]*2,
transform=transform,
color="k", clip_on=False))
xpos0 = xpos1
a.plot.bar()
set_hierarchical_xlabels(a.index)
細かいデザインは目的や好みによって変わると思うので、上記を参考に適当に改変すればOK。
3階層以上のMultiIndexでもプロットできます。