十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Matplotlib专业绘图全攻略:从环境搭建到图表美化与实战排坑

Matplotlib专业绘图全攻略:从环境搭建到图表美化与实战排坑 Matplotlib这个东西我用了快十年从最早在学校里画实验曲线到后来在项目里做数据报表再到帮朋友调论文图表几乎每天都要跟它打交道。很多人觉得Matplotlib就是画个折线图、柱状图调个颜色加个图例能出图就行。但真正到了要交付PPT、写论文、做汇报的时候那些“能出图”的图表往往经不起细看——坐标轴字号不对、图例乱跑、颜色刺眼、中文字体变方块各种小毛病一堆。这篇博文就是冲着“专业”这两个字来的我会带着你把Matplotlib从最基础的运行环境一路走到能画出一张能直接放进正式场合的图表包括接口选择、常用图形、颜色配置、图例插入、样式美化以及运行过程中的各种坑。不管你是刚在PyCharm里装好库的新手还是已经画过几张图但总觉得不够精致的进阶用户这篇都能给你一些能直接落地的参考。1. 绘图前的思路整理先想清楚要画什么1.1 Matplotlib到底解决什么问题很多人学Matplotlib第一步就卡住了不是因为代码难而是没搞清楚它解决什么问题。Matplotlib本质上是一套Python的2D绘图库它的核心能力是把数据转换成可视化的图形而这个转换过程几乎全部由你手中的代码控制。换句话说它不是那种“点一下出图”的工具而是一支画笔画成什么样完全取决于你怎么用。在实际工作中我用它最多的是三个场景一是数据探索阶段快速看分布和趋势二是做技术报告时输出清晰的对比图三是给论文或公众号文章配图。这三个场景对图表的要求不一样前一个追求速度后两个追求美观和规范。Matplotlib好就好在它既能让你在几十行代码内快速画一张草图也能通过精细的配置把它打磨到出版级别。关键不在于它能不能画而在于你有没有掌握那套“从粗到细”的配置思路。1.2 两个接口怎么选pyplot与面向对象Matplotlib有两种主流使用方式一个是pyplot接口一个是面向对象OO接口。不少新手一上来就对着网上零散的代码抄一会儿用plt.plot()一会儿又看到fig, ax plt.subplots()完全不知道区别在哪。pyplot接口是Matplotlib提供的一个有状态接口它内部维护了一套“当前图形”和“当前坐标轴”的概念。你调用plt.plot()其实就是往当前坐标轴上画线调用plt.xlabel()就是在给当前坐标轴加标签。这种接口上手快写起来代码量少适合快速绘图和交互式使用。面向对象接口则更显式你先创建一个Figure对象相当于一张画布再在画布上创建一个或多个Axes对象相当于绘图区然后所有绘图操作都通过ax.plot()、ax.set_xlabel()这样的方法来调用。这样做的优势是当画面上有多张子图、多个坐标系时你清楚地知道每个操作作用于哪个对象不会有“画错图”的隐患。我个人的经验是初学阶段可以先用pyplot练手但一旦你要画子图、做复杂排版一定要切换到面向对象接口。下面是一个最简单的对比示例同样的图形两种写法都能完成import matplotlib.pyplot as plt import numpy as np x np.linspace(0, 10, 100) y np.sin(x) # pyplot接口 plt.figure(figsize(6, 4)) plt.plot(x, y) plt.title(pyplot style) plt.show() # 面向对象接口 fig, ax plt.subplots(figsize(6, 4)) ax.plot(x, y) ax.set_title(OO style) plt.show()两种写法画出来的图是一样的但面向对象接口在后续改动时更清晰。比如我要在这个基础上再加一条曲线、改一下图例位置ax的方式能让我一目了然地知道改的是哪个绘图区。2. 环境准备与最小运行流程2.1 PyCharm中的环境搭配热词里有“pycharm使用numpy和matplotlib”这确实是新手最先遇到的问题。PyCharm本身不内置Python环境你需要先有一个解释器然后在解释器里安装Matplotlib和NumPy。这里我建议直接用PyCharm的虚拟环境不要图省事装到全局。虚拟环境的优势是每个项目的依赖相互隔离不会出现“A项目需要Matplotlib 3.6B项目需要3.8”这样的版本冲突。具体操作就是在创建项目的时候选Virtualenv类型Python解释器选你本机装的Python 3.8以上版本。项目创建好了之后在PyCharm底部的Terminal里执行pip install matplotlib numpy如果你因为网络原因装得慢可以换成清华镜像源pip install matplotlib numpy -i https://pypi.tuna.tsinghua.edu.cn/simple装完之后在代码里写import matplotlibPyCharm通常会弹出提示告诉你“Matplotlib not found”这个时候只要点一下安装即可。但为了可控性我建议还是手动在终端里装至少你能看到装的是什么版本。2.2 让第一张图跑起来的完整流程装好库之后很多人会遇到一个特别尴尬的问题代码运行了但什么窗口都没弹出来。这就要说到plt.show()这个方法了。Matplotlib默认情况下不会主动把图显示出来你需要显式调用plt.show()来弹出图像窗口。在PyCharm里如果用的是SciView的画图面板有时代码执行完会自动显示图像但这个行为取决于PyCharm的配置和后端设置。更稳妥的做法是写代码时始终加上plt.show()然后配合plt.close()释放资源不然循环画很多张图时内存会悄悄涨上去。下面这个流程是我在PyCharm里测试最基本的一张图时常用的代码import numpy as np import matplotlib.pyplot as plt # 生成数据 x np.linspace(0, 2 * np.pi, 100) y np.sin(x) # 创建图形和坐标轴 fig, ax plt.subplots(figsize(8, 5)) # 绘图 ax.plot(x, y, labelsin(x)) # 坐标轴与标题 ax.set_xlabel(x) ax.set_ylabel(y) ax.set_title(Sine Function) # 显示图例 ax.legend() # 显示图像 plt.show()这段代码是入门级的“最小可运行示例”它涵盖了数据生成、图形创建、绘图、标注、图例和显示几个核心环节。我建议所有新手都把这个流程背下来之后再慢慢地往里面加东西这样不容易迷失。3. 常用图形逐个拆解3.1 折线图最基础也最容易出效果折线图是Matplotlib里使用频率最高的一种图形适合展示数据随时间或其他连续变量变化的趋势。很多人在画折线图时只关注数据本身忽略了线条样式、标记点和图例这些细节导致出来的图显得很“素”。真正专业的折线图至少要关注几个维度线宽、线型、颜色、标记、透明度。线宽一般用linewidth参数控制默认值是1.5如果是打印到纸上或投到屏幕上建议调到2到3之间线型用linestyle常见有实线-、虚线--、点划线-.和点线:标记用marker有圆点o、方格s、三角^、星号*等透明度用alpha取值0到1多组数据重叠时尤其好用。下面是一个稍微完整的折线图示例两条曲线加对比效果import numpy as np import matplotlib.pyplot as plt x np.linspace(0, 10, 100) y1 np.sin(x) y2 np.cos(x) fig, ax plt.subplots(figsize(8, 5)) ax.plot(x, y1, linestyle-, linewidth2.5, marker, labelsin(x)) ax.plot(x, y2, linestyle--, linewidth2.5, marker, labelcos(x)) ax.set_xlabel(x, fontsize12) ax.set_ylabel(y, fontsize12) ax.set_title(Comparing sin(x) and cos(x), fontsize14) ax.legend(locupper right, frameonTrue) ax.grid(True, linestyle:, alpha0.6) plt.show()注意这里的linestyle、linewidth、marker都是高频参数建议多尝试几种组合。另外grid(True)让网格线显示出来这对判断数值位置非常有帮助大部分专业图表都会保留浅色网格。3.2 散点图、柱状图与直方图折线图之外散点图、柱状图和直方图也是各场景里逃不掉的硬骨头。散点图用来观察两个变量之间的关系柱状图适合展示分类数据的对比直方图则用于查看数据的分布情况。散点图的颜色映射是一个高级技巧可以用c参数传入一个数值序列再用cmap指定颜色映射表这样散点的颜色本身就携带了第三个维度的信息。比如我要展示一组样本的“身高”和“体重”关系同时用颜色表示“年龄”代码可以这样写rng np.random.default_rng(42) height rng.normal(170, 10, 200) weight rng.normal(65, 12, 200) age rng.integers(20, 60, 200) fig, ax plt.subplots(figsize(8, 5)) sc ax.scatter(height, weight, cage, cmapviridis, s30, alpha0.8) fig.colorbar(sc, axax, labelage) ax.set_xlabel(height (cm)) ax.set_ylabel(weight (kg)) plt.show()柱状图里最值得关注的是误差棒的展示yerr参数可以传入一组误差范围让图表看起来更严谨。直方图则需要合理设置bins数量太少会丢失分布细节太多会显得杂乱一个常用的经验是bins的数量可以根据数据量的平方根来估计。当然如果数据量特别大也可以用densityTrue把纵轴变为概率密度。3.3 用子图把多个图形组合起来当一张图里既有数据分布又有趋势线时把多个图形组合到一张画布上的需求就会出现。plt.subplots()的nrows和ncols参数可以轻松实现多子图布局。我之前在一个项目里需要同时展示原始信号、它的频谱和它的统计直方图就是用一个2行2列的四子图布局解决的。关键技巧是给每个子图都设置独立的标题和坐标轴标签并调整tight_layout避免子图之间相互挤压。x np.linspace(0, 4 * np.pi, 500) y np.sin(x) 0.3 * np.sin(5 * x) fig, axes plt.subplots(2, 2, figsize(12, 8)) # 左上原始信号 axes[0, 0].plot(x, y) axes[0, 0].set_title(Original Signal) axes[0, 0].set_xlabel(time) axes[0, 0].set_ylabel(amplitude) # 右上信号频谱简化示例 freq np.fft.fftfreq(len(x), dx[1]-x[0]) amps np.abs(np.fft.fft(y)) axes[0, 1].plot(freq[:len(freq)//2], amps[:len(amps)//2]) axes[0, 1].set_title(Frequency Spectrum) axes[0, 1].set_xlabel(frequency) axes[0, 1].set_ylabel(amplitude) # 左下直方图 axes[1, 0].hist(y, bins40, colorsteelblue, alpha0.7) axes[1, 0].set_title(Histogram) axes[1, 0].set_xlabel(value) axes[1, 0].set_ylabel(count) # 右下散点图 axes[1, 1].scatter(x[:200], y[:200], s10, alpha0.5) axes[1, 1].set_title(Scatter View) axes[1, 1].set_xlabel(time) axes[1, 1].set_ylabel(amplitude) plt.tight_layout() plt.show()这段代码里让我反复受益的是tight_layout()没有它子图的标题和坐标轴标签经常被裁掉。现在我已经养成了习惯只要是画子图tight_layout必写。4. 颜色的专业用法4.1 Matplotlib颜色体系速览颜色是决定图表专业度的第一要素。Matplotlib里指定颜色有几种常见方式内置颜色名称、单字符缩写、十六进制、RGB/RGBA元组。内置颜色名称有red、blue、green这类常规命名也有steelblue、sandybrown这类更细腻的命名。单字符缩写是新手容易误用的地方b代表蓝色、g代表绿色、r代表红色但到了复杂的图表里这种颜色往往显得儿童化。我个人的建议是正式场合尽量用十六进制颜色或者Matplotlib提供的Tableau颜色比如tab:blue、tab:orange这些颜色经过了视觉设计即使打出来也不会失真太多。下面是一个颜色对比示例直观感受不同颜色在同一个图上的效果fig, ax plt.subplots(figsize(8, 5)) ax.plot(x, np.sin(x), colortab:blue, linewidth2, labeltab:blue) ax.plot(x, np.sin(x 1), color#e66101, linewidth2, label#e66101) ax.plot(x, np.sin(x 2), colorsteelblue, linewidth2, labelsteelblue) ax.legend() plt.show()十六进制颜色在网上有很多配色工具可以参考比如ColorBrewer它提供的配色方案在色盲友好性上做得不错。4.2 colormap让颜色有层次当数据本身带着一个连续数值变量时单纯用单一颜色就不够用了。这时候colormap颜色映射表派上了用场。Matplotlib内置了几十种colormap常用的有viridis、plasma、inferno、cividis等这些是官方专门设计为在灰度打印和色盲场景下仍然可辨识的颜色序列。我在实际使用中特别推荐viridis它在深色背景下也能保持较好的对比度而且不同数值段之间的颜色变化是均匀的不会产生视觉上的误导。使用colormap的常见方式是配合scatter的c参数或者imshow的cmap参数。如果想单独获取一个颜色映射对象用import matplotlib.pyplot as plt import numpy as np cmap plt.get_cmap(viridis) colors cmap(np.linspace(0, 1, 5)) print(colors) # 返回5个RGBA颜色这段代码返回的5个RGBA颜色可以直接传给图表里的线条或柱状图实现同一张图里不同元素的颜色过渡统一。4.3 颜色常见坑太杂、失真颜色用得太多是新手最容易犯的错。一张图里超过6种颜色基本就开始显得杂乱。专业图表并不等于五颜六色反而是在有限颜色对中做出清晰的层次。另外一个坑是打印失真。很多颜色在屏幕上看很鲜艳但一旦转成灰度打印就全部变成一片灰色。如果你需要在论文里用黑白打印建议在画图时直接选择灰度容易区分的线条或者在后期把图像转换前做一次灰度对比。用colormap时也要注意jet这类彩虹色在某些区间之间是非线性的会让数值差异看起来比实际更大这也是科学绘图领域一直在呼吁少用jet的原因。5. 图例与标注让图表自己会说话5.1 插入图例的关键参数热词里提到“matplotlib 插入图例”这确实是每一个用过Matplotlib的人都绕不开的操作。插入图例最简单的办法是在绘图函数里传入label参数然后调用ax.legend()。但图例里的门道远不止于此。loc参数控制图例位置常见值有best、upper right、lower left等。frameon决定是否显示图例边框一般True会有一个框False则没有正式论文里多倾向于False或弱化边框。fontsize和title可以调整图例文字大小并给图例加标题。ncol参数则可以把图例排成多列节省垂直空间。我给一个实战场景有一张包含四个数据系列的图图例如果默认放在右上角会正好挡住一组关键数据。此时我一般会手动指定locupper center加bbox_to_anchor(0.5, 1.15)让图例移到画布外侧顶部这样既不影响数据区域又保证了可读性。fig, ax plt.subplots(figsize(8, 5)) for i in range(4): ax.plot(x, np.sin(x i), linewidth2, labelfseries {i}) ax.legend(locupper center, bbox_to_anchor(0.5, 1.12), ncol4, fontsize10, frameonFalse) plt.tight_layout() plt.show()5.2 标题、坐标轴标签与文本标注图例只是图表标注的一部分一个完整的专业图表还包括标题、坐标轴标签和图内文本标注。标题的定义要能一句话说清图表内容而不是简单写“结果图”这样的笼统描述。坐标轴标签必须写清单位单位放在括号里例如“时间 (s)”“浓度 (mg/L)”这是科技图表的通用规范。文本标注可以用ax.text()或ax.annotate()。annotate的功能更强大它支持箭头连接能精确地指向图中的某个特征点比如标出某个峰值或异常值。这类细节在论文审稿或项目验收时特别加分因为在关键位置加一句说明就能让读者快速抓住图表想强调的核心信息。fig, ax plt.subplots(figsize(8, 5)) ax.plot(x, y, colortab:blue, linewidth2) ax.annotate(peak, xy(np.pi/2, 1), xytext(np.pi/21, 0.8), arrowpropsdict(arrowstyle-, colorblack)) ax.set_xlabel(x) ax.set_ylabel(y) plt.show()6. 高级美化从能用到专业6.1 全局样式与rcParams如果你不希望每一张图重复设置字体、字号、颜色等参数那么Matplotlib的rcParams全局配置就是你必须掌握的。这个机制相当于给所有图表设置一套默认样式一次性定义好后后面画的每张图都自动套用。我自己常用的全局配置包括plt.rcParams[figure.figsize] (8, 5) plt.rcParams[font.size] 12 plt.rcParams[axes.labelsize] 12 plt.rcParams[axes.titlesize] 14 plt.rcParams[xtick.labelsize] 10 plt.rcParams[ytick.labelsize] 10 plt.rcParams[lines.linewidth] 2 plt.rcParams[axes.grid] True plt.rcParams[grid.linestyle] : plt.rcParams[grid.alpha] 0.6这些配置放在脚本开头后续就不用每个图都写一遍xlabel的fontsize了。不过注意rcParams是全局的如果项目中不同图需要不同风格最好还是在图开头临时设置避免互相影响。6.2 网格、边框与细节调整专业图表很少像默认那样四条边框都完整显示。很多时尚的可视化图表会隐藏上边框和右边框只保留左轴和下轴这样视觉上更轻盈。ax.spines就是用来控制边框的ax.spines[top].set_visible(False) ax.spines[right].set_visible(False)网格线方面whichmajor用于显示主刻度网格whichminor用于显示次刻度网格。如果数据变化很频繁可以开启ax.minorticks_on()配合whichminor让网格更细致。不过网格线不能喧宾夺主颜色要浅、线型要虚默认的实黑网格线会让画面显得脏。刻度方向也是一个细节。中文科技期刊和许多国外期刊倾向于刻度朝外而Matplotlib默认刻度朝内。设置方式ax.tick_params(directionin)这个细节可能有些人觉得无所谓但在正式排版时统一刻度方向会明显提升图表的规范感。6.3 常见风格主题选择Matplotlib内置了几套主题风格通过plt.style.use()来切换这在“从能用到专业”的进阶过程中是最省力的一个工具。最常用的几个风格default默认风格适合日常快速查看。seaborn-v0_8带浅灰网格的暖色风格视觉上很舒服早年seaborn没独立之前被广泛使用。ggplot仿R语言ggplot2风格适合熟悉R的人。bmh贝叶斯方法系列的风格网格清晰科研感强。我个人比较喜欢seaborn-v0_8的配色和网格但需要注意在较新的Matplotlib版本里seaborn风格已经更名为seaborn-v0_8老代码里的seaborn可能报错。如果你有固定的报告模板也可以基于某个风格再叠加自己的rcParams二次覆盖。7. 运行显示与排查问题实录7.1 图表运行不出来怎么办“运行不显示”是新手遇到的最高频问题。根据我的经验按照下面的顺序排查基本能解决先确认代码里有没有写plt.show()。如果没有图像不会弹出。再确认PyCharm是否开启了SciView的“Show plots in tool window”如果开了图像会显示在PyCharm侧边栏不会弹出独立窗口。第三检查后端设置plt.get_backend()可以查看当前后端如果是agg这种非交互式后端任何show()都不会弹窗需要改成TkAgg或QtAgg。如果需要强制切换后端可以在脚本开头加入import matplotlib matplotlib.use(TkAgg) import matplotlib.pyplot as plt但注意这个设置必须在导入pyplot之前完成否则不生效。7.2 中文字体与负号显示问题用Matplotlib画中文标题或坐标轴标签时经常会出现一个个空心方块这是因为Matplotlib默认字体里没有中文字体映射。解决办法是先检查系统有哪些中文字体再手动指定。import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] # Windows常用 # 或者 [WenQuanYi Zen Hei] # Linux # 或者 [Arial Unicode MS] # macOS plt.rcParams[axes.unicode_minus] False # 解决负号显示成方块axes.unicode_minus必须设置为False否则坐标轴上的负号也会变成方块。这个问题我踩过很多次每次换个环境就要重新查一次字体名字后来干脆写了一个小工具函数自动检测系统字体并配置。7.3 常用排查表格我把日常使用中碰到频率最高的问题整理成一张速查表方便你按图索骥问题现象可能原因解决方法运行后没有图窗弹出没写plt.show()在脚本末尾加plt.show()图窗弹出后立刻消失脚本退出导致窗口关闭加input()或plt.show(blockTrue)中文显示为方块未配置中文字体设置rcParams[font.sans-serif]负号显示为方块unicode_minus未关闭设置rcParams[axes.unicode_minus]False图例中文字重叠图例位置不合适调loc或bbox_to_anchor或增大figsize子图标题被裁切子图间距不够加plt.tight_layout()保存的图片背景透明保存时未设置facecolor使用plt.savefig(..., facecolorwhite)无法保存高分辨率图dpi参数缺失plt.savefig(..., dpi300)其中那个“图窗弹出后立刻消失”的问题在PyCharm里很少遇到但如果你直接用命令行运行脚本就会经常出现。解决办法是在plt.show()之后加一行input()让窗口保持打开直到你按回车。7.4 保存图片的细节与输出格式画好一张图肯定要保存下来保存时用plt.savefig()而不是plt.show()过程中的截图这样图片分辨率、尺寸、字体细节都能保证。plt.savefig()有几个关键参数dpi控制分辨率普通PPT用150印刷建议300bbox_inchestight可以自动裁剪多余空白让图片内容紧凑transparentFalse保持白色背景避免透明背景在深色PPT里看不清。我常用的保存代码如下fig.savefig(output.png, dpi300, bbox_inchestight, facecolorwhite)如果你需要矢量图可以选择PDF或SVG格式这在论文排版中非常有用。矢量图放大多少倍都不会模糊而PNG是位图放大后会有锯齿。另外如果你在循环中批量生成图表每次生成后一定要plt.close(fig)否则图很多时内存占用会越来越大最后导致PyCharm卡死。这个坑我踩过循环100张图不close内存直接飙升到几个G。8. 从会画到画得专业差的就是这些细节回到最开始那个话题Matplotlib本身并不难难点在于“把图画得专业”这件事上有太多零碎的细节。同一组数据有人画出来像草稿有人画出来可以直接进论文差别往往不在代码复杂度上而在于对颜色、字体、网格、图例、边框、清晰度这些细节的控制。我在实际工作中总结出一个习惯每画一张图我都会默念一遍检查清单——坐标轴标签有没有带单位标题是不是能一句话看懂图例是否遮挡数据配色有没有明显冲突网格线是否太刺眼保存分辨率够不够。这套清单走完之后这张图基本就可以放心交付了。如果你现在还是个新手不要指望一次就把所有细节都掌握。先把我上面给的示例代码跑通然后再试着改颜色、改图例、改字体、改样式。每次改动都是一个小的实验实验做多了你看到一张图的时候自然就会意识到哪里需要调整。到最后你会发现其实Matplotlib的专业画法也就是把那些最琐碎的基础功练到极致而已。我个人还有一个建议把你自己常用的配置写成一个my_plot_style.py模块里面封装好rcParams、字体设置、常用颜色搭配甚至一个save_fig()函数。以后每次绘图直接import不仅能省下大量重复代码还能保证你所有图表的风格完全统一。这种一致感才是专业图表最大的底气。
返回列表