资讯动态

Matplotlib入门:pyplot接口与figure/axes核心机制详解

发布时间:2026/10/17 6:01:33 来源:尧图企业网站定制
Matplotlib大概是数据分析这条路上绕不开的第一个门槛。装好Python、配好环境之后大多数人写的第一句可视化代码十有八九是import matplotlib.pyplot as plt。这个plt缩写就是今天这篇要盘的主角——pyplot。这篇是Matplotlib系列的第一章不讲什么高深花样就是把pyplot这个入口给你讲明白它凭什么能成为Python绘图的默认选择、底层figure和axes那套关系是怎么回事以及怎么用几条核心指令从零拼出一张能放进论文和汇报里的图。我自己当年从R语言切到Python时最不适应的就是这个pyplot的写法后来真正搞清楚了它背后的工作方式画图效率直接翻倍。这篇内容适合刚接触Python可视化的新手也适合那些已经能跑通代码、但总觉得plt背后是黑盒的读者看完你能获得一套可复用的画图心智模型。1. 先搞清楚pyplot到底是个什么玩意儿1.1 matplotlib是底层引擎pyplot是操作面板很多新人把pyplot当成matplotlib本身这种理解不算全错但不精确。用个生活化的类比matplotlib是一台能印刷各种复杂画面的大机器pyplot只是机器外面那个最常用的操作面板。机器里面还有一堆你平时看不见的部件——后端渲染器负责把图形画到屏幕上或者写入文件Artist层负责管理画布上每个元素的属性线条、文字、坐标轴、图例都是Artist对象而pyplot只是最外面那层脚本接口帮你用最短的代码操纵这一切。matplotlib的整体架构大致分三层Backend层决定图形渲染到哪里是弹出一个窗口比如TkAgg、QtAgg还是直接写入文件比如Agg。Artist层图里每个可见元素都是ArtistFigure、Axes、Line2D、Text、Rectangle都属于这一层。pyplot层由一组函数组成的模块内部维护当前figure和当前axes的全局状态提供极简调用接口。这三层理解到什么程度才算够用对于日常画图的读者至少要知道pyplot是门面Artist是本体Backend负责输出。搞清楚这个顺序许多报错信息你就能第一时间定位到是渲染的问题还是对象设置的问题。pyplot最初的设计大量模仿了MATLAB的绘图接口你会发现很多函数名跟MATLAB几乎一样plot、scatter、bar、hist、legend连r--这种字符串缩写风格都一脉相承。对于老MATLAB用户来说极其亲切对于没接触过MATLAB的人直接记住这些函数的作用就是把数据变成图形上的几何元素就好。1.2 状态机机制plt.plot其实省略了三条指令pyplot内部藏着一个全局状态管理机制你可以把它理解成一个当前正在操作哪个画板的登记本。当你调用plt.plot(x, y)时实际的执行过程大致是检查当前有没有Figure没有就自动创建一个检查当前有没有Axes没有就在Figure上自动创建一个把数据绘制到这个当前Axes上。所以plt.plot(x, y)这一行实际上替你省去了创建画布、创建坐标系、选定当前坐标系这一串步骤。这个设计很聪明尤其在Jupyter Notebook、IPython这类交互环境里边写边看非常舒服几行代码就能出图。代价是状态是隐式的一旦你去画多子图、做复杂布局没有显式指定目标axes就很容易陷入我的线到底画哪儿去了的困惑。我个人的使用习惯是在Jupyter里做数据探索时喜欢用pyplot接口写脚本或者做正式汇报图时改用面向对象接口。两者的差别核心就一句话pyplot接口是对当前axes执行操作的简写面向对象接口是先取到某个axes对象然后告诉它该做什么。后者没有全局状态依赖逻辑更清晰在多个子图之间切换时不容易出错。举个直观的小例子import matplotlib.pyplot as plt # pyplot接口 plt.plot(x, y) # 面向对象接口 fig, ax plt.subplots() ax.plot(x, y)上面两段代码结果几乎一样但第二段里的fig和ax是你能够控制的对象——你可以把它们传给其他函数、存进数据结构里也可以自由组合多张子图。第一段则完全依赖pyplot内部的当前axes记忆简单场景很方便复杂场景下容易让你找不到北。这个关系搞清楚之后matplotlib里百分之九十的困惑都会迎刃而解。2. 第一次Plot It十分钟画出第一张图2.1 从数据到画布figure和axes必须分清楚新手最容易把figure和axes混着用这里一次说清楚。Figure是整个图形窗口你可以理解成一张白纸或者一个页面它负责承载所有内容。Axes是figure里面的一个坐标系区域通俗说就是画图的那一小块方块上面有x轴、y轴、数据曲线、网格、图例这些元素。Axis是Axes内部的坐标轴本身负责提供刻度线和刻度标签。三者是包含关系一个Figure可以包含多个Axes也就是多个子图一个Axes一般有一对Axisx轴和y轴极坐标里叫r轴和theta轴。用plt.subplots(nrows1, ncols2)会创建1行2列两个子图返回结果中fig是一个Figure对象axes是包含两个Axes元素的数组一般用axes[0]、axes[1]来访问。figsize(宽, 高)的单位是英寸dpi是每英寸点数这两个参数直接决定输出图片的物理尺寸和清晰度。比如figsize(8, 4)配上dpi100出来的图就是800x400像素。很多人觉得图片模糊多半是dpi太低或者保存时忘了指定更高分辨率。这里要时刻记住你画线、加标注、调刻度的对象是Axes不是Figure。打个比方Figure是相册Axes才是照片本身plt.plot就是往某张照片上添内容。很多新人拿到示例代码后习惯性写plt.title(...)这类全局调用在只有一个子图时没问题一旦有多个子图标题只能加到最后一个当前的axes上这就是混乱的起点。2.2 画一条正弦曲线看懂plot函数的完整流程光讲概念不画图等于白讲下面是一个完整的画图流程示例。这个示例本质上就对应着标题里那句“Plot It”——别想太多先把图画出来。import numpy as np import matplotlib.pyplot as plt x np.linspace(0, 10, 100) y np.sin(x) fig, ax plt.subplots(figsize(8, 4)) ax.plot(x, y, color#d62728, linewidth2, labelsin(x)) ax.scatter(x[::10], y[::10], s30, color#1f77b4, label采样点) ax.set_xlabel(x) ax.set_ylabel(sin(x)) ax.set_title(正弦曲线示例) ax.legend(locupper right) ax.grid(True, alpha0.3, linestyle--) fig.tight_layout() fig.savefig(sin_plot.png, dpi150) plt.show()逐段拆开来看。np.linspace(0, 10, 100)从0到10均匀生成100个数中间那个100决定了曲线采样密度越大曲线越平滑如果取10画出来就是折线感很强的多边形。ax.plot负责画折线图color可以用#d62728这种十六进制精确指定颜色也可以用r、blue这种内置颜色名。ax.scatter把每隔10个点取一个样本画成散点s控制点的大小这样图上既有平滑曲线又有原始采样点信息量更足。set_xlabel、set_ylabel、set_title都是对axes内部文本元素的设置方法名的前缀set_表示这是一系列对象属性的赋值操作。legend显示图例locupper right把图例放到数据区域的右上角。grid打开网格alpha是透明度取值范围0到1linestyle--让网格线变成虚线这样既有参考线又不至于遮挡主线。后面的fig.tight_layout()会自动调整子图间距防止标题或者轴标签被裁切这个操作在图形元素较多时是刚需。savefig会按照当前figure的状态把图输出到文件所以它必须在所有修改完成之后调用。plt.show()在交互环境里弹出窗口展示在Jupyter里自动内嵌到单元格输出中。顺序上有个经验先savefig再show或者两者只选一个避免窗口阻塞之后保存出白图。2.3 显示与保存每次作图最后一步的讲究plt.show()在交互环境里会弹出一个窗口在Jupyter里自动嵌入输出区但在某些纯脚本环境下它把所有当前figure输出后工作就结束了。有个经典陷阱在脚本里先写plt.show()再写plt.savefig()保存出来的经常是一张白图因为show之后部分后端的figure内容会被清空。正确顺序是永远先保存后展示或者直接把show省略。savefig有几个参数值得记牢。dpi控制输出分辨率学术图片一般150起步投稿可以到300。bbox_inchestight会自动裁掉图片四周多余的空白让图像区域撑满整个文件这个参数我几乎每次都用。transparentTrue可以把背景设为透明适合做PPT配图或者网页贴图。format可以从文件扩展名推断也可以显式指定支持png、pdf、svg、jpg等格式。学术投稿或者需要印刷时优先保存为pdf或svg矢量格式缩放不糊、体积还小如果只是在聊天工具里发预览图png加dpi150就够了。另外在无图形界面的Linux服务器上默认后端可能是Agg它不会弹任何窗口只负责渲染到文件。你运行plt.show()可能什么也不显示但代码并没有错这是环境特性而不是你的失误。想确认当前用的是哪个后端执行print(plt.get_backend())就行。这类问题在比赛训练、远程开发场景里特别常见提前了解能省下不少排查时间。3. 进阶实操把图画到“能见人”的程度3.1 用样式表和rcParams给全图统一调性新手画图最常被吐槽的其实是审美不是功能。matplotlib从3.0以后内置了不少高级风格前提是你选对样式。plt.style.use(seaborn-v0_8)可以整体切换到一个统计图常见风格网格淡雅、配色柔和是很多人出报告的首选。除此之外还有ggplot、fivethirtyeight、bmh这些经典样式每个都有强烈个性比如fivethirtyeight适合做数据新闻风格的图表。样式表本质上就是一组预设好的rcParams。如果你不想整表切换可以单独设置全局参数import matplotlib.pyplot as plt plt.rcParams[font.size] 12 plt.rcParams[axes.grid] True plt.rcParams[grid.alpha] 0.3 plt.rcParams[figure.dpi] 100rcParams是matplotlib配置体系的根基所有绘图元素的默认属性都由它管理。设置之后后续所有图都会继承这些默认值省得每张图重复指定。颜色方面线条类别多的时候推荐plt.cm.tab10配色这是10种类别色的稳定方案数值型渐变推荐viridis它对色盲用户也比较友好。我有个经验同一份报告、同一个项目里的所有图尽量用同一套样式表和同一组配色。比如固定seaborn风格打底主线用#d62728红色辅助线用#1f77b4蓝色每张图都是这个规矩整套PPT看起来就会很统一。相反每张图都换一种颜色体系哪怕单张都好看拼在一起也很杂乱。3.2 中文乱码与负号显示彻底解决不再复发中文乱码是中文用户最头疼的问题没有之一。原因其实很简单matplotlib默认的英文字体一般是DejaVu Sans里没有中文字形碰到中文字符就只能显示成方块。解决方案分三步确认本机存在一款中文字体比如Windows上常见的SimHei黑体、Microsoft YaHei微软雅黑、KaiTi楷体macOS上常见PingFang SC、Arial Unicode MSLinux上常见Noto Sans CJK SC、WenQuanYi Micro Hei。告诉matplotlib按顺序去哪些字体里找中文字形设置plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei]。同时设置plt.rcParams[axes.unicode_minus] False否则图上的负号可能显示成一个奇怪的方块。很多人只改了第2步就以为万事大吉结果正文好了、负号还是乱码这个细节值得单独划重点。标准配置可以当作项目模板放在代码最上方import matplotlib as mpl mpl.rcParams[font.sans-serif] [SimHei, Microsoft YaHei] mpl.rcParams[axes.unicode_minus] False如果服务器上没有图形界面、也无法安装系统字体可以用一个比较笨但可靠的办法把TTF字体文件放到和脚本相同的目录下作图前用matplotlib.font_manager里的fontManager.addfont()方法注册字体然后再重新设置rcParams。这个路径不依赖系统字体安装权限受限的集群上也适用。还有一个排查技巧matplotlib在字体名字写错时不会报错而是静默回退到默认字体所以很多人明明配了却“没生效”八成就是字体名和系统字体列表对不上。验证方法很简单from matplotlib import font_manager fonts {f.name for f in font_manager.fontManager.ttflist} print(SimHei in fonts)先确认True再画中文图基本不会再出乱码。3.3 图例、网格、注释让图自己会说话一张好图不在于特效多而在于信息传达效率。图例legend、网格grid、标注annotate是三个最常用的提效工具。图例的常用参数有loc位置、ncol列数、frameon是否带边框、fontsize字号、title图例标题。如果图例总是挡住数据可以设locbestmatplotlib会自动选择遮挡最少的位置。图例项太多时建议设ncol2横向排两列比竖向一长串更省空间。网格线的设置是ax.grid(True, alpha0.3, linestyle--, whichmajor)。which决定网格线作用在主刻度还是次刻度折线图开浅色网格能有效提升读数精度但透明度在0.2到0.4之间比较合适太深会盖过主线。annotate用来在图上做标注最常见的场景是标记极值点ax.annotate( 峰值, xy(np.pi / 2, 1), xytext(np.pi / 2 0.5, 1.1), arrowpropsdict(arrowstyle-, colorgray) )xy是箭头指向的数据坐标xytext是文字所在位置arrowprops指定箭头样式。在汇报图上用这个功能直接圈出关键现象比在PPT里二次加工省太多事。折线图加一个极值标注、柱状图加一组数值标签图的表达力立刻上一个档次但要注意别滥用一张图超过三四个标注就会显得拥挤。4. 常用图表类型与选型思路4.1 折线图、散点图、柱状图、直方图怎么选不纠结pyplot里画图基本是一行函数的事plot、scatter、bar、hist、boxplot。但“会调函数”和“会选图表”是两码事。我提供一个朴素的判断框架——先问自己这张图要回答什么问题想看随时间或连续过程的走势变化折线图plot想看两个数值变量的关系、聚类、离群点散点图scatter想对比几种类别之间的数值大小柱状图bar想统计单个数值变量的分布形态直方图hist或核密度图想看不同组的分布区间和离群情况箱线图boxplot图表类型适用场景典型变量构成常用参数折线图 plot时间/连续序列趋势x数值连续y数值连续marker、linewidth、label散点图 scatter变量间相关/聚集x数值y数值s、c、alpha、cmap柱状图 bar类别数值对比x类别y数值width、bottom、color直方图 hist数值分布形态x数值按区间分组bins、density、cumulative箱线图 boxplot分布与异常值类别可选数值notch、patch_artist这里有一个非常常见的误区把柱状图拿来画时间序列。比如某个月份的销量数据用柱状图表达“每月之间的对比”是恰当的因为横轴本质是离散类别但如果你想表达多个连续时间点的上升下降趋势折线图更能体现变化方向。判断标准就一句话横轴是连续数值还是离散类别。连续趋势用折线离散对比用柱状逻辑永远不会打架。4.2 多子图布局subplots的灵活用法当一张图表达不完所有信息时多子图就是刚需。最常用的入口是plt.subplots(nrows, ncols)fig, axes plt.subplots(1, 3, figsize(12, 4), shareyTrue) axes[0].plot(x, y1) axes[1].plot(x, y2) axes[2].plot(x, y3) fig.suptitle(三张子图对比)shareyTrue让三个子图共享同一个y轴刻度范围做对比图时视觉基准统一这是对比图的精髓。如果各子图y轴范围不一致同样的上升趋势会被各自缩放“骗”过去看着都是抖一抖实际量级差很远。非均匀布局用GridSpec会更顺手。它支持width_ratios和height_ratios参数调整各子图的宽高权重比如让左侧图是右侧图的两倍宽。适合做“主图附属图”的结构像左图放原始曲线、右图放局部放大那种排版。从第二张多子图开始我建议你养成一个习惯plt.subplots(..., constrained_layoutTrue)。这个figure级参数会自动调整子图间距、图例位置和标签边距比手动subplots_adjust省太多功夫。但要注意constrained_layout和fig.tight_layout()不要同时使用二者会互相覆盖造成奇怪的布局抖动。5. 常见问题与排查技巧实录5.1 中文方块和负号问题一套配置直接带走这个问题在前面已经详细展开这里给出可以直接照抄的固定配置import matplotlib as mpl import matplotlib.pyplot as plt mpl.rcParams[font.sans-serif] [SimHei, Microsoft YaHei] mpl.rcParams[axes.unicode_minus] False放在项目代码最上方之后所有图都不会再乱码。如果你的环境里没有这些字体参考3.2节的方法注册中文字体。这里最容易被忽视的坑是字体名不一致所以遇到中文乱码时第一步永远是打印font_manager.ttflist确认目标字体真的被识别到了再检查拼写和大小写。字体名写错时matplotlib不会报任何错误只会默默回退到DejaVu Sans这种“静默失败”比报错更坑人。5.2 保存图片空白或只保存了上一次的图这类问题经常出现在循环生成图片的场景。比如你在for循环里不断创建新figure却忘了在循环末尾处理掉旧figure内存越堆越高保存的文件也容易张冠李戴。保存空白图的另一个高发原因是show和savefig的顺序问题前面已经说过。更隐蔽的一个坑发生在Jupyter里连续执行两个单元格第二格只修改了ax的某些属性却因为没有重新调用plt.show()你会看到第一张图的输出还在第二张的修改“好像没生效”。其实图已经改了只是交互界面上没有刷新。解决的办法很简单把修改和显示放到同一个单元格里执行。多图循环保存推荐这样写figs [] for i in range(4): fig, ax plt.subplots() ax.plot(x, y[i]) figs.append(fig) fig.savefig(ffigure_{i}.png, dpi150, bbox_inchestight) # 全部保存完之后统一销毁防止内存暴涨 plt.close(all)把所有figure先收集起来统一保存最后用plt.close(all)关闭全部窗口并释放内存。不用close的话每张图还会保留在内存里数据量大时程序会越跑越慢最后直接卡死。5.3 坐标轴标签被截断、刻度重叠标签被截断最常见的原因是figure尺寸太小或子图间距不足。优先用fig.tight_layout()或constrained_layoutTrue解决手动不断调整subplots_adjust只能算治标不治本换一个字体或者标题长度就又会出问题。刻度重叠常见于时间日期数据刻度标签太长。最简单的处理是plt.xticks(rotation45)让文字倾斜摆放或者用ticker模块限制刻度数量from matplotlib.ticker import MaxNLocator ax.xaxis.set_major_locator(MaxNLocator(nbins6)) ax.tick_params(axisx, rotation45)nbins是期望的刻度个数matplotlib会自动挑出间隔合适的刻度。要记住的是nbins不是越大越好。刻度太密时数字会互相拥挤图表可读性反而下降一般4到8个比较平衡。5.4 数据量大时画图卡顿性能优化三板斧当数据量到百万级别一个普通的scatter就会让matplotlib慢得令人怀疑人生。我应对这种情况有三板斧。第一板斧是降采样。图片分辨率有限几百万个点画上去很多也是重叠的肉眼根本分辨不出区别。抽样画几千到几万个点视觉上几乎无差别但响应速度能快几个数量级。第二板斧是rasterizedTrue参数。它能让单个artist在保存时做位图化处理既保留图例、文字这些元素的矢量清晰度又避免整个矢量文件里堆几百万个点导致体积爆炸。这个参数对大数据量散点图特别友好。第三板斧是改画法。我处理过一千万级别的散点数据方法是把数据按像素网格做聚合用hexbin画成六边形密度图速度快了几十倍。这背后是一种可视化思维当点太多已经无法体现个体信息时你要画的不再是每个数据点而是数据的密度分布。数据量越大聚合展示的意义越大原始散点图反而没有价值。6. 这些经验值得你早点知道6.1 用OO接口写脚本用pyplot做探索这条经验我在开篇就提过现在展开说。交互式探索阶段比如在Jupyter里看数据分布、临时验证某个字段关系直接用plt.plot、plt.scatter这类pyplot接口最方便代码短、出图快。但一旦进入写脚本、封装绘图函数、把图嵌进业务系统的阶段我强烈建议切换到fig, ax plt.subplots()这种面向对象的写法。ax是一个实实在在的对象你可以把它传进函数、存进列表、在多个模块之间流转逻辑完全确定不会因为某个地方隐式改动了“当前axes”而导致画串图。6.2 配色与字体的统一性从第一天就要开始保持同一次报告、同一份文档里的图配色最好遵守同一套风格。我带过的几个开发者在第一版图里什么颜色都有到后期统一风格时全部返工相当痛苦。现在我的习惯是先plt.style.use(seaborn-v0_8)打底再固定一两个强调色作为关键数据线的颜色其余辅助元素全部交给默认配色。字体的道理也一样一套报告里中文字体换来换去会显得非常业余务必在项目最开始把字体配置写成公共模块固定下来。6.3 画图之前先想清楚“这张图要表达什么”很多人拿着数据就急着调plt.plot结果图画出来自己都不知道想证明什么。我的习惯是画图前先自问一句这张图要给谁看想传达什么结论如果是时间趋势用折线图如果是对比用柱状图如果是寻找相关性用散点图。图表选对之后美化才有意义。选错的图表配色再漂亮也只是把一个错误的信息包装得更精致而已。这也是我把“选型”单独作为一章的原因——在绘图这个领域思路永远比手艺更重要。最后再分享一个小技巧把常用的中文字体配置、样式表、savefig参数预先封装成一个个人绘图配置模块每次做新项目直接导入复用。我在实际工作中已经把这个配置模块沉淀了好几个项目每次画图能省掉至少三分之一的时间而且出图风格稳定同事接手我的图也不会觉得突兀。matplotlib用到现在我最大的体会是它并不难难的是你要理解它是“由对象组成”的绘图库而pyplot只是它友好的门面。把门面背后的那套机制摸透这个系列后面的章节你都会走得很顺。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑