尧图精选

Python Matplotlib 画图实战:中文、刻度与论文出图

🕒 发布时间:2026/9/17 13:41:01 📁 来源:尧图网络
简介面向Python初学者与需要快速上手数据可视化的开发者这份速查资料以单份PDF文档呈现适合在软件开发中制作图表、撰写分析报告时随手翻阅。文档围绕绘图模块的基本用法展开从最简单的折线绘制、坐标轴标签设置与图形显示到用简写字符串指定点形与颜色、限定坐标轴取值范围再借助数组运算在同一张图上叠加多条曲线并通过属性设置函数统一调整颜色与线宽最后用子图命令划分多面板布局同时演示图中标注文字、绘制直方图、添加网格等常见做法便于读者对照练习。全包仅1个PDF文件约217KB体量轻便适合离线保存与打印。目前已有155人学习浏览可作为入门阶段查阅常用接口、巩固绘图思路的参考清单。1. 图纸有了代码还没写matplotlib 画图解决的其实是“改得动”同事丢过来一份《pythonmatplotlib画图[参照].pdf》里面是几张样例图一条趋势折线、一组散点、一张带双纵轴的对比图。要求三天后交同样的图数据换成新一版。手绘一次不难难的是数据一动就得从头再来这正是 Excel 和桌面画图软件最别扭的地方——图是“死”的改一次数据就重排一次版式。matplotlib 把图变成一段可复现的 python 代码换掉输入数组坐标轴范围、图例、字体、线宽全部跟着走同一份脚本批量生成几十张论文画图或数学建模画图都不成问题。学习曲线集中在两处一是 matplotlib 的对象模型二是中文、刻度、布局这些看起来不起眼但天天卡人的细节。下面按“能跑起来、画得对、改得动、出得去”的顺序把常用做法和参数挨个抠一遍。2. python matplotlib 画图的最小可复现路径2.1 python 安装、matplotlib 安装与编辑器解释器选择画不出图的人九成不是代码写错了而是装了两个 pythonpip 装进 A编辑器跑的是 B。先确认当前解释器身份再决定往哪装。用虚拟环境把依赖锁在项目里是后面所有步骤的前提。# 确认当前解释器版本与路径 python --version python -c import sys; print(sys.executable) # 建虚拟环境并激活 python -m venv .venv # Windows .venv\Scripts\activate # macOS / Linux source .venv/bin/activate # 升级 pip再装画图三件套 python -m pip install --upgrade pip python -m pip install matplotlib pandas numpypython -m venv .venv建出隔离目录避免污染系统解释器激活后坚持用python -m pip而不是裸pip这样安装目标一定是当前解释器。国内网络拉包慢在 pip 命令后追加镜像源参数即可不必改全局配置。装完立刻验证python -c import matplotlib; print(matplotlib.__version__, matplotlib.get_backend())后端的名字会打出来这个信息在第 2.2 节会用到。编辑器侧VSCode 装好 python 扩展后按CtrlShiftP选 “Python: Select Interpreter”指向.venv里的解释器PyCharm 在 Settings → Project → Python Interpreter 里添加已存在的虚拟环境。vscode python 环境配置和 pycharm 配置 python 环境踩的坑几乎一样右下角显示的解释器和你终端里激活的不是同一个。pandas numpy matplotlib scikit-learn这一套通常一起装做 python 数据分析与可视化时省得来回补依赖。2.2 后端backend与 plt.show() 不弹窗的关系脚本没报错窗口却没出来或者本地好好的扔到服务器上就报一串 TclError。这是后端在工作。后端决定 matplotlib 把图画到哪儿去画到屏幕窗口、画到内存还是画进 notebook 单元格。import matplotlib # 先看一眼当前用的是什么后端 print(matplotlib.get_backend()) # 无图形界面的环境服务器、CI、Docker只出文件不弹窗 # 注意必须在 import pyplot 之前调用 matplotlib.use(Agg) import matplotlib.pyplot as pltmatplotlib.use()一旦 pyplot 被导入后再调用部分后端已经初始化切换会失效甚至直接抛错所以它必须写在最前面。用 Agg 之后plt.show()不会有任何反应出图只能靠savefig()这不是 bug。后端典型场景show() 行为TkAgg本地桌面标准 python 安装弹出窗口QtAgg本地桌面装了 Qt 绑定弹出窗口交互更顺Agg服务器 / 批量出图不弹窗只能存文件inline 后端Jupyter Notebook直接显示在单元格下方Jupyter 里不用手动切单元格首行写%matplotlib inline即可想要可缩放、带坐标读数的图就换成%matplotlib widget前提是装了 ipympl。2.3 折线图与散点图的最小可运行代码把两张最常见的图放一起写顺便交代几个必然会用到的参数。import numpy as np import matplotlib.pyplot as plt # 生成横坐标与两条曲线 x np.linspace(0, 2 * np.pi, 200) y np.sin(x) # figsize 单位是英寸dpi 决定屏幕像素密度 fig, ax plt.subplots(figsize(6.4, 4.0), dpi120) ax.plot(x, y, labelsin(x), color#2b6cb0, linewidth1.8) # 每隔 20 个点采一个散点zorder 保证散点压在线条上方 ax.scatter(x[::20], y[::20], s18, color#e53e3e, zorder3) ax.set_xlabel(x / rad) ax.set_ylabel(amplitude) ax.set_title(sin(x) 采样示意) ax.legend(locupper right) fig.savefig(sin.png, bbox_inchestight) plt.show()figsize(6.4, 4.0)是画布宽高单位英寸乘上dpi就是最终像素linewidth控制线宽论文里通常 1.52.0 之间s是散点面积不是直径几十的量级就比较克制zorder决定绘制层级数值越大越靠上bbox_inchestight在保存时裁掉画布四周多余白边这一步几乎每次都要加否则导出的图边缘会留一大圈空白。2.4 pyplot 状态机与面向对象写法的取舍入门教程大多从plt.plot()讲起正式项目里更常见fig, ax plt.subplots()。两者不是新旧关系而是适用场景不同。写法典型 API适用场景风险pyplot 状态机plt.plot/plt.title一次性脚本、快速验证多子图或多图叠加时状态互相干扰面向对象fig, ax plt.subplots()子图、函数封装、批量出图代码行数略多判断标准很简单这段画图逻辑要不要被包进函数、要不要循环调用、要不要多个子图。只要沾上一条就用面向对象写法。ax上的方法是显式的fig负责整体布局和保存ax负责一个坐标系里的所有元素边界清晰排查问题也快。提示函数里画图时把fig和ax都返回出去让调用方决定savefig还是show。函数内部直接plt.show()的代码在服务器上跑必炸。3. 标题、坐标轴名称与图例一张 matplotlib 图的可读性配置3.1 set_title / set_xlabel / set_ylabel 的常用参数图能不能看懂一半靠数据一半靠标注。标题和轴标签有几个参数几乎每次都要调。fig, ax plt.subplots(figsize(7, 4.2), dpi120) ax.plot(t, temp, label室内) ax.set_title(一天内的温度变化, fontsize15, pad12, fontweightbold) ax.set_xlabel(时间 t / h, fontsize12, labelpad6) ax.set_ylabel(温度 T / ℃, fontsize12, labelpad6)fontsize会覆盖 rcParams 里的全局字号只对这一个元素生效pad是标题到绘图区上边缘的距离单位是点图很挤的时候把它调小比压缩画布更划算labelpad是轴标签到刻度文字的距离中文上下结构的字容易被刻度压住加 48 个点通常就干净了。set_ylabel还支持loctop把纵轴标签挪到轴顶端这是很多期刊的排版习惯。多子图场景下ax.set_title只管自己那一格。整张图的总标题要用fig.suptitle(...)它属于 figure 级别配合fig.subplots_adjust(top0.85)给它腾出空间否则总标题会和子图标题叠在一起。3.2 legend 的位置、列数与框线图例空着、图例挡住数据、图例跑到画布外面是三个高频抱怨原因都指向同一处图例只认带label的元素。ax.legend( title传感器, # 图例组标题 locupper left, # 九宫格锚点 ncol2, # 横向排列两列 frameonFalse, # 去掉外框论文排版常用 fontsize10, handlelength1.8, # 图例中线段样例的长度 )loc取upper left/lower right这类九宫格关键词时图例会贴着对应角。九宫格位置都不满意就用bbox_to_anchor(x, y)配合loc精确定位坐标默认是坐标轴的归一化值0 到 1加bbox_transformfig.transFigure就切成整张画布的归一化坐标。图例太长时用ncol拆列比调字号更有效。注意调用legend()之后才给线条补label图例不会更新。正确顺序是先plot(..., labelxx)再legend()。3.3 用 rcParams 统一管理字号、网格与刻度一个项目里几十张图靠每张单独设字号迟早不一致。把公共样式提到 rcParams 里一处改动全图生效。import matplotlib.pyplot as plt BASE { font.size: 12, axes.titlesize: 14, axes.labelsize: 12, xtick.labelsize: 11, ytick.labelsize: 11, legend.fontsize: 11, axes.grid: True, grid.linestyle: :, grid.alpha: 0.4, figure.dpi: 120, } plt.rcParams.update(BASE)rcParams是一份全局字典update只影响之后新建的 figure 和 axes已经创建的对象不会追溯生效所以这段代码要放在所有画图逻辑之前。只希望在局部换风格用rc_context包一段退出后自动还原不会污染后面的图with plt.rc_context({axes.grid: False, font.size: 14}): fig, ax plt.subplots() ax.plot(x, y) # 这一张无网格、字号 14出了 with 就恢复原样3.4 常用配置项速查rcParams 键含义建议值font.size全局默认字号1012axes.titlesize子图标题字号比全局大 2 号axes.labelsize轴标签字号与全局一致axes.grid默认是否画网格开启并配grid.alphaaxes.unicode_minus是否使用 Unicode 负号中文环境设Falsefigure.dpi屏幕显示分辨率100150savefig.dpi保存分辨率论文 300 以上网格线要克制alpha在 0.30.5 之间既起参考作用又不抢数据。双纵轴图里两条轴线颜色和刻度颜色最好跟着曲线走避免看错归属。4. plt 画图显示中文、横坐标太密集与对数坐标轴的三类硬骨头4.1 plt 画图显示中文问题的定位与修复现象很有辨识度所有中文变成一排方框同时终端刷出UserWarning: Glyph xxx missing from current font。根因是默认字体 DejaVu Sans 不含中文字形matplotlib 找不到就画方块。第一步先看系统里到底有哪些可用字体from matplotlib import font_manager # 列出所有字体名过滤出可能带中文的 names sorted({f.name for f in font_manager.fontManager.ttflist}) print([n for n in names if any(k in n for k in (Hei, YaHei, Song, Noto, CJK, PingFang))])拿到名字后写进候选列表matplotlib 会按顺序取第一个存在的import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei, Noto Sans CJK SC, PingFang SC] plt.rcParams[axes.unicode_minus] Falsefont.sans-serif是列表而非单值跨平台脚本把 Windows、macOS、Linux 上常见的名字都列上哪台机器跑都能命中一个。SimHei 在 Windows 自带macOS 用 PingFang SC 或 Arial Unicode MSLinux 桌面环境一般不预装中文字体需要额外装 Noto Sans CJK或者手动把字体文件注册进去font_manager.fontManager.addfont(/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc) plt.rcParams[font.family] Noto Sans CJK SCaxes.unicode_minus False这行别漏。字体里的 Unicode 减号 U2212 经常缺失负刻度会变成方框设成False后改用 ASCII 连字符渲染能显示但视觉上略短属于可接受折中。提示改了font.sans-serif仍不生效检查是否在设置之前就import pyplot并创建了 figure以及字体缓存是否过期。删掉~/.cache/matplotlib下的字体缓存文件重跑一次通常就好。4.2 python 画图横坐标太密集的四种收口手法时间序列画出来最容易出这个问题横坐标全是01-01 00:00:00这种长标签叠成一团黑。核心思路是减少刻度数量或缩短标签长度两条路可以一起走。import matplotlib.dates as mdates # 主刻度每 3 天一个次刻度每天一个 ax.xaxis.set_major_locator(mdates.DayLocator(interval3)) ax.xaxis.set_major_formatter(mdates.DateFormatter(%m-%d)) ax.xaxis.set_minor_locator(mdates.DayLocator(interval1)) # 自动旋转并右对齐一行顶替手写 rotation 加对齐 fig.autofmt_xdate(rotation45, haright)日期轴用DayLocator/MonthLocator/HourLocator这类专用定位器比统一用数值间隔靠谱跨月跨年不会错位。格式器DateFormatter管显示%m-%d比默认的完整时间戳短得多。数值轴则换成本地定位器from matplotlib.ticker import MultipleLocator, MaxNLocator ax.xaxis.set_major_locator(MultipleLocator(10)) # 固定每 10 一个刻度 ax.xaxis.set_major_locator(MaxNLocator(nbins6)) # 最多 6 个自动挑整数 ax.tick_params(axisx, labelsize9, rotation30) # 兜底缩字号 旋转MultipleLocator(10)是硬性间隔适合量纲规整的数据MaxNLocator(nbins6)让 matplotlib 自己挑整数刻度标签长度不一时更协调。两者都装在同一根轴上会互相覆盖后设的生效别同时用。旋转标签必须配对齐方式只写rotation45不改水平对齐标签尾部会顶到坐标轴上haright才是完整做法。注意日期格式化的前提是 x 轴数据本身是datetime64或Timestamp字符串数组不会被自动解析成时间轴pd.to_datetime()要先转一遍。4.3 matplotlib 对数坐标轴与次刻度网格跨几个数量级的数据必须上对数轴这里默认行为有几个点容易被忽略。fig, ax plt.subplots(figsize(6.5, 4.0)) ax.plot(freq, gain, markero, ms3) ax.set_xscale(log) ax.set_yscale(log) ax.set_xlim(1e-1, 1e4) ax.grid(True, whichboth, linestyle:, alpha0.35) # 主次刻度都画网格 ax.tick_params(whichminor, length2, labelsize9)set_xscale(log)默认底数 10需要底数 2 就写成set_xscale(log, base2)。横纵都取对数等价于快捷函数loglog()只对 x 取对数用semilogx()只对 y 用semilogy()——这三个函数在科学计算可视化 matplotlib 模块里出现频率极高。grid的which参数默认只作用于主刻度加both才会把密集的次刻度网格也画出来视觉参考价值正在这些次刻度的位置。次刻度的标签默认不显示tick_params(whichminor)里给labelsize就能打开。数据里含 0 或负值时对数轴会直接丢弃这些点不报错也不提示只是曲线上少一段。处理办法是按业务含义过滤掉无效值或者整体加一个远小于最小正数的偏移量再取对数。这条规则在双对数坐标下尤其要盯一眼图不会有任何异常提示。4.4 现象与排查对照表现象常见原因处理中文变方框字体不含中文字形设置font.sans-serif候选列表负号变方框字体缺 U2212axes.unicode_minus False横轴标签重叠刻度间隔太小Locator 调间隔 旋转右对齐对数轴少一段曲线数据含 0 或负值过滤、加偏移或改回线性轴保存的图一片空白用了 Agg 仍等 show只调savefig检查路径与文件名图例是空框线条没设labelplot(..., labelxx)后再legend()5. 论文画图与数据分析出图的进阶参数5.1 样式表切换与自建 mplstyle想要整体换风格不必逐项设 rcParams样式表一步到位import matplotlib.pyplot as plt print(sorted(plt.style.available)[:12]) # 看内置样式名 plt.style.use(seaborn-v0_8-whitegrid) # 全局生效 with plt.style.context(ggplot): # 只在 with 内生效 fig, ax plt.subplots() ax.plot(x, y)style.use与rc_context是同一套机制的不同入口前者整段脚本生效后者限定作用域。投稿用的版式建议写成.mplstyle文件plt.style.use(./paper.mplstyle)直接加载内容就是一行一个键: 值可以进版本库团队里谁跑都一样。5.2 GridSpec 布局与共享坐标轴不规则版式用add_gridspec比subplots(2,2)灵活得多横跨、比例不等都能表达fig plt.figure(figsize(7, 5), constrained_layoutTrue) gs fig.add_gridspec(2, 2, height_ratios[2, 1]) ax1 fig.add_subplot(gs[0, :]) # 上排横跨两列 ax2 fig.add_subplot(gs[1, 0]) ax3 fig.add_subplot(gs[1, 1], sharexax2) # 与 ax2 共享 x 轴刻度 ax1.plot(t, y1) ax2.scatter(t, y2, s6) ax3.scatter(t, y3, s6) fig.suptitle(多子图出图, fontsize14)sharex让两块子图共用同一根横轴缩放联动、刻度只显示在下方那一块比手工对齐set_xlim稳得多。constrained_layoutTrue会自动避让标题和标签比tight_layout()更适应带 colorbar 或 suptitle 的复杂版式两者不要同时开。5.3 保存出图dpi、bbox_inches 与矢量格式plt.rcParams[pdf.fonttype] 42 # PDF 内嵌 TrueType 字体子集 fig.savefig(fig1.png, dpi300, bbox_inchestight, facecolorwhite) fig.savefig(fig1.pdf, bbox_inchestight)参数作用建议取值dpi位图分辨率论文 300 起屏幕 100150bbox_inchestight裁掉四周多余白边保存时常态开启facecolor背景色截图混排用白色PPT 叠加可transparentTrueformat输出格式png 位图pdf / svg 矢量pdf.fonttypePDF 字体嵌入方式设 42避免期刊端替换字体位图和矢量的选择看用途屏幕展示、PPT 用 png投稿和打印用 pdf 或 svg放大不糊且体积小。矢量格式的字体这一步最容易被忽略不改pdf.fonttype时部分期刊的预检工具会报字体缺失或嵌入方式不合规改成 42 让字体以 TrueType 子集嵌进文件跨机器打开样式不走样换成 svg 输出时则要确认文字是按文本写入还是已转成路径前者能在 Illustrator 里继续编辑。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →