
摸过 Python 数据可视化的人十有八九是从柱状图开始的。确实类别对比的诉求在数据分析里出现频率太高了各产品线销量、各部门 KPI、各月份新增用户、各类目转化率。遇到这种场景画个柱状图最快、也最直观。而 Matplotlib 的plt.bar和plt.barh这一组 API我认为是所有绘图库里最值得吃透的。它会直接决定你后面画折线图、散点图、组合图时对坐标系统和图形元素的掌控力。这篇文章会把垂直、水平、分组、堆叠四种经典形态完整过一遍还会补几个只有踩过坑才会注意到的细节比如颜色怎么统一、数值标注怎么放、保存 GIF 时 Pillow 没装会怎样。内容从最小代码开始逐步加需求再到问题排查全部基于我实际跑过的代码适合刚入门 Python 绘图的新手也适合平时用 Matplotlib 做报表、做 PPT 配图、做论文插图的职场人和研究生。1. 柱状图是类别对比的万金油1.1 柱状图在传递什么信息柱状图的核心逻辑很简单用条形的长度来映射数值大小。人眼对长度差异的感知比对面积的感知要敏感得多这也是为什么柱状图比饼图更适合做精确对比。它天然处理的是“离散类别 连续数值”这种数据组合比如三个城市的销售额、五个渠道的点击量、十二个月的营收。在实际项目里柱状图承担的角色通常是“一眼看出差异”。它不需要像折线图那样展示趋势变化斜率也不需要像散点图那样探索相关性它解决的是“A 和 B 谁更大、大多少”以及“总量怎么被几个部分瓜分”两个问题。当你明确自己要回答这两个问题中的一个时柱状图就是最优先考虑的图。1.2 为什么我推荐把 Matplotlib 吃透Excel 出图表的速度确实快但排版、配色、坐标轴控制非常不可控尤其是当你要把图贴进正式报告时。Origin 在科研绘图里很强大尤其适合处理双 y 轴和误差棒但它的操作方式是 GUI 拖拽不适合自动化产出。ECharts 交互和视觉效果一流可它属于前端技术栈做静态分析时还是需要额外搭环境。Matplotlib 胜在三个地方。第一它是 pandas 和 seaborn 的底层引擎你平时用df.plot()或者sns.barplot()画的图本质都是 Matplotlib 再加工出来的学会底层 API 就等于解锁了所有上层封装。第二它对图形的控制粒度最细从柱子的边缘线宽到每个刻度标签的旋转角度都能改。第三它是完全代码化的改数据、改样式、批量出图都方便跑一次脚本能生成几十张图。后面几个章节的案例我都会用它来演示。1.3 四种形态的选型逻辑很多人一上来就plt.bar(categories, values)只画垂直柱状图但其实不同数据形态有更优的表达方式。先把选型逻辑讲清楚后面写代码才有方向。数据场景推荐形态选型原因类别少3~8 个、数值单一、名称短垂直柱状图最符合阅读习惯数值标签横向排列清晰类别名较长或类别数超过 10 个水平柱状图标签放在左侧不会被挤压横向条长更适合榜单排序需要对比“组内多系列”分组柱状图同一类别的不同系列并排差异一目了然看“总量 内部构成”堆叠柱状图一条柱子同时表达整体规模和组成部分占比这个表看着简单但实际做报表时我见过太多用错形态的图。比如有 24 个产品类别硬要画垂直柱状图x 轴标签全部叠在一起最后只能旋转 90 度勉强显示可读性很差。这种数据直接换成水平柱状图就舒服得多。反过来只有 4 个类别、每类 2 个指标却用两个子图分开画不如一张分组柱状图直观。先思考“我的读者要比较什么”再选形态这才叫会画图。2. 垂直柱状图实战从最小代码到能看的图2.1 最小可用写法与坐标系统我们从一个最简单的例子入手。假设你有四个产品类别的月度销量想比较一下大小代码只需要五行import matplotlib.pyplot as plt categories [服饰, 数码, 家居, 食品] values [120, 235, 180, 275] plt.bar(categories, values) plt.show()这里有个细节新手容易忽略plt.bar的第一个参数传字符串列表时Matplotlib 会自动把类别名映射到 x 轴整数刻度上也就是“服饰”在 0 的位置“数码”在 1 的位置。你并不需要手动构造一个 0 到 3 的数值序列。但要记住这种自动映射只是隐式的一旦你后面要画分组柱状图或者叠加其他图形就必须显式地用x np.arange(len(categories))来管理坐标因为字符串无法参与加减运算。2.2 控制宽度、颜色、边框与透明度plt.bar最常用的四个样式参数是width、color、edgecolor和alpha。默认的width是 0.8如果只有一个系列这个宽度视觉上很舒适柱子之间留了 0.2 的空隙。一旦你要画分组柱状图每根柱子的宽度基本要缩小到 0.35 左右对应关系我在第 4 章详细讲。color参数最灵活它可以是一个单一颜色字符串也可以是一个颜色列表列表长度必须和类别数一致。举个例子colors [#4C72B0, #55A868, #C44E52, #8172B2] plt.bar(categories, values, colorcolors, edgecolorwhite, linewidth0.8)edgecolor和linewidth是我建议在正式图表里一定要设置的参数。给柱子加一个浅色边框能让柱子之间的边界更清晰尤其在打印黑白版报告时浅色柱子如果没有边框很容易糊成一片。alpha是透明度参数我要提醒一个坑如果你并不是故意要做叠加效果尽量把alpha设为 1 或者干脆不设。因为当柱子之间发生重叠时半透明会让重叠区域颜色变深读者会误以为那是第三种颜色或者阴影。2.3 柱顶数值标注的正确姿势画柱状图不标注数值等于白画。人眼虽然能比较长度差异但要准确读 274 和 268 的区别还是很吃力的。最通用的标注方法是plt.textfor x, v in zip(categories, values): plt.text(x, v 5, str(v), hacenter, vabottom, fontsize10)这里的x是类别对应的数值坐标v 5表示在柱顶上方留出 5 个单位的空隙。hacenter让文字水平居中于柱子的中轴线vabottom让文字的底部对齐在指定位置这样文字就会整体悬浮在柱顶上方不会被柱子“吃掉”。偏移量 5 不是固定值要根据你的数据量级调整。如果数据是几千上万偏移 5 等于没偏移文字会压在柱顶边缘上。这一步没有技术难度但直接影响图表专业度。3. 水平柱状图长标签与排行榜的最优解3.1 barh 基本用法与坐标差异水平柱状图的函数是plt.barh参数形态和plt.bar基本对称plt.barh(categories, values)它的好处非常直接类别标签放在 y 轴左侧每个标签各自占据一行无论名称多长都不会和别的标签相互挤压。我处理过一个 15 个省份的消费数据那些省份名称全是四个字画垂直柱状图时标签旋转 45 度也是叠着的换成水平柱状图后所有问题都消失了。需要特别注意的是barh的数值标注逻辑不一样。垂直图里我们改y坐标和va水平图里则要改x坐标和hafor y, v in enumerate(values): plt.text(v 5, y, str(v), vacenter, haleft, fontsize10)这里的y是类别所在的纵坐标v 5是条形的末端位置再往外偏移 5 个单位vacenter让文字在垂直方向上居中haleft让文字从偏移点往右排列。很多新手画水平图时还沿用垂直图那套hacenter, vabottom结果文字全部叠在柱子内部这属于最常见的低级错误。3.2 让数据“开口说话”排序与倒轴水平柱状图最经典的用法是排行榜。但默认情况下Matplotlib 会按数组顺序从下往上画如果你希望最大值出现在最上方必须做两件事原始数据降序排序、y 轴从上到下排布。import matplotlib.pyplot as plt data [(服饰, 120), (数码, 235), (家居, 180), (食品, 275)] data.sort(keylambda x: x[1], reverseTrue) names [item[0] for item in data] values [item[1] for item in data] fig, ax plt.subplots(figsize(8, 4.5)) ax.barh(names, values, color#4472C4) ax.invert_yaxis() # 让第一个类别出现在顶部 for y, v in enumerate(values): ax.text(v 5, y, str(v), vacenter, haleft, fontsize10) plt.tight_layout() plt.show()ax.invert_yaxis()这行至关重要。Matplotlib 默认的 y 轴是数值从小到大的方向也就是第一个数据画在最底部。做 Top 榜单一律要倒轴否则读者第一眼看到的是最小的类别。排序时我用reverseTrue让最大的排在列表第一个再配合倒轴正好呈现“从上往下递减”的阅读节奏。3.3 实战案例产品线 Top 10结合上面所有技巧我做一个完整的 Top 10 产品线营收排名图。这种图在实际业务周报里非常常见完整代码可以直接抄import matplotlib.pyplot as plt products [智能手表, 蓝牙耳机, 便携充电器, 智能音箱, 机械键盘, 显示器支架, 无线鼠标, 桌面台灯, 摄像头, 笔记本支架] revenue [532, 468, 391, 342, 318, 276, 252, 231, 188, 167] data sorted(zip(revenue, products), reverseTrue) rev_sorted [r for r, _ in data] prod_sorted [p for _, p in data] fig, ax plt.subplots(figsize(9, 6)) bars ax.barh(prod_sorted, rev_sorted, color#5B9BD5, edgecolorwhite) ax.invert_yaxis() for bar in bars: ax.text(bar.get_width() 8, bar.get_y() bar.get_height() / 2, f{bar.get_width()} 万元, vacenter, haleft, fontsize9) ax.set_xlabel(月度营收万元) ax.set_title(Top 10 产品线月度营收排名, fontweightbold) ax.set_xlim(0, 600) plt.tight_layout() plt.show()这里我用了一个小技巧plt.barh返回的bars对象列表每个元素自带get_width()、get_y()、get_height()方法。bar.get_width()得到柱子长度也就是数值bar.get_y()是柱子底边坐标bar.get_height()是柱子高度加一半就是垂直居中位置。用这种方式标注数值不需要反向维护排序后的列表代码更健壮。4. 分组柱状图多系列对比的正确姿势4.1 原理与细节剖解分组柱状图本质上就是在同一个类别刻度上画多根柱子靠 x 坐标的微小偏移把同一类别的柱子并排错开。比如类别“服饰”在 x0 的位置两个系列分别放在 x-0.2 和 x0.2 的位置。只要偏移量控制得当柱子就不会重叠也不会离得太远。实现方案非常固定第一步是构造数值坐标点第二步是偏移画柱第三步是重设刻度标签。import numpy as np x np.arange(len(categories)) width 0.35 plt.bar(x - width / 2, values_a, widthwidth, label系列 A) plt.bar(x width / 2, values_b, widthwidth, label系列 B) plt.xticks(ticksx, labelscategories)最核心的是x - width/2和x width/2。因为每根柱子宽度是width中心点向左右各偏移width/2刚好让两根柱子的边缘接触或者保留少量空隙。如果你用的是离散索引而不是 numpy 数组也完全可以for i, (va, vb) in enumerate(...)效果一样。4.2 完整可复现的分组柱状图我写一个接近真实业务场景的例子某公司三个销售区域、两个季度的销售额对比。两个系列用分组柱状图加上图例、数值标注、网格线让整个图直接到可交付的级别import numpy as np import matplotlib.pyplot as plt regions [华东, 华北, 华南] q1 [420, 510, 365] q2 [480, 470, 420] x np.arange(len(regions)) width 0.3 fig, ax plt.subplots(figsize(8, 5)) bars1 ax.bar(x - width / 2, q1, widthwidth, label第一季度, color#4472C4, edgecolorwhite) bars2 ax.bar(x width / 2, q2, widthwidth, label第二季度, color#ED7D31, edgecolorwhite) for bars in (bars1, bars2): for bar in bars: height bar.get_height() ax.text(bar.get_x() bar.get_width() / 2, height 10, str(height), hacenter, vabottom, fontsize10) ax.set_xticks(x) ax.set_xticklabels(regions, fontsize12) ax.set_ylabel(销售额万元) ax.set_title(各区域季度销售额对比, fontweightbold) ax.legend() ax.grid(axisy, linestyle--, alpha0.6) ax.spines[top].set_visible(False) ax.spines[right].set_visible(False) plt.tight_layout() plt.show()细看这段代码bar.get_x()返回柱子左边 x 坐标bar.get_width()是宽度两者相加再除以 2就是柱子中心的 x 坐标。数值标注就居中落在柱顶。ax.grid(axisy, ...)只显示水平网格线能让读者快速对照柱高和数值轴刻度同时避免竖线干扰阅读。隐藏上边框和右边框是当前主流图表风格视觉上更干净。4.3 三个高频出错点排查第一plt.xticks(ticksx, labelsregions)里的ticks和labels必须成对使用。只写_ plt.xticks(x)而不传入labelsx 轴会显示 0、1、2 而非“华东、华北、华南”。第二偏移量不能写成x - width那样两个系列的中心距离是一根柱子的整宽边缘刚好相切一旦柱子宽度略大就会重叠。最稳妥的偏移永远是width/2。第三图例顺序和bar调用顺序保持一致这个看起来幼稚的问题在系列超过三个时特别容易翻车因为 Matplotlib 的记录顺序是代码调用顺序不是你想展示的顺序。5. 堆叠柱状图总量与构成一图兼得5.1 从相面到堆叠bottom 参数很多场景里我们不仅要看总量还要知道总量是怎么由各部分构成的。比如这个月的总营收是 500 万但其中线上和线下各占多少这时候垂直柱状图和分组柱状图都不合适因为它们把总量拆成了多根柱子读者要心算才能加起来。堆叠柱状图把多个系列按顺序垒在同一根柱子里一眼就能读出总量和每一段的大小。实现方法是bottom参数import matplotlib.pyplot as plt import numpy as np x np.arange(4) online [30, 42, 28, 50] offline [20, 35, 32, 25] plt.bar(x, online, label线上, color#4472C4) plt.bar(x, offline, bottomonline, label线下, color#ED7D31)第二个plt.bar加上了bottomonline意思是第二段从online的高度开始往上画而不是从 0 开始。这是堆叠图与普通柱状图唯一的本质区别。如果你漏掉bottom两个系列会完全重叠后画的盖住先画的画面效果惨不忍睹。我最初上手时踩过这个坑排查了半天才发现是bottom没传。5.2 百分比堆叠把绝对数换算成构成比当堆叠图的每根柱子总量差异很大时直接比较各段的绝对值没有意义更合适的做法是转换百分比堆叠图。每一组都归一化到 100观众只看构成比例的差异不看总量。实现也很简单画图前先对每个 x 位置归一化total online offline online_pct [o / t * 100 for o, t in zip(online, total)] offline_pct [o / t * 100 for o, t in zip(offline, total)] fig, ax plt.subplots(figsize(8, 4.5)) ax.bar(x, online_pct, label线上占比, color#4472C4) ax.bar(x, offline_pct, bottomonline_pct, label线下占比, color#ED7D31) ax.set_xticks(x) ax.set_xticklabels([一月, 二月, 三月, 四月]) ax.set_ylabel(占比%) ax.set_ylim(0, 100) ax.legend()注意set_ylim(0, 100)是必须的。因为每根柱子都凑成 100%如果不固定 y 轴范围Matplotlib 可能会自动扩展出多余空间柱顶留白多100% 的刻度对应不到柱顶占比的说法就不成立。百分比堆叠图最适合分析“产品结构变化”比如每个月的线上/线下构成是否在逐步调整。5.3 双 y 轴场景的避坑方案很多业务报表需要同时展示“营业额柱状”和“增长率折线”自然就引出双 y 轴。热词里有人问 Origin 双 y 轴柱状图重叠怎么处理这个问题在 Matplotlib 里同样存在而且更隐蔽。常规写法是这样第一个ax画柱状图第二个ax.twinx()画折线。结果经常出现的问题是柱状图柱宽过大把折线区域完全挡住或者两个 y 轴刻度范围设置不当折线被压在图底部。我的处理经验有三条。第一柱宽控制到 0.4 以内给折线腾出空间第二第二个 y 轴也从 0 开始不要自作聪明地从一个接近最小值的数开始那会放大增长率波动幅度误导读者第三尽量让柱状图的主轴管理大数值营业额折线副轴管理百分比两个轴用不同颜色并在图例中明确对应关系。如果数据本身有 20 个类别以上我不建议再做堆叠柱 折线组合信息载荷太高正确方案是把柱状图和折线图拆成上下两个子图共享 x 轴视线可以上下对齐。6. 进阶美学与导出颜色、动画与交互6.1 配色方案从默认色到风格化Matplotlib 默认的柱状图颜色是Tab10循环色就是那个蓝色、橙色、绿色、红色轮流来的方案。说实话它辨识度还行但在正式报告里显得有点“出厂设置”感。我用得最多的是两套方案。第一套是单色系渐变适合表达“同一指标不同数值”的大小关系。比如你想突出数值越大的柱子颜色越深import matplotlib.pyplot as plt import numpy as np values [12, 35, 18, 27] cmap plt.cm.Blues colors [cmap(0.5 0.5 * v / max(values)) for v in values]这里用plt.cm.Blues这个 colormap 对象v / max(values)把数值归一化到 0 到 1再通过0.4 0.6 * ...控制映射范围避开了最浅的颜色整体看起来既渐变又有区分度。第二套是多类别专用色固定用一组手挑的十六进制色值比如[#4C72B0, #55A868, #C44E52, #8172B2, #CCB974]这是 seaborn 经典配色deep的默认色值观感比 Tab10 稳定很多。有朋友问我 ECharts 柱状图里那种单根柱子的渐变色怎么在 Matplotlib 里实现。坦白说Matplotlib 原生没有一个参数能像 ECharts 那样直接写渐变。如果你非要实现类似效果一个可行方案是逐柱用渐变色阶把一根柱子细分成许多小矩形每个矩形按 colormap 从浅到深填色。代码如下from matplotlib.patches import Rectangle fig, ax plt.subplots(figsize(7, 4)) for i, v in enumerate(values): for j in range(50): frac j / 50 color plt.cm.Reds(0.6 0.4 * frac) ax.add_patch(Rectangle((i - 0.4, v * frac), 0.8, v / 50, colorcolor)) ax.set_xlim(-0.5, 3.5) ax.set_ylim(0, max(values) * 1.1)看起来是渐变了但代价是图形对象数量暴增几百个图一多性能明显下降。所以我的建议是业务场景下不要纠结渐变用深浅色阶映射就够了真需要炫酷视觉效果时把 Matplotlib 生成的图导出到 ECharts 里重新配样式分工合作才是高效路径。6.2 保存高清图与动画 GIF保存图片是我见被问得最多的问题之一。plt.show()弹窗截图再粘贴分辨率永远不够。正确做法是savefigplt.savefig(bar_chart.png, dpi300, bbox_inchestight, facecolorwhite)dpi300是印刷级清晰度bbox_inchestight会自动裁剪空白边让标签和图题都完整落在图内。facecolorwhite是防坑关键当你使用深色主题或者无边框窗口时默认保存的 PNG 背景可能是透明的贴到 Word 里会露底。加上facecolorwhite就确保白底输出。再看动画 GIF。热词里有人想“保存动画伟 gif”应该是想要柱状图逐帧增长的效果。用 Matplotlib 的FuncAnimation可以很轻松实现from matplotlib.animation import FuncAnimation fig, ax plt.subplots() categories [A, B, C, D] final_values [12, 35, 18, 27] bars ax.bar(categories, [0] * len(categories), color#4472C4) def animate(frame): frac frame / 50 heights [v * frac for v in final_values] for bar, h in zip(bars, heights): bar.set_height(h) ax.set_ylim(0, max(final_values) * 1.15) return bars anim FuncAnimation(fig, animate, frames50, interval50, blitFalse) anim.save(bar_growth.gif, writerpillow, dpi120)有三个坑我要提前帮你避开。第一保存 GIF 依赖 Pillow如果提示ModuleNotFoundError先执行pip install pillow。第二frames50代表 50 帧动画每帧 50 毫秒整个动画大约 2.5 秒帧数再高文件体积会成倍增长不建议超过 100 帧。第三blitFalse虽然绘图效率低一点但能避免绘制结果异常对于这种柱高变化的动画闭眼用blitFalse最稳。6.3 鼠标悬停显示数值交互小技巧有人问“鼠标点哪儿在哪儿显示柱状图”我理解是想做一个交互特效鼠标移到柱子上自动把柱子的数值显示出来。Matplotlib 本身是静态库但要实现这种悬停交互并不难有两种思路。第一种是安装mplcursors代码量最少import mplcursors fig, ax plt.subplots() bars ax.bar(categories, values) mplcursors.cursor(hoverTrue) plt.show()只要这一行mplcursors.cursor(hoverTrue)鼠标移动到柱子上就会自动弹出数值标签不需要写任何事件代码。mplcursors 是个独立库pip install mplcursors装一下就行。第二种是完全手工实现用 Matplotlib 的事件机制fig, ax plt.subplots() bars ax.bar(categories, values) annotation ax.annotate(, xy(0, 0), xytext(0, 15), textcoordsoffset points, hacenter, bboxdict(boxstyleround,pad0.3, fc#FFFFCC)) annotation.set_visible(False) def on_move(event): annotation.set_visible(False) if event.inaxes is ax: for bar in bars: bbox bar.get_bbox_patch().get_bbox() if bbox.contains(event.x, event.y): annotation.xy (bar.get_x() bar.get_width() / 2, bar.get_height()) annotation.set_text(str(bar.get_height())) annotation.set_visible(True) break fig.canvas.draw_idle() fig.canvas.mpl_connect(motion_notify_event, on_move) plt.show()核心是bbox.contains(event.x, event.y)判断鼠标坐标是否落在当前柱子内部。需要一个annotation对象作为可复用的标签每次移动先隐藏再判断是否命中柱子命中就设置文本内容。这种方法不依赖第三方库适合你不想额外引入依赖的场景。6.4 与其他工具的联动与取舍其实很多时候开发者的真实场景是混合的。Excel 和 Origin 的数据处理顺手ECharts 的交互效果好Matplotlib 则更适合 Python 数据流水线。上面提到 ECharts 柱状图渐变色你要是熟悉前端完全可以在 Matplotlib 里导出数据、再到 ECharts 里画渐变色柱状图。反过来如果团队已经在用 ECharts 做前端看板那后端用 Matplotlib 做静态截图需求时就沿用同一份数据源保持口径一致。我个人的工作流是探索分析阶段用 pandas 快速df.plot.bar()看分布正式出图阶段回到 Matplotlib 手工控制每一个细节需要交付到网页看板时导出数据给前端 ECharts 重绘。三套工具不冲突但只有 Matplotlib 这套底层 API 是跨场景通用的。7. 常见问题与排查技巧实录7.1 中文乱码与负号显示这是本地化使用最经典的问题。默认 Matplotlib 字体不包含中文字形你写plt.bar([苹果, 华为], ...)时x 轴标签变成一个个小方块。解决方案plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei, Arial Unicode MS] plt.rcParams[axes.unicode_minus] False第一行指定中文字体第二行解决坐标轴负号在部分中文字体下显示成方块的问题。SimHei在 Windows 下可用macOS 用Arial Unicode MS或者PingFang SC。更稳健的做法是在画图前直接把这两行放到一个setup_chinese.py模块里每个图表脚本 import 一下避免每段代码重复。注意font.sans-serif需要设置为一个字体列表Matplotlib 会按顺序查找第一个可用的字体。如果你安装了多套中文字体请把最想用的放在第一位。7.2 柱子太粗、太挤、间距不对柱子间距问题的根源几乎都是width和坐标系统的不匹配。单个系列默认width0.8视觉上舒服分组图必须缩小到0.35左右堆叠图如果只有两个系列width可以保持 0.6 到 0.8。如果 8 个以上类别的垂直柱状图还是觉得挤优先调整figsize(12, 5)把画布拉宽而不是把width缩到 0.2 以下后者会让柱子变成细线对比效果很差。7.3 堆叠图系列多时颜色区分度差堆叠图一旦超过四个系列用默认颜色列表会出现相邻两个系列颜色相近的情况。我建议直接使用 colormap 生成一组间隔均匀的颜色import numpy as np import matplotlib.pyplot as plt cmap plt.cm.viridis(np.linspace(0.2, 0.9, 5)) labels [A, B, C, D, E] data [np.random.randint(10, 50, size6) for _ in range(5)] for i, (d, c, lab) in enumerate(zip(data, cmap, labels)): if i 0: plt.bar(range(6), d, labellab, colorc) else: plt.bar(range(6), d, bottomnp.sum(data[:i], axis0), labellab, colorc)np.linspace(0.2, 0.9, 5)从 viridis 色带中间取 5 个颜色避免取到最浅的端点导致白底上看不见。我之前就因为默认色表中间两个颜色太接近做完的堆叠图连自己都要仔细分辨。7.4 刻度标签旋转与对齐类别名太长时常见操作是旋转标签但不配合对齐参数反而更乱。正确做法plt.xticks(rotation45, haright)rotation45让文字逆时针旋转 45 度haright让文字右对齐这样标签会沿着刻度点斜向排列不会互相遮挡。很多新手只写了rotation结果每个标签以自己左下角为轴旋转看起来歪七扭八。如果需要多列标签自动换行可以用 Python 的textwrap.fill把字符串按逗号或空格切成两行再作为标签传入。7.5 PNG 图导出后边缘被裁切savefig之后发现图例或者标题被裁掉了几乎所有情况都是因为没加bbox_inchestight。这个参数会让 Matplotlib 自动计算所有图形元素的实际边界把画布扩展到内容区域而不是使用默认的固定边距。如果加了tight还是被裁通常是你手动设置了fig.subplots_adjust(left0.1, ...)这些参数和tight冲突了优先检查plt.tight_layout()是否与bbox_inchestight同时使用。最后分享一个我自己的习惯画完任何柱状图我都会把返回值存到变量里比如bars ax.bar(...)。这不仅是为了方便调用get_height()做标注也是因为后面要加一条趋势线、改一次颜色、或者做动画时你会需要这些柱子的完整数据。还有一个很实用的小技巧如果要对已有的柱状图批量改样式直接遍历ax.patches就行for patch in ax.patches: patch.set_color(...)可以一次性改掉所有柱子颜色。这个属性在我做批量图表时省了很多时间希望也能帮到你。