
做数据分析这几年Matplotlib里的pyplot几乎是我每天都会打开的模块。别人问我会不会画图我说会其实就是会用pyplot把数据变成可视化图形。但真正把绘图这件事做得又快又顺手不是记住几个API就够的你得理解它的绘图逻辑知道怎么把多张图拼在一起还得学会处理那些奇奇怪怪的报错比如最近经常有人问的“请安装matplotlib以显示图标”到底是怎么回事。这篇博文就围绕Matplotlib.pyplot绘图展开。我会从pyplot的底层思路讲起重点拆解“六张图合在一个图上”这类多子图拼接需求再分享一套完整可复用的实操代码最后把安装配置、中文乱码、图表不显示这些高频问题一次性讲透。不管你是刚装好Matplotlib的新手还是已经画过一阵子图但总感觉不系统的同学这篇应该都能帮上忙。1. 先把思路理顺pyplot到底解决什么问题1.1 为什么选 pyplot而不是直接上手面向对象很多人第一次接触Matplotlib上来就学习figure、axes这些概念结果一头雾水。我个人的建议是先从pyplot入手因为它是一层更贴近直觉的封装。pyplot设计得很像MATLAB的绘图方式核心思路是一个“状态机”。你调用plt.plot()它就把数据画到“当前坐标系”上再调用plt.xlabel()它就给刚才这张图加标签再调用plt.title()就给这张图加标题。你不需要时刻盯着figure和axes对象只管像喊口令一样一步一步来就行。这种模式特别适合交互式探索场景。比如你在Jupyter Notebook里分析数据临时想看看某两列的关系两条命令就出图了。但是如果要做正式的、复杂的图表我建议还是要用fig, ax plt.subplots()这种面向对象的方式。原因很简单pyplot的隐式状态在复杂场景下容易“串图”尤其当你需要同时管理多个子图时显式的axes对象会让代码更清晰、更好控制。1.2 安装配置pip install matplotlib 前后的那些坑先解决环境问题。安装Matplotlib本身很简单一行命令pip install matplotlib但实际项目中这条命令可能踩到几个典型坑。最常见的是“装到了错误的Python环境”。比如你电脑里既有Anaconda又有系统Python还在某个虚拟环境里开发如果你没激活虚拟环境就执行pip install matplotlib装完发现导入仍报错那多半就是装错地方了。我的习惯是在项目根目录建一个虚拟环境然后老老实实激活后再装python -m venv venvWindows下激活venv\Scripts\activatemacOS/Linux下激活source venv/bin/activate然后再装matplotlib。装完验证一下python -c import matplotlib; print(matplotlib.__version__)能输出版本号说明环境没问题。如果你的网络下载比较慢可以用国内镜像源比如清华的PyPI镜像pip install matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple另外很多人遇到的“请安装matplotlib以显示图标”并不是matplotlib本体报错而是IDE或者Jupyter环境检测不到已安装的matplotlib时给出的提示。解决思路很简单在对应环境里装好matplotlib然后重启Notebook内核或重启IDE。如果装好了还是提示就检查一下是不是内核选的不是当前虚拟环境这属于老生常谈但确实容易搞混的问题。2. 核心细节解析从一张图到多子图拼接2.1 先吃透 pyplot 的基本绘图链路我把pyplot绘图的基本流程概括成六步大部分图表都逃不出这个套路准备数据一般就是列表、NumPy数组或者Pandas列。创建画布用plt.figure(figsize(宽, 高))这一步可选但建议显式设置画布尺寸否则好看不到哪去。添加坐标系或者直接用plt.plot()等函数隐式创建。调用具体绘图函数比如plot、scatter、bar、hist。设置细节包括标题、轴标签、图例、网格、刻度范围。显示或保存交互环境用plt.show()脚本或批量生成用plt.savefig()。如果你关注热搜词里的“matplotlib六张图合在一个图上”那核心痛点就出在第3步到第6步之间。很多人知道了单张图画法但一到拼图就懵了。其实原理不复杂关键在于理解“坐标系”这个概念。简单打个比方一张画布是figure画布上面可以划分出很多个格子每个格子里放一张图每个格子就是一个axes对象。你要画六张图本质就是创建一张画布在上面划分出2行3列的网格然后往每个格子里填内容。2.2 三种实现“六张图合一个图”的方案对比具体实现多子图目前主流有三种方式我列个表格对比一下方式核心函数优点适用场景逐个定位plt.subplot(2, 3, index)写法直观适合手动控制某一张图子图数量少、布局固定批量创建plt.subplots(2, 3)返回画布和坐标轴数组代码整洁推荐规则网格布局比如2行3列复杂布局GridSpec支持跨行跨列、不等宽布局仪表盘、报告封面、复杂排版如果你就是想把6张图均匀地拼成一个2行3列的大图最省事的方案就是plt.subplots(2, 3)。它会一次性返回一个figure对象和一个形状为(2, 3)的axes数组然后你用axes[0][0]、axes[1][2]这些下标去定位每一张子图就行。这里我要特别说一下plt.subplot(2, 3, 1)和plt.subplots(2, 3)的区别。前者是每次调用都指定“第几个位置”你要画6张图就得调用6次后者是一次性建好6个位置后续只需要在对应位置画图。如果你用面向对象方式写后续调整样式、共享坐标轴都会方便很多。2.3 构图参数figsize、dpi、共享坐标轴和布局调整多子图拼在一起之后最常见的两个问题一是图太小看不清二是子图之间挤在一起。前者靠figsize解决后者靠tight_layout()或constrained_layoutTrue解决。figsize的单位是英寸dpi是每英寸像素点数。比如plt.subplots(2, 3, figsize(12, 8), dpi120)意思是画布宽12英寸、高8英寸分辨率120 DPI最终生成的图片宽就是1440像素。这个参数不是随便拍脑袋定的要看你最终用途公众号配图一般100-150 DPI就够打印或投稿则需要300 DPI。再说共享坐标轴。六个子图画在同一张画布上有时候它们代表的是同一套时间轴或同一套数值范围那可以用sharexTrue或shareyTrue这样缩放一个子图时其他子图会联动而且刻度标签不会重复占空间整体观感会很紧凑专业。最后提醒一点拼图完成后一定要调用plt.tight_layout()或者创建figure时设置constrained_layoutTrue否则子图标题和轴标签很容易被切掉或者重叠。这个小细节很多人忽略结果图一出来边距奇怪还以为是代码问题。3. 实操过程用一份代码完成六图拼接3.1 场景设定一份数据分析报告需要六张图为了把这个过程讲得具体我模拟一个真实场景。假设你在做一份商品销售数据分析报告里需要展示以下六张图销售额随时间的变化趋势折线图各渠道销售额占比饼图订单金额的分布情况直方图商品价格与销量的关系散点图各区域销售额对比柱状图不同门店销售额的分布差异箱线图这就是典型的“六张图合在一个图上”的需求用plt.subplots(2, 3)来做非常合适。我先构造一份模拟数据然后一步步来画。3.2 核心代码实现与逐步讲解先导入库和准备数据import matplotlib.pyplot as plt import numpy as np # 解决中文乱码重点是SimHei字体和负号设置 plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False # 模拟数据 np.random.seed(42) months np.arange(1, 13) sales np.array([120, 135, 128, 150, 165, 180, 175, 190, 205, 210, 225, 240]) channels [App, Web, 小程序, 线下] channel_sales [360, 220, 280, 140] order_amounts np.random.normal(150, 40, 500) price np.random.uniform(10, 200, 200) qty (500 / price np.random.normal(0, 8, 200)).clip(1, 60) regions [华东, 华南, 华北, 西南] region_sales [820, 735, 620, 410] store_sales [np.random.normal(80, 15, 30) for _ in range(4)]然后创建画布和子图fig, axes plt.subplots(2, 3, figsize(14, 9), dpi120)这一行的意思就是创建一个14英寸宽、9英寸高的画布划分成2行3列共6个子图axes的形状是(2, 3)。接下来逐个填充子图# 1. 销售额趋势 axes[0, 0].plot(months, sales, markero, color#1f77b4, linewidth2) axes[0, 0].set_title(月度销售额趋势) axes[0, 0].set_xlabel(月份) axes[0, 0].set_ylabel(销售额万元) axes[0, 0].grid(True, linestyle--, alpha0.6) # 2. 渠道占比 axes[0, 1].pie(channel_sales, labelschannels, autopct%.1f%%, startangle90) axes[0, 1].set_title(各渠道销售额占比) # 3. 订单金额分布 axes[0, 2].hist(order_amounts, bins30, color#2ca02c, edgecolorwhite, alpha0.8) axes[0, 2].set_title(订单金额分布) axes[0, 2].set_xlabel(订单金额元) axes[0, 2].set_ylabel(频数) # 4. 价格与销量关系 axes[1, 0].scatter(price, qty, alpha0.5, s30, color#ff7f0e) axes[1, 0].set_title(商品价格与销量关系) axes[1, 0].set_xlabel(价格元) axes[1, 0].set_ylabel(销量件) # 5. 各区域销售额 axes[1, 1].bar(regions, region_sales, color#d62728, alpha0.85) axes[1, 1].set_title(各区域销售额对比) axes[1, 1].set_xlabel(区域) axes[1, 1].set_ylabel(销售额万元) for i, v in enumerate(region_sales): axes[1, 1].text(i, v 10, str(v), hacenter, fontsize10) # 6. 门店销售额箱线图 axes[1, 2].boxplot(store_sales, tick_labelsregions, patch_artistTrue) axes[1, 2].set_title(门店销售额分布) axes[1, 2].set_xlabel(区域) axes[1, 2].set_ylabel(销售额万元) plt.tight_layout() plt.savefig(sales_report.png, bbox_inchestight, dpi150) plt.show()这段代码跑完之后你应该能看到一张2行3列的大图六个子图分别是折线图、饼图、直方图、散点图、柱状图、箱线图。整张图会被自动保存为sales_report.png同时弹窗展示。几个我特别想强调的细节第一plt.rcParams的中文字体设置必须放在绘图之前否则画出来的中文全是方块。而且axes.unicode_minusFalse这个也别忘了不然坐标轴的负号会显示成乱码。第二boxplot函数里我用了patch_artistTrue这样箱线图才能填充颜色否则默认是透明的打印出来观感差很多。新版matplotlib里tick_labels代替了旧的labels参数写代码时注意兼容。第三饼图里的autopct%.1f%%是显示百分比格式保留一位小数。这个格式很常用如果你要保留两位就写成%.2f%%。3.3 让图更专业样式、配色与出版级导出六张图都能画出来只是“能用”真正让图显专业的往往是细节。尽可能统一配色就是最简单有效的方法。Matplotlib默认的颜色循环虽然不难看但用久了会有“默认脸”的感觉。我个人比较喜欢用color参数手动指定色系或者直接换主题风格plt.style.use(seaborn-v0_8-whitegrid)这句可以让背景变白、网格变淡整体瞬间精神很多。你可以自己试试ggplot、fivethirtyeight等风格找到适合你项目的。导出图片时savefig的参数比你想的重要。bbox_inchestight会自动裁剪多余白边dpi控制清晰度。如果需要插入到Word或PPT里我一般用150 DPI如果是要打印或者投稿我会用到300 DPI。另外savefig支持透明背景参数是transparentTrue在某些版式下特别有用。如果你的图片要放到深色背景的PPT里还可以把文字颜色调成浅色这个属于进阶玩法但掌握之后非常出彩。4. 常见问题与排查技巧实录4.1 安装和导入阶段的报错速查表我整理了一份我在工作中真实遇到的报错对照表算是排坑经验集合报错信息可能原因解决方案ModuleNotFoundError: No module named matplotlib当前环境未安装matplotlib激活正确环境后执行pip install matplotlib“请安装matplotlib以显示图标”IDE或Notebook内核检测不到matplotlib安装matplotlib并重启内核或IDEImportError: DLL load failed依赖库版本冲突常见于Windows重装matplotlib或升级numpy安装时网络超时下载源不稳定使用国内镜像源pip安装安装后import很慢首次缓存构建多运行几次即可或考虑换更轻量的后端“请安装matplotlib以显示图标”这个提示我特别想多说两句。我第一次看到这个提示是在一个Jupyter Notebook里当时明明在终端里可以正常导入matplotlib但Notebook就是不认。后来才意识到Notebook的内核指向的是base环境而我安装matplotlib是在另一个虚拟环境里。解决方法是确认Notebook内核对准当前环境或者直接在当前内核环境里重新安装。如果你用的是VS Code这个错误还会出现在“交互式窗口”里解决办法是在VS Code右下角切换Python解释器让它指向你安装了matplotlib的那个环境。4.2 图表显示和保存阶段的经典问题画图没有弹窗这个问题在脚本运行场景下出现频率极高。本质原因是Matplotlib的默认后端是TkAgg或QtAgg但在某些服务器或无界面环境下没有图形显示器可以弹窗。解决方案有几种脚本里检查有没有写plt.show()。如果是服务器上运行直接用plt.savefig()保存图片不调用plt.show()。如果需要交互提前设置后端import matplotlib; matplotlib.use(TkAgg)。另一个经典问题是“保存图片是空白的”。这个现象通常是把plt.savefig()放在plt.show()之后。plt.show()会清空当前图形缓存所以先show()再savefig()保存到的对象就是空的。正确顺序是先savefig()后show()。中文显示为方块也几乎人人都遇到过。原因很简单Matplotlib默认字体不包含中文字符。用plt.rcParams[font.sans-serif] [SimHei]是Windows下最快的解决办法。macOS可以用[Arial Unicode MS]或[PingFang SC]。Linux服务器上如果字体不够你可以用fc-list :langzh查看系统已装的中文字体没有就装一个fonts-wqy-zenhei之类的字体包。4.3 绘图性能和布局排错的进阶心得当你用Matplotlib画几十万个点的散点图时渲染性能会明显下降图表拖起来卡顿。这种场景下我建议调低点的大小和透明度或者用rasterizedTrue让图层栅格化图片体积和渲染速度会改善不少。坐标轴刻度重叠也是个高频问题。比如柱状图的横轴标签太长或者日期格式的刻度太多。最简单的方案是用plt.xticks(rotation45)旋转标签或者用plt.tight_layout()调整边距。如果刻度太多还可以用MaxNLocator限制刻度数量from matplotlib.ticker import MaxNLocator ax.xaxis.set_major_locator(MaxNLocator(integerTrue, nbins6))这样横轴最多显示6个刻度干净利落。布局排错方面我刚接触子图时经常因为axes的下标写错而报IndexError。比如创建的是(2, 3)的网格你却写了axes[0, 3]那肯定越界。这种问题没有捷径只能多看多写。写多了之后你会形成肌肉记忆第一维是行第二维是列axes[1, 0]就是左下角那张图。我个人在实际操作中的体会是Matplotlib这条路真正难的不是API记不住而是心态。你越是急着画完越容易卡在细节上。倒不如先搭建一个固定的绘图模板把常见配置写成函数需要的时候改数据、改标题就行。如果你要把这套技能用到真正的项目里建议从复制上面那段六图拼接代码开始把每个参数都改一遍观察效果变化这个过程比背文档高效得多。最后再分享一个小技巧设置plt.rcParams时可以把常用配置做成一个mpl_config.py文件每次项目里直接import省得每个脚本重复写那段中文乱码设置。