中央揃え帯グラフ

帯グラフで中央で揃える方法を紹介した。英語では diverging stacked bar chart というらしい。divergingは両側に伸びる意味だが、適訳がない。

t検定で使ったLumleyのデータを中央揃え帯グラフにしてみよう。

import numpy as np
import pandas as pd
from collections import Counter
import matplotlib.pyplot as plt
from matplotlib.ticker import MaxNLocator

x = [1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 4, 1, 1]
y = [3, 3, 4, 3, 1, 2, 3, 1, 1, 5, 4]

df = pd.DataFrame([Counter(x), Counter(y)], index=["x", "y"])
df = df.fillna(0).astype(int)
df = df.reindex(columns=range(min(x+y), max(x+y)+1), fill_value=0)
df = df[::-1] 

n = len(df.columns)
def get_offset(row):
    mid_idx = n // 2
    if n % 2 == 1:
        return -(row.iloc[:mid_idx].sum() + row.iloc[mid_idx] / 2)
    else:
        return -row.iloc[:mid_idx].sum()

offsets = df.apply(get_offset, axis=1)

fig, ax = plt.subplots(figsize=(6.4, 2))
cmap = plt.get_cmap("coolwarm")
colors = cmap(np.linspace(0, 1, n))

y_pos = np.arange(len(df.index))

for i, col in enumerate(df.columns):
    widths = df[col]
    lefts = offsets + df.iloc[:, :i].sum(axis=1)
    ax.barh(y_pos, widths, left=lefts, color=colors[i], edgecolor="black", label=col)

ax.set_yticks(y_pos)
ax.set_yticklabels(df.index)
# ax.axvline(0, color='black', linewidth=1, linestyle='--')
ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left')

min_x = offsets.min()
max_x = (offsets + df.sum(axis=1)).max()
# ax.set_xlim(min_x, max_x)
ax.set_xlim(min_x - 1, max_x + 1)

# 目盛を整数に限る
ax.xaxis.set_major_locator(MaxNLocator(integer=True))

# 見やすい位置に移動
plt.tight_layout()
plt.show()
中央揃え帯グラフ