この記事が役に立つ人
- Pythonのmatplotlibで正負混合のデータの積み上げグラフ内にラベルを入れたい人
- 積み上げグラフの幅が狭いときラベルを省略したい人
ラベルの位置決め
正負混合のデータの積み上げグラフはpandasの描画機能で次のように作ることができます.
import numpy as np
import pandas as pd
import matplotlib
import matplotlib.pyplot as plt
df = pd.DataFrame({'id':[1,2,3,4,5,6],
'f1':[30,45,10,15,4,30],
'f2': [20,-30,60,-40,45,-4],
'f3': [50,25,-30,45,15,2]})
df = df.set_index('id')
ax = plt.subplot()
df.plot.bar(stacked=True, legend=True, ax=ax)
ax.set_ylabel('value')
グラフは次のようになります.
正負混在しているグラフでは,描画されている矩形の位置が決まっていないので,参考資料にあるように位置に依存したテキストの描画ができません.
そのため,matplotlibのpatch情報を調べて矩形の描画位置を調べなければなりません.この場合のpatchはRectangleになります.Rectangleからは直接 y方向の描画位置がわかりません.
print (ax.patches[0])
>Rectangle(-0.25,0;0.5x30)
描画位置をとるには,get_bbox()を用います.これが,描画位置になります.
print (ax.patches[0].get_bbox())
>Bbox(x0=-0.25, y0=0.0, x1=0.25, y1=30.0)
patch情報は,配列として取得され,その順番は dataframe の並びに依存しています.この情報を用いて,次のようにテキストを描画できます.
yno = len(df)
xno = len(df.columns)
x_offset = 0.0
y_offset = 0.0
# for p in ax.patches:
for index, p in enumerate(ax.patches):
(xn, yn) = divmod(index, yno)
value = df.iloc[yn,xn]
label = df.columns[xn]
b = p.get_bbox()
# ypos = (b.y0 + b.y1)/2
ypos = min(b.y0, b.y1)
ax.text( b.x0 + x_offset, ypos + y_offset, label)
ここでは,矩形の左隅に描画しています.
高さの小さい矩形のなかにはラベルを書かない処理
積み上げグラフで重要な項目はデータの大きなものです.この例ではデータの数が少ないですが,多い場合は不要なテキストの描画を避けるべきです.データの値によって選択することもできますが,データの値がわからないときは,描画位置から決めることができます.
次に一例をあげます.この例では,描画範囲の6%未満のデータにはテキストを書きません.
yno = len(dfs)
xno = len(dfs.columns)
# check max range of bar
rmax = 0
rmin = 0
for index, p in enumerate(ax.patches):
b = p.get_bbox()
if (b.y1 > rmax):
rmax = b.y1
if (b.y0 > rmax):
rmax = b.y0
if (b.y1 < rmin):
rmin = b.y1
if (b.y0 < rmin):
rmin = b.y0
rrange = rmax-rmin
x_offset = 0.0
y_offset = 0.0
t_height = rrange*0.06 # condition for adding text
for index, p in enumerate(ax.patches):
(xn, yn) = divmod(index, yno)
value = dfs.iloc[yn,xn]
label = dfs.columns[xn]
b = p.get_bbox()
height = abs (b.y1 - b.y0)
if (height < t_height): # 高さが小さいならスキップ
continue
ypos = min(b.y1, b.y0)
# ypos = (b.y1 + b.y0)/2
ax.text( b.x0 + x_offset, ypos + y_offset, label)