You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Matplotlib柱状图:按柱宽1/5偏移调整柱顶文本位置失效问题

Matplotlib柱状图数值文本偏移失效问题解决

我想在Matplotlib绘制的柱状图中,把每个柱形的数值文本放在柱顶下方,偏移量设为柱宽(barwidth)的1/5,但代码里的text_y -= bar.get_width()/5完全没生效。以下是完整代码和当前效果:

#!/usr/bin/env python3
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import pandas as pd
import matplotlib.patheffects as PathEffects
import math
import numpy as np

def save_fig(fig,pngname):
    fig.savefig(pngname, dpi=fig.dpi, bbox_inches="tight")
    print("[[%s]]"%pngname)    
    return

def plot_bar(df):
    xname = df.columns[0]    
    fig, ax = plt.subplots(figsize=(10, 5))
    x = np.arange(len(df[xname]))
    n = len(df.columns[1:])
    bar_width = 0.95/n
    fontsize = 20

    colors = ['#5891ad','#004561','#ff6f31','#1c7685','#0f45a8','#4cdc8b','#0097a7']
    dy = -bar_width/10
    bars = []
    # add bars
    for i,colname in enumerate(df.columns[1:]):
        bar = ax.bar(x+i*bar_width, df[colname], width=bar_width,color=colors[i])
        bars.append(bar)

    # add text on bars
    for bar in ax.patches:
        bar_value = bar.get_height()
        text = f'{bar_value:,}'
        text_x = bar.get_x() + bar.get_width() / 2
        text_y = bar.get_y() + bar_value
        text_y -= bar.get_width()/5 # <- 这里未生效
        bar_color = bar.get_facecolor()
        t = ax.text(text_x, text_y, text, ha='center', va='top', color=bar_color,
              size=fontsize)
        t.set_path_effects([PathEffects.withStroke(linewidth=bar_width*15, foreground='w')])

    ax.set_xticks(x +  1/ 2 - bar_width/2)
    ax.set_xticklabels(df[xname])

    ax.legend()
    ax.spines['top'].set_visible(False)
    ax.spines['right'].set_visible(False)
    #ax.spines['left'].set_visible(False)
    #ax.spines['bottom'].set_color('#DDDDDD')
    ax.tick_params(bottom=False, left=False)
    ax.set_axisbelow(True)
    ax.yaxis.grid(True, color='#EEEEEE')
    ax.xaxis.grid(False)

    ax.set_xlabel('x', labelpad=15)
    ax.set_ylabel('y', labelpad=15)
    ax.set_title('title', pad=15)

    fig.tight_layout()    

    plt.show()
    return

data = [['a',3,2,1],
    ['b',2,3,1],
    ['c',3,1,3],
    ['d',5,1,3],    
    ]

df = pd.DataFrame(data,columns=['f1','f2','f3','f4'])
plot_bar(df)

当前柱状图效果


问题原因

bar.get_width()获取的是x轴方向的刻度单位宽度,但text_y是y轴的坐标值,x轴和y轴的尺度差异极大(比如你图里x轴范围0-3,y轴范围0-5),用x轴的数值去调整y轴坐标,偏移量小到肉眼根本察觉不到,所以看起来像没生效。

解决方法

方法1:用y轴相对比例设置偏移(最稳妥)

把失效的那行代码替换成:

# 取y轴总范围的1%作为向下偏移量,可根据需求调整比例
y_range = ax.get_ylim()[1] - ax.get_ylim()[0]
text_y -= y_range * 0.01

方法2:固定像素偏移(直观可控)

如果想要固定像素的偏移,用坐标转换实现:

# 向下偏移8个像素,数值可按需调整
pixel_offset = -8
# 把数据坐标转成屏幕显示坐标,调整后再转回去
display_pos = ax.transData.transform((text_x, text_y))
new_display_pos = (display_pos[0], display_pos[1] + pixel_offset)
text_x, text_y = ax.transData.inverted().transform(new_display_pos)

方法3:把x轴宽度转成y轴等效偏移(坚持原需求逻辑)

如果一定要用柱宽的1/5作为偏移,计算x和y轴的显示尺度比例,转换后再调整:

# 计算x、y轴每个刻度单位对应的像素数
x_pixel_per_unit = ax.transData.transform((1, 0))[0] - ax.transData.transform((0, 0))[0]
y_pixel_per_unit = ax.transData.transform((0, 1))[1] - ax.transData.transform((0, 0))[1]
# 把x轴的宽度值转换成y轴的等效偏移量
scale_ratio = y_pixel_per_unit / x_pixel_per_unit
text_y -= (bar.get_width() / 5) * scale_ratio

内容的提问来源于stack exchange,提问作者lucky1928

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 13:48:24