Matplotlib核心架构与实战指南:从Figure/Axes对象到出版级图表
1. 认识MatplotlibPython数据可视化的基石如果你刚开始用Python处理数据或者已经写了几行代码想把一堆数字变成直观的图表那你迟早会遇到它——Matplotlib。这几乎是每个Python数据分析师、科学家甚至工程师的“第一把绘图刷子”。我第一次接触它时感觉就像拿到了一把瑞士军刀功能多到眼花缭乱但最初连怎么打开主刀都搞不清楚。网上很多教程一上来就甩出一段复杂的代码画出一个精美的图表但对于Figure、Axes这些核心概念却一笔带过让人云里雾里。这篇内容我就想从一个实际使用者的角度帮你把Matplotlib的“骨架”和“关节”拆解清楚。我们不讲那些华而不实的炫技就聊聊怎么理解它以及如何避开我当初踩过的那些坑。无论你是想画一张简单的折线图汇报工作还是为复杂的学术论文定制图表理解Matplotlib的设计哲学都是第一步也是最关键的一步。简单说Matplotlib是一个用于创建静态、动态、交互式可视化的综合库。它的强大之处在于其底层的灵活性和高度的可定制性但这也带来了相对陡峭的学习曲线。别被“绘图库”三个字简单概括了它更像一个画室Figure是画布Axes是画布上的一个个绘图区域你可以理解为子图而各种绘图函数如plot,scatter就是你的画笔和颜料。很多人代码跑不通或者图长得不对劲根源往往是对这几个核心对象的关系没理清。2. Matplotlib的设计哲学与核心架构解析2.1 为什么是“画室”而不是“打印机”很多更上层的库如Seaborn, Pandas的.plot()方法像是“智能打印机”你给数据它自动给你一个不错的图表。Matplotlib则不同它把控制权完全交给你。这种设计源于其科学绘图的基因研究人员需要对图表的每一个细节——从线条粗细、刻度标签到图例位置——进行精确控制以满足学术出版的严苛要求。这种控制是通过一个层次化的对象模型实现的。你可以把它想象成导演拍电影Figure对象相当于整个电影项目或舞台。它是最顶层的容器承载一切。你可以设置画布的大小、分辨率DPI、背景色等全局属性。Axes对象相当于舞台上的一个个具体场景或镜头。这是绝大多数绘图动作发生的地方。一个Figure可以包含一个或多个Axes。每个Axes都拥有自己独立的坐标系x轴和y轴、标题、刻度、图例等。我们常说的“子图”其实就是指一个Figure中的多个Axes对象。Axis对象这是Axes的组成部分特指坐标轴本身包括轴线、刻度线、刻度标签等。每个Axes通常包含两个Axis对象x轴和y轴3D图则有三个。Artist对象所有在画布上可见的元素都是Artist包括Figure、Axes、Axis以及更基本的Line2D线条、Text文本、Patch矩形、圆形等形状等。你看到的每一个点、每一条线、每一个文字标签都是一个Artist实例。理解这个层次关系至关重要。当你调用plt.plot()时Pyplot接口我们常用的import matplotlib.pyplot as plt会在“当前”Figure和“当前”Axes上创建一条Line2D对象。如果不存在它会自动创建它们。这种便捷性在简单脚本中很好用但在构建复杂图表或编写函数、类时直接操作对象才是更清晰、更可控的方式。2.2 两种编程接口Pyplot与面向对象这是初学者最容易混淆的地方也直接影响到代码的风格和可维护性。1. Pyplot接口快速但隐式这是我们最常见、入门教程最常用的方式。它提供了一个基于MATLAB风格的函数式接口维护了一个“当前图形”和“当前坐标轴”的状态机。import matplotlib.pyplot as plt import numpy as np # 生成数据 x np.linspace(0, 10, 100) y np.sin(x) # Pyplot风格 plt.figure(figsize(8, 5)) # 创建一个新的图形 plt.plot(x, y, labelsin(x)) # 在“当前”坐标轴上绘图 plt.xlabel(X Axis) plt.ylabel(Y Axis) plt.title(A Simple Sine Wave) plt.legend() plt.grid(True) plt.show()这种方式写起来快适合交互式环境如Jupyter Notebook和快速原型。但它的缺点是“状态性”。如果你在同一个脚本中创建多个图或者需要精细调整某个子图很容易因为“当前”状态指向错误的对象而出错代码逻辑也不够清晰。2. 面向对象接口清晰且强大这是构建复杂、可复用图表的首选也是理解Matplotlib精髓的关键。它显式地创建和引用Figure和Axes对象。import matplotlib.pyplot as plt import numpy as np x np.linspace(0, 10, 100) y np.sin(x) # 面向对象风格 fig, ax plt.subplots(figsize(8, 5)) # 显式创建图形和坐标轴对象 ax.plot(x, y, labelsin(x)) # 在特定的坐标轴对象ax上绘图 ax.set_xlabel(X Axis) # 使用set_方法设置属性 ax.set_ylabel(Y Axis) ax.set_title(A Simple Sine Wave (OO Style)) ax.legend() ax.grid(True) plt.show()plt.subplots()是一个极其常用的函数它一次性创建了一个Figure和一个或多个Axes子图并返回这两个对象。之后所有的操作都通过ax这个对象进行指向明确避免了状态混乱。我的实操心得除非是Jupyter Notebook里一两行的快速查看否则我强烈建议从一开始就习惯面向对象的写法。它让代码的意图更清晰更容易封装成函数或集成到更大的项目中。当你需要画子图时面向对象方式的优势将更加明显。3. 核心对象深度剖析与实操3.1 理解Figure你的画布Figure对象是顶层容器它的参数决定了图像的输出质量。fig plt.figure( figsize(10, 6), # 图形尺寸宽高单位英寸。这是打印或显示时的大小。 dpi150, # 分辨率每英寸点数。figsize * dpi 图像的像素尺寸。用于控制输出清晰度。 facecolorwhitesmoke, # 图形背景颜色 edgecolorblack, # 图形边框颜色 linewidth2, # 图形边框线宽 layoutconstrained # 布局引擎。constrained能自动调整子图元素避免重叠非常推荐。 ) # 在创建Figure后通常需要添加Axes才能绘图 ax fig.add_subplot(111) # 添加一个子图1行1列第1个关键参数解析figsize: 这是最容易出错的地方。很多人发现图上的字特别小或者特别大往往是因为figsize设置不当。一个经验法则是如果你打算将图表插入到A4纸大小的文档中单栏宽度通常在3.5英寸左右双栏在7英寸左右。可以先设定一个值然后根据预览调整。dpi: 屏幕显示通常72-150 DPI就够了。如果用于高清印刷或海报可能需要300 DPI甚至更高。注意高DPI会显著增加图像文件大小和内存占用。layout: 这是Matplotlib较新版本3.4引入的功能。设置为‘constrained’或‘tight’可以自动解决标签、标题重叠的问题比手动调整plt.tight_layout()更智能、更稳定建议作为默认选项。3.2 理解Axes真正的绘图战场Axes是灵魂所在。一个Figure可以包含多个Axes它们通过add_subplot或plt.subplots创建。创建单个与多个Axes# 方法1使用 add_subplot fig plt.figure(figsize(12, 4)) ax1 fig.add_subplot(1, 3, 1) # 1行3列第1个位置 ax2 fig.add_subplot(1, 3, 2) # 第2个位置 ax3 fig.add_subplot(1, 3, 3) # 第3个位置 # 方法2使用 plt.subplots 更推荐 fig, axs plt.subplots(nrows2, ncols2, figsize(10, 8)) # 2行2列共4个子图 # axs 是一个2x2的NumPy数组可以通过索引访问如 axs[0, 0], axs[0, 1]... axs[0, 0].plot([1, 2, 3], [1, 4, 9]) axs[0, 0].set_title(Top Left) # 方法3创建不规则网格的子图 fig plt.figure(figsize(10, 6)) # 使用GridSpec实现更灵活的布局 import matplotlib.gridspec as gridspec gs gridspec.GridSpec(2, 2, figurefig) # 2行2列网格 ax_big fig.add_subplot(gs[0, :]) # 第0行所有列跨列 ax_small1 fig.add_subplot(gs[1, 0]) # 第1行第0列 ax_small2 fig.add_subplot(gs[1, 1]) # 第1行第1列Axes的核心属性设置绘图之后90%的定制工作都在调整Axes的属性。fig, ax plt.subplots() x np.linspace(0, 2*np.pi, 100) ax.plot(x, np.sin(x), labelSine, colorroyalblue, linewidth2, linestyle-, markero, markersize4) # 1. 设置坐标轴标签和标题 ax.set_xlabel(Time (s), fontsize12, fontweightbold) ax.set_ylabel(Amplitude, fontsize12) ax.set_title(Waveform Demonstration, fontsize14, pad20) # pad是标题与图的间距 # 2. 设置坐标轴范围 ax.set_xlim(0, 2*np.pi) # 或 ax.set_xlim([0, 2*np.pi]) ax.set_ylim(-1.5, 1.5) # 3. 设置刻度 ax.set_xticks([0, np.pi/2, np.pi, 3*np.pi/2, 2*np.pi]) ax.set_xticklabels([0, r$\frac{\pi}{2}$, r$\pi$, r$\frac{3\pi}{2}$, r$2\pi$], fontsize10) # 使用LaTeX渲染数学符号 ax.set_yticks([-1, -0.5, 0, 0.5, 1]) # 4. 添加网格和图例 ax.grid(True, whichboth, linestyle--, linewidth0.5, alpha0.7) # whichboth表示主次刻度都加网格alpha是透明度 ax.legend(locupper right, frameonTrue, shadowTrue, fontsize10) # loc参数控制图例位置 # 5. 设置坐标轴比例如对数坐标 # ax.set_xscale(log) # ax.set_yscale(log) # 6. 添加文本注释 ax.text(np.pi/2, 1, Peak, hacenter, vabottom, fontsize11, styleitalic) # ha, va控制水平/垂直对齐 # 添加带箭头的注释 ax.annotate(Zero Crossing, xy(np.pi, 0), xytext(np.pi, -0.8), arrowpropsdict(arrowstyle-, connectionstylearc3, colorred), fontsize10)注意事项set_xticklabels的调用必须在set_xticks之后否则自定义标签无法生效。另外直接使用ax.set_xticks([])可以隐藏刻度这在绘制某些特殊图表如热力图旁边的时间轴时很有用。3.3 绘图函数选型指南Matplotlib提供了数十种绘图函数以下是最常用的一些及其典型场景函数名主要用途关键参数示例适用场景plot绘制折线图或点图x, y, fmt(如‘ro--’),linewidth,marker,label趋势分析、时间序列、函数曲线scatter绘制散点图x, y, s(点大小),c(颜色),marker,alpha(透明度)相关性分析、聚类可视化、分布观察bar/barh绘制水平条形图x, height, width, color, edgecolor,tick_label分类数据比较、数量统计hist绘制直方图x, bins, density(是否归一化),alpha,edgecolor数据分布探索、概率密度估计imshow显示图像或2D数组X, cmap(颜色映射),aspect(纵横比),interpolation(插值)图像处理、矩阵可视化、热力图pie绘制饼图x, labels, autopct(百分比格式),explode(突出部分)构成比例展示需谨慎使用boxplot绘制箱线图x, whis(须线范围),showmeans(显示均值)多组数据分布比较、异常值检测fill_between填充两条曲线间区域x, y1, y2, where, alpha, color置信区间、误差带、区域高亮一个综合示例在同一Axes上叠加多种图形fig, ax plt.subplots(figsize(9, 5)) # 准备数据 categories [A, B, C, D] values_bar [23, 45, 56, 78] values_line [25, 40, 60, 75] x_pos np.arange(len(categories)) # 绘制条形图为主 bars ax.bar(x_pos, values_bar, width0.6, colorskyblue, edgecolornavy, labelValue (Bar), alpha0.8) # 在条形上方添加折线图次坐标轴 ax2 ax.twinx() # 创建共享x轴的第二个y轴 line ax2.plot(x_pos, values_line, colorcrimson, markers, linewidth2, labelTrend (Line)) # 个性化条形图在每个条形顶部添加数值 for bar in bars: height bar.get_height() ax.text(bar.get_x() bar.get_width()/2., height 1, f{height}, hacenter, vabottom, fontsize9) # 设置标签和标题 ax.set_xlabel(Category, fontsize12) ax.set_ylabel(Bar Value, fontsize12, colorskyblue) ax2.set_ylabel(Line Value, fontsize12, colorcrimson) ax.set_title(Combination Chart: Bar and Line, fontsize14, pad15) ax.set_xticks(x_pos) ax.set_xticklabels(categories) # 合并图例技巧点 lines_labels_ax ax.get_legend_handles_labels() lines_labels_ax2 ax2.get_legend_handles_labels() lines, labels lines_labels_ax[0] lines_labels_ax2[0], lines_labels_ax[1] lines_labels_ax2[1] ax.legend(lines, labels, locupper left) plt.show()这个例子展示了如何在一个坐标轴上组合不同类型的图表并使用twinx()创建双y轴这是商业报告中常见的图表形式。关键在于通过get_legend_handles_labels()方法合并两个坐标轴的图例。4. 样式与美化从“能看”到“好看”Matplotlib的默认样式‘default’比较朴素。美化图表主要涉及两方面全局样式设置和局部元素调整。4.1 使用样式表Matplotlib提供了多种内置样式表可以一键改变图表风格。print(plt.style.available) # 查看所有可用样式 plt.style.use(seaborn-v0_8-darkgrid) # 应用样式如‘ggplot’ ‘seaborn’ ‘fivethirtyeight’ # 应用样式后创建的图表都会继承该风格 fig, ax plt.subplots() # ... 绘图代码样式表会修改颜色循环、网格样式、背景色、字体大小等全局属性。你可以在绘图前应用它。注意样式表是全局的会影响之后创建的所有图表。4.2 手动精细调整对于出版级图表通常需要手动调整。1. 颜色与颜色映射命名颜色Matplotlib支持大量HTML/CSS颜色名称如‘tomato’,‘steelblue’,‘lightgray’。十六进制/RGB/A‘#1f77b4’,(0.1, 0.2, 0.5),(0.1, 0.2, 0.5, 0.8)最后一位是透明度。颜色映射用于将标量数据映射到颜色常见于scatter的c参数或imshow。内置的cmap非常多选择有讲究‘viridis’,‘plasma’,‘inferno’,‘magma’感知均匀的渐变色适合连续数据且对色盲友好。‘coolwarm’,‘RdBu’,‘PiYG’发散色系适合有正负或临界值的数据。‘Set1’,‘Set2’,‘tab20c’定性色系适合分类数据。# 使用颜色映射的示例 data np.random.randn(50, 50) fig, ax plt.subplots() im ax.imshow(data, cmapcoolwarm, interpolationnearest) fig.colorbar(im, axax) # 添加颜色条2. 字体与文本中文字体显示是常见问题。# 解决方案指定中文字体路径以思源黑体为例 import matplotlib # 方法1临时设置推荐不影响其他图表 font_path /path/to/your/SourceHanSansSC-Regular.otf # 替换为你的字体文件路径 font_prop matplotlib.font_manager.FontProperties(fnamefont_path) plt.rcParams[font.sans-serif] [font_prop.get_name()] plt.rcParams[axes.unicode_minus] False # 解决负号显示为方块的问题 # 方法2修改全局rcParams永久生效需谨慎 # plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei] # Windows常用 # plt.rcParams[font.sans-serif] [WenQuanYi Micro Hei, DejaVu Sans] # Linux常用 # plt.rcParams[axes.unicode_minus] False fig, ax plt.subplots() ax.set_xlabel(时间轴, fontpropertiesfont_prop, fontsize12) # 单个文本指定 ax.set_title(包含中文的标题, fontpropertiesfont_prop)3. 布局与间距使用plt.tight_layout()或fig.set_layout_engine(‘constrained’)可以自动调整子图参数使标签、标题等不重叠。对于极端情况可以使用plt.subplots_adjust()手动调整。fig, axs plt.subplots(2, 2, figsize(10, 8)) # ... 在各个axs上绘图 fig.tight_layout(pad2.0, w_pad3.0, h_pad2.0) # pad是整体边距w_pad/h_pad是子图间宽/高间距 # 或者更精细的手动控制 # fig.subplots_adjust(left0.1, right0.95, bottom0.1, top0.9, wspace0.3, hspace0.4)5. 实战从数据到出版级图表让我们用一个完整的例子串联起从数据准备到图表美化的全过程。假设我们要可视化某产品上半年各月的销售额和用户增长率。import matplotlib.pyplot as plt import numpy as np import matplotlib # 1. 数据准备 months [Jan, Feb, Mar, Apr, May, Jun] sales [125, 138, 150, 165, 158, 190] # 单位万 growth_rate [5.2, 8.1, 9.5, 12.3, 10.8, 15.6] # 单位% # 2. 创建图形和坐标轴使用面向对象方式 fig, ax1 plt.subplots(figsize(10, 6)) # 设置全局字体假设已解决中文问题 plt.rcParams[font.size] 11 # 3. 绘制主坐标轴数据销售额 - 条形图 x_pos np.arange(len(months)) width 0.35 # 条形宽度 bars ax1.bar(x_pos - width/2, sales, width, color#2E86AB, edgecolorblack, linewidth0.8, labelSales (10k), alpha0.85, zorder2) # zorder控制绘制顺序数值大的在上层 # 4. 个性化条形添加数据标签 for bar in bars: height bar.get_height() ax1.annotate(f{height}, xy(bar.get_x() bar.get_width() / 2, height), xytext(0, 3), # 垂直偏移3个点 textcoordsoffset points, hacenter, vabottom, fontsize10, fontweightbold) # 5. 设置主坐标轴属性 ax1.set_xlabel(Month, fontsize12, fontweightbold) ax1.set_ylabel(Sales Volume (10k), fontsize12, fontweightbold, color#2E86AB) ax1.set_title(H1 Sales Performance User Growth Trend, fontsize14, fontweightbold, pad20) ax1.set_xticks(x_pos) ax1.set_xticklabels(months, fontsize11) ax1.tick_params(axisy, labelcolor#2E86AB) ax1.grid(axisy, linestyle--, alpha0.7, zorder1) # 只显示水平网格线 # 6. 创建次坐标轴用户增长率 - 折线图 ax2 ax1.twinx() line ax2.plot(x_pos, growth_rate, color#E15554, markero, linewidth2.5, markersize8, labelGrowth Rate (%), zorder3) ax2.set_ylabel(Growth Rate (%), fontsize12, fontweightbold, color#E15554) ax2.tick_params(axisy, labelcolor#E15554) # 设置次坐标轴范围让折线图在图形中上部显示更美观 ax2.set_ylim(0, max(growth_rate) * 1.2) # 7. 合并图例 lines1, labels1 ax1.get_legend_handles_labels() lines2, labels2 ax2.get_legend_handles_labels() ax1.legend(lines1 lines2, labels1 labels2, locupper left, frameonTrue, shadowTrue, fancyboxTrue) # 8. 自动优化布局 fig.tight_layout() # 9. 保存图表 fig.savefig(h1_sales_performance.png, dpi300, bbox_inchestight, facecolorwhite, edgecolornone) # bbox_inchestight 会裁剪掉图形周围的空白区域 plt.show()这段代码的关键点解析颜色选择使用了十六进制颜色码#2E86AB,#E15554比简单的‘blue’ ‘red’更专业、更易保持一致。数据标签使用annotate而非text可以更灵活地控制标签位置通过xytext的偏移量。网格线只打开了y轴方向的网格线axis‘y’避免网格线过于杂乱同时通过zorder确保网格在条形后面。次坐标轴范围手动设置了ax2.set_ylim让折线图在图形中上部显示视觉上更平衡。这是美化中很重要的一步避免折线被压扁在底部。图例合并标准操作确保两个坐标轴的图例能合并显示在一个框内。保存图片dpi300保证高清输出bbox_inches‘tight’自动去除多余白边是保存出版级图片的必备参数。6. 常见问题与排查技巧实录即使理解了原理在实际操作中还是会遇到各种奇怪的问题。下面是我总结的一些高频“坑点”和解决方法。6.1 图形不显示或显示异常问题在脚本中写了plt.plot()但运行后没有弹出图形窗口。排查检查是否在最后调用了plt.show()。在非交互式环境如.py脚本中必须调用plt.show()来阻塞程序并显示图形。如果你在使用Jupyter Notebook确保使用了正确的魔术命令%matplotlib inline静态嵌入或%matplotlib notebook交互式。Notebook中通常不需要plt.show()。某些IDE如PyCharm的科学模式可能需要额外配置。尝试在设置中启用“SciView”或检查Matplotlib的后端设置。问题图形窗口一闪而过。排查在脚本中plt.show()是阻塞式的但如果你在它之后还有代码如print语句并且脚本很快执行完毕窗口可能会随程序结束而关闭。确保plt.show()是脚本中最后执行的与绘图相关的代码。对于需要持续显示的可以考虑使用plt.ion()开启交互模式或使用plt.pause(interval)。6.2 中文显示为方框问题标题、标签中的中文显示为小方框。解决方案如上文“字体与文本”部分所述根本原因是Matplotlib默认字体不包含中文字形。一劳永逸法找到系统支持中文的字体如Windows的‘SimHei’ Mac的‘PingFang SC’并将其名称添加到rcParams[‘font.sans-serif’]列表的开头并设置rcParams[‘axes.unicode_minus’] False。你可以将这段配置代码放在一个单独的配置文件或每次绘图前执行。临时指定法使用FontProperties对象在具体的set_xlabel,set_title等函数中传入fontproperties参数。这种方法更灵活不影响其他图表。检查字体路径如果使用自定义字体文件.ttf/.otf确保路径正确并使用matplotlib.font_manager.FontProperties(fnamepath)来加载。6.3 子图元素重叠或布局混乱问题多个子图的标题、标签挤在一起或者图形边缘被裁剪。解决方案首选在创建Figure时指定layout‘constrained’Matplotlib 3.6或使用fig.set_layout_engine(‘constrained’)。这是最新的、最推荐的自动布局引擎。次选在绘图完成后调用fig.tight_layout()或plt.tight_layout()。它可以自动调整子图参数使它们适应图形区域。通过pad,w_pad,h_pad参数微调间距。手动微调如果自动布局仍不满意使用fig.subplots_adjust(left, right, bottom, top, wspace, hspace)进行像素级精确控制。参数值是相对于图形宽高的比例0-1之间。这需要反复尝试。终极方案对于极其复杂的布局放弃subplots使用matplotlib.gridspec.GridSpec或plt.subplot2grid进行更灵活的手动网格划分。6.4 保存的图片质量差或有白边问题保存的.png或.jpg图片模糊或者周围有大量不必要的空白。解决方案提高DPI在fig.savefig()中设置dpi300或更高。DPI是“每英寸点数”值越高图像像素越多越清晰但文件也越大。去除白边使用bbox_inches‘tight’参数。它会计算图形的实际内容边界并只保存这部分区域。设置背景默认保存的图片背景是透明的。如果你需要白色背景设置facecolor‘white’。如果需要透明背景则设置transparentTrue。格式选择.png无损压缩适合有线条、文字、颜色块的图表。.jpg有损压缩文件小适合颜色渐变更平滑的图像如照片但不适合线条图容易产生 artifacts。.svg矢量格式无限放大不失真适合用于出版物或网页但文件是XML文本可能较大。.pdf矢量格式适合包含多页或用于LaTeX文档。6.5 性能问题绘制大量数据点时卡顿问题当用plot或scatter绘制数十万甚至上百万个数据点时图形渲染极慢操作卡顿。解决方案数据降采样这是最有效的方法。对于可视化趋势而言不需要每一个点。可以使用np.random.choice随机采样或者按固定间隔取点。使用更高效的方法对于散点图使用plt.plot的‘,’或‘.’标记样式如plt.plot(x, y, ‘,’)比plt.scatter快得多但功能单一。考虑使用ax.hexbin六边形分箱图或ax.hist2d二维直方图来可视化超大规模散点数据的密度分布它们本质上是聚合统计。启用快速样式在绘图前设置plt.style.use(‘fast’)这会禁用一些美化功能以提升速度。考虑其他库对于真正的海量数据交互式可视化Matplotlib可能不是最佳选择可以评估Datashader先栅格化再渲染或Bokeh、Plotly等基于WebGL的交互式库。6.6 图例Legend显示不正常问题图例不显示、显示错误标签、或位置不合适。排查确保有label在绘图函数如ax.plot(…, label‘My Line’)中必须指定label参数图例才会收集这个对象。手动创建图例如果绘图时忘了加label或者需要自定义图例项可以使用from matplotlib.lines import Line2D custom_lines [Line2D([0], [0], color‘blue’, lw2), Line2D([0], [0], color‘red’, lw2, linestyle‘--’)] ax.legend(custom_lines, [‘Model A’, ‘Model B’])控制图例位置loc参数可以是字符串如‘upper right’,‘best’也可以是元组表示坐标轴坐标系下的位置如(0.5, 0.5)表示图形中心。bbox_to_anchor参数可以更灵活地将图例放置在坐标轴外部例如loc‘center left’, bbox_to_anchor(1, 0.5)会将图例放在坐标轴右侧中间。图例顺序混乱默认按添加顺序。如果需要特定顺序可以在ax.legend()中通过handles参数显式传入一个艺术家对象列表来指定顺序。掌握Matplotlib就像学习一门新的语言语法函数是基础但更重要的是理解其思维方式对象层次。从面向对象的接口开始练习多动手复现和修改别人的代码遇到问题优先查阅官方文档虽然有时比较晦涩并善用dir()和help()函数来探索对象的属性和方法。当你能够不假思索地画出心中所想的那张图时你会发现数据探索和结果展示的效率得到了质的提升。