ARTICLE DETAIL

资讯详情

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

Matplotlib图形绘制方法精讲:从面向对象架构到多子图布局实战

Matplotlib图形绘制方法精讲:从面向对象架构到多子图布局实战 1. 为什么“从零开始”学Matplotlib却要从“图形绘制方法”切入很多朋友一听到“从零开始学习”下意识就会去找那种从安装Python、配置环境开始的保姆级教程。这当然没错但对于Matplotlib这个库如果你真的按部就班从import matplotlib.pyplot as plt开始然后学plt.plot()、plt.scatter()这些基础函数大概率会陷入一个怪圈教程里的图都画出来了但一到自己的项目里想调个图例位置、改个坐标轴刻度、或者把几个子图排列得好看点就立刻抓瞎代码越写越乱最后只能去Stack Overflow上复制粘贴看不懂的魔改代码。这就是典型的“知道函数但不懂方法”。函数是工具方法是使用工具的逻辑和体系。这篇“特别篇”的目的就是帮你跳过那个混乱的摸索期直接建立起Matplotlib图形绘制的“方法观”。我们不追求面面俱到地介绍所有函数而是聚焦于几个核心的、能解决你80%绘图需求的“方法”。当你掌握了这些方法再去看官方文档或者复杂案例你会发现它们都是这些基础方法的组合与变体学起来会快得多。最近在技术社区里关于Matplotlib的讨论除了常规的“如何画得更好看”还出现了一些很有意思的实践比如结合NumPy进行矩阵计算后可视化像“numpy 计算matplotlib画的方块邻居元素之和”这类问题这其实就涉及到了数据计算与图形元素精准映射的方法。而“pr中图形绘制中的控制健”这个搜索词虽然可能是其他领域的误输入也侧面反映了大家对“控制”和“交互”的渴望——在Matplotlib里这就是通过对象方法和事件回调来实现的。所以这篇内容会紧扣“方法”二字让你不仅能画出图更能理解图背后的对象层级和控制逻辑从而真正掌控你的可视化。2. 理解Matplotlib的“两层楼”架构为什么你的图总调不好几乎所有Matplotlib新手都会踩的第一个大坑就是混淆了它的两种接口风格pyplot状态机接口和面向对象OO接口。很多入门教程只教前者因为它简单几行代码就能出图但这恰恰是导致后期代码难以维护和定制的根源。2.1 快速出图但“失控”的pyplot模式我们最熟悉的写法是这样的import matplotlib.pyplot as plt import numpy as np x np.linspace(0, 10, 100) y np.sin(x) plt.figure(figsize(8, 4)) # 创建一个图形 plt.plot(x, y, labelsin(x)) # 在当前图形和坐标轴上画线 plt.title(A Simple Sine Wave) # 给“当前”坐标轴加标题 plt.xlabel(X Axis) plt.ylabel(Y Axis) plt.legend() plt.grid(True) plt.show()这段代码能完美运行。plt.plot()、plt.title()、plt.xlabel()这些函数仿佛有记忆它们自动在“当前”的图形Figure和坐标轴Axes上操作。pyplot模块在背后维护了一个状态机记录着当前激活的是哪个图形和坐标轴。这种方法对于写脚本、做一次性探索分析非常快捷。但是它的弊端在需要精细控制时暴露无遗隐式依赖你永远要小心“当前”激活的是哪个坐标轴尤其是在画子图subplot时一个不小心就会把内容画错地方。代码难以模块化如果你想写一个函数来定制化某个图这个函数内部如果调用了plt.xxx()它会影响到函数外部的全局绘图状态带来意想不到的副作用。功能受限很多高级定制功能在pyplot接口里要么没有要么用起来很别扭。2.2 清晰且强大的面向对象OO模式这才是Matplotlib设计的核心也是我们这篇“方法篇”的基石。它的理念非常直观显式地创建图形对象并在这些对象上调用方法。同样的图用OO模式重写import matplotlib.pyplot as plt import numpy as np x np.linspace(0, 10, 100) y np.sin(x) # 1. 显式创建图形画布和坐标轴子图 fig, ax plt.subplots(figsize(8, 4)) # 2. 所有绘图和设置操作都在具体的ax对象上进行 line, ax.plot(x, y, labelsin(x)) # ax.plot() 返回一个线条对象列表 ax.set_title(A Simple Sine Wave) # 注意是 set_title不是 plt.title() ax.set_xlabel(X Axis) ax.set_ylabel(Y Axis) ax.legend() ax.grid(True) # 3. 显示图形 plt.show()关键变化fig, ax plt.subplots()这行代码同时创建了图形fig和一个坐标轴ax。fig好比是画布ax是画布上的一个坐标系可以有很多个。ax.plot(),ax.set_title(),ax.set_xlabel()所有操作都是针对ax这个明确的对象。代码意图一目了然没有任何隐式状态。line, ax.plot(...)plot方法返回一个包含线条对象的列表。这里用逗号赋值将其解包出来。这个line对象后续可以用来单独设置线条属性如颜色、宽度实现更精细的控制。为什么OO模式是必须掌握的方法因为它将Matplotlib的三个核心对象层级清晰地暴露给你Figure图形顶级容器相当于画布或窗口。可以设置画布大小figsize、DPI、背景色等。Axes坐标轴这是绝大多数魔法发生的地方。一个Figure可以包含多个Axes子图。我们常说的“画图”其实90%的工作是在Axes对象上完成的绘图plot, scatter, bar...、设置坐标轴范围、刻度、标签、图例、网格等。Axis轴注意是Axis不是Axes。指的是每个坐标轴上的那条线x轴、y轴控制着刻度位置、刻度标签格式等。理解了这个“两层楼”Figure - Axes或“三层楼”Figure - Axes - Axis的架构你就掌握了Matplotlib的“地图”。无论你想修改什么都知道该去找哪个对象。实操心得从今天起强迫自己使用OO模式。plt.subplots()是你的好朋友。即使是单图也写成fig, ax plt.subplots()。这一个小小的习惯能为你后续节省大量的调试时间。3. 核心绘制方法不止是plot和scatter掌握了OO模式我们来看看在Axes对象上有哪些核心的绘图“方法”。这些方法决定了你呈现数据的“视觉编码”方式。3.1 折线图与散点图数据趋势与分布的基石ax.plot()和ax.scatter()是最常用的两个方法但它们的参数远不止x, y。ax.plot()- 连接点以显示趋势# 基础用法 ax.plot(x, y, colorblue, linewidth2, linestyle--, markero, markersize8, labelData Trend) # 关键参数解析 # - color: 颜色。可以用名字(red)、十六进制(#FF5733)或RGB元组((0.1, 0.2, 0.5))。 # - linewidth (lw): 线宽。整数或浮点数。 # - linestyle (ls): 线型。-实线--虚线:点线-.点划线。 # - marker: 数据点标记。o圆圈s方块^上三角*星星等。 # - markersize (ms): 标记大小。 # - label: 为线条设置标签用于图例。plot方法的核心思想是“连接”适合展示连续数据的变化趋势、时间序列等。ax.scatter()- 展示个体与分布# 生成随机数据 np.random.seed(42) x_scatter np.random.randn(100) y_scatter np.random.randn(100) colors np.random.rand(100) # 用于颜色映射的数值 sizes 1000 * np.random.rand(100) # 点的大小数组 # 绘制散点图用颜色和大小传递第三、第四维信息 scatter ax.scatter(x_scatter, y_scatter, ccolors, ssizes, alpha0.6, cmapviridis, edgecolorsblack, linewidth0.5) # 添加颜色条(Colorbar)解释颜色映射 fig.colorbar(scatter, axax, labelColor Value) # 关键参数解析 # - c: 颜色。可以是一个颜色字符串所有点同色也可以是一个数值序列此时颜色由cmap参数和该数值共同决定。 # - s: 点的大小。可以是一个标量所有点一样大也可以是一个数组每个点不同大小。 # - alpha: 透明度0-1之间。 # - cmap: 颜色映射表。当c是数值时通过此色彩映射将数值映射为颜色。viridis, plasma, coolwarm等都是常用选项。 # - edgecolors: 点的边缘颜色。 # - linewidth: 边缘线宽。scatter方法的核心是“展示每个点”适合展示数据分布、聚类、相关性并且能通过点的大小、颜色和透明度轻松编码更多维度的信息是探索性数据分析EDA的利器。3.2 柱状图与直方图比较与分布可视化ax.bar()与ax.barh()- 分类比较categories [A, B, C, D, E] values [23, 45, 56, 78, 33] errors [2, 3, 4, 5, 2] # 误差值 # 垂直柱状图 bars ax.bar(categories, values, yerrerrors, capsize5, colorskyblue, edgecolornavy, linewidth1.5) # 在柱子上方添加数值标签 for bar in bars: height bar.get_height() ax.text(bar.get_x() bar.get_width() / 2., height 1, f{height}, hacenter, vabottom, fontsize10) # 水平柱状图只需使用 ax.barh(y_positions, widths, ...)柱状图用于比较不同类别的数值。yerr/xerr参数可以添加误差棒capsize控制误差棒端帽的大小。ax.text()用于添加自定义文本标签这是美化图表的关键技巧。ax.hist()- 数据分布探查data np.random.randn(1000) # 1000个符合标准正态分布的随机数 # 绘制直方图 n, bins, patches ax.hist(data, bins30, densityTrue, alpha0.7, colorsteelblue, edgecolorblack) # 叠加一个理论上的概率密度函数PDF曲线例如正态分布PDF from scipy.stats import norm x_pdf np.linspace(min(data), max(data), 100) y_pdf norm.pdf(x_pdf, data.mean(), data.std()) ax.plot(x_pdf, y_pdf, r-, linewidth2, labelNormal PDF) ax.legend() # 关键参数解析 # - bins: 箱子数量。可以是整数也可以是一个定义箱子边界的序列。 # - density: 如果为Truey轴显示的是概率密度面积和为1而非频数。 # - alpha: 透明度。 # - histtype: 直方图类型如bar(默认) barstacked, step, stepfilled。 # - range: 数据的范围超出范围的数据将被忽略。直方图将连续数据分段统计每个区间的频数或频率是观察数据分布形态是否正态、是否偏斜、有无多峰最基本也最重要的工具。densityTrue参数使其变为概率密度直方图便于与理论分布对比。3.3 高级方法示例填充、等高线与矢量场为了应对更复杂的科学可视化需求Matplotlib提供了丰富的高级绘图方法。ax.fill_between()- 填充区域常用于表示置信区间x_fill np.linspace(0, 10, 100) y_mean np.sin(x_fill) y_std 0.2 * np.abs(np.cos(x_fill)) # 假设标准差随x变化 y_upper y_mean y_std y_lower y_mean - y_std ax.plot(x_fill, y_mean, b-, labelMean) ax.fill_between(x_fill, y_lower, y_upper, colorblue, alpha0.2, label±1 Std Dev) ax.legend()这个方法在展示模型预测的不确定性、误差范围时极其有用。alpha参数控制填充区域的透明度避免遮盖底层线条。ax.contour()与ax.contourf()- 绘制等高线填充# 生成网格数据 x_contour np.linspace(-3, 3, 100) y_contour np.linspace(-3, 3, 100) X, Y np.meshgrid(x_contour, y_contour) Z np.sin(np.sqrt(X**2 Y**2)) # 一个二维函数例如震荡衰减曲面 # 绘制等高线 contour_lines ax.contour(X, Y, Z, levels10, colorsblack, linewidths0.5) ax.clabel(contour_lines, inlineTrue, fontsize8) # 在线条上添加标签 # 在同一坐标轴上叠加填充等高线 contour_fill ax.contourf(X, Y, Z, levels10, cmapRdYlBu_r, alpha0.7) fig.colorbar(contour_fill, axax, labelZ Value) # 添加颜色条 # 关键点np.meshgrid是生成二维网格坐标的必备工具。 # levels参数控制等高线的数量或具体数值。等高线图常用于地理、气象和工程领域展示二维标量场如高度、温度、压力的分布。contour画线contourf填充颜色两者结合信息更丰富。ax.quiver()- 绘制矢量场箭头图x_quiver np.arange(0, 2*np.pi, 0.5) y_quiver np.arange(0, 2*np.pi, 0.5) X_q, Y_q np.meshgrid(x_quiver, y_quiver) # 定义一个矢量场例如旋涡场 U np.sin(Y_q) # X方向分量 V np.cos(X_q) # Y方向分量 ax.quiver(X_q, Y_q, U, V, colorred, anglesxy, scale_unitsxy, scale5, width0.005) ax.set_xlim(0, 2*np.pi) ax.set_ylim(0, 2*np.pi) ax.set_aspect(equal) # 保证x和y轴比例相同箭头方向才准确quiver图用于可视化矢量场如流体速度场、电磁场、梯度方向等。关键参数anglesxy和scale_unitsxy确保了箭头在数据坐标系中的方向和长度是准确的这是最容易出错的地方。避坑指南当你的箭头图看起来方向或大小很奇怪时十有八九是angles和scale_units参数没设置对。对于在常规笛卡尔坐标系中的数据设置anglesxy, scale_unitsxy是最稳妥的选择。4. 多子图布局的艺术从混乱到有序一张图里放多个子图Subplot是科研论文和报告中的常态。Matplotlib提供了多种强大的布局方法但方法选不对排版就会一团糟。4.1 基础网格布局plt.subplots与add_subplotplt.subplots一次性创建网格推荐这是最清晰、最常用的方法。# 创建一个2行3列的图形网格 fig, axs plt.subplots(nrows2, ncols3, figsize(12, 8), constrained_layoutTrue) # constrained_layout自动调整间距 # axs 是一个 2x3 的 numpy 数组存储着6个Axes对象 print(axs.shape) # 输出(2, 3) # 像访问二维数组一样访问每个坐标轴进行绘图 axs[0, 0].plot(x, y, r-) axs[0, 0].set_title(Subplot (0,0)) axs[1, 2].scatter(x_scatter, y_scatter) axs[1, 2].set_title(Subplot (1,2)) # 如果需要将整个网格的x轴或y轴标签统一可以遍历设置 for ax in axs.flat: ax.set(xlabelCommon X, ylabelCommon Y) ax.label_outer() # 一个有用的技巧只显示最外围子图的刻度标签避免重叠constrained_layoutTrue参数能自动调整子图间的间距和与画布边缘的间距解决标签重叠问题强烈建议始终开启。add_subplot动态添加子图fig plt.figure(figsize(10, 6)) # 添加第一个子图占据1行2列中的第1个位置 ax1 fig.add_subplot(1, 2, 1) # 参数行数列数索引位置 ax1.plot(x, y) # 添加第二个子图 ax2 fig.add_subplot(1, 2, 2) ax2.hist(data, bins20) # 这种方法在动态、条件性创建子图时更灵活。这种方法更“手动”适合子图布局不规则或需要动态生成的情况。4.2 复杂布局GridSpec与subplot_mosaic当需要子图大小不一时基础网格就不够用了。GridSpec- 灵活的网格规划师import matplotlib.gridspec as gridspec fig plt.figure(figsize(10, 8)) # 定义一个3行3列的网格 gs gridspec.GridSpec(3, 3, figurefig, wspace0.4, hspace0.4) # 让第一个子图占据第一整行 ax_big fig.add_subplot(gs[0, :]) # 第0行所有列 ax_big.plot(x, y**2) ax_big.set_title(Big Plot - Row 0, All Columns) # 第二行第一列的子图 ax_small1 fig.add_subplot(gs[1, 0]) ax_small1.scatter(x[:50], y[:50]) # 第二行第二、三列合并的子图 ax_small2 fig.add_subplot(gs[1, 1:]) # 第1行第1列到最后一列 ax_small2.hist(data) # 第三行所有列但只占一半高度我们可以进一步嵌套GridSpec # 但更简单的方法是直接指定位置 ax_bottom fig.add_subplot(gs[2, :]) ax_bottom.bar(categories, values)GridSpec通过类似NumPy数组的切片语法:表示所有start:stop表示范围来定义子图占据的网格区域功能非常强大。subplot_mosaic- 直观的“马赛克”布局Matplotlib 3.3这是我最推荐给新手的复杂布局方法因为它用可视化的字符网格来定义布局非常直观。# 定义一个布局马赛克用字符串代表每个子图区域 mosaic_layout [A A A] [B C D] [B E E] # ‘A’区域占第一行整行‘B’区域占第二、三行的第一列等等。 fig, ax_dict plt.subplot_mosaic(mosaic_layout, figsize(10, 8), constrained_layoutTrue) # ax_dict 是一个字典键是你在布局中定义的字符 ax_dict[A].plot(x, y, g-) ax_dict[A].set_title(Area A) ax_dict[B].scatter(x_scatter, y_scatter, ccolors, cmapviridis) ax_dict[B].set_title(Area B) ax_dict[C].hist(data, bins20, orientationhorizontal) ax_dict[C].set_title(Area C) # ... 设置其他区域subplot_mosaic极大地简化了复杂布局的代码让意图一目了然。如果你的Matplotlib版本在3.3以上请优先考虑它。布局经验先规划后编码在写代码前最好在纸上画一下你想要的布局草图。善用constrained_layout它能解决90%的标签重叠和裁剪问题。统一风格对于一组相关的子图尽量使用相同的颜色映射、刻度范围和图例风格以方便比较。共享坐标轴plt.subplots的sharex和sharey参数可以让子图共享x轴或y轴节省空间且便于对比。例如plt.subplots(2, 2, sharexTrue, shareyTrue)。5. 精细化控制让你的图表从“能用”到“好看”数据可视化不仅是展示信息更是传递洞察。一个精心修饰的图表能极大提升可读性和专业性。这部分我们深入Axes和Figure对象的细节控制方法。5.1 坐标轴与刻度的完全掌控默认的刻度标签和范围往往不符合我们的需求。设置范围与比例ax.set_xlim([0, 12]) # 手动设置x轴范围 ax.set_ylim([-1.5, 1.5]) # 手动设置y轴范围 ax.set_aspect(equal) # 设置x轴和y轴的单位长度相等对于几何图形、地图等至关重要。 # 其他比例选项auto默认一个数字如1表示y轴单位长度是x轴的1倍自定义刻度位置与标签# 1. 设置主要刻度位置 ax.set_xticks([0, np.pi/2, np.pi, 3*np.pi/2, 2*np.pi]) # 2. 为这些位置设置自定义标签 ax.set_xticklabels([0, r$\frac{\pi}{2}$, r$\pi$, r$\frac{3\pi}{2}$, r$2\pi$], fontsize12) # 使用LaTeX语法渲染数学符号r表示原始字符串避免转义问题。 # 3. 设置次要刻度更细的网格 from matplotlib.ticker import MultipleLocator ax.xaxis.set_minor_locator(MultipleLocator(np.pi/4)) # 在x轴上每pi/4设置一个次要刻度 # 4. 格式化刻度标签例如显示为百分比 import matplotlib.ticker as mtick ax.yaxis.set_major_formatter(mtick.PercentFormatter(1.0)) # 将y轴刻度格式化为百分比假设数据范围是0-1对数坐标轴ax.set_xscale(log) # 将x轴设置为对数坐标 ax.set_yscale(log) # 将y轴设置为对数坐标 # 这对于展示跨越多个数量级的数据如地震震级、恒星亮度、微生物数量非常有用。 # 设置后plot和scatter等方法会自动在对数坐标系下正确绘制。5.2 图例、文本与注释的艺术图例Legend的精细控制line1, ax.plot(x, y1, labelModel A, lw2) line2, ax.plot(x, y2, labelModel B, ls--) scatter ax.scatter(x_scatter, y_scatter, labelObservations, cgreen, s20) # 基础图例 ax.legend() # 高级控制 legend ax.legend(locupper left, # 位置best, upper right, lower left等或元组(x, y) frameonTrue, # 是否显示边框 shadowTrue, # 是否显示阴影 fancyboxTrue, # 边框是否圆角 framealpha0.9, # 边框透明度 borderpad1, # 边框内部填充 titleLegend Title, # 图例标题 fontsizemedium, ncol2) # 图例分2列显示 # 手动指定图例句柄和标签例如只想显示其中两条线 # ax.legend(handles[line1, scatter], labels[Theoretical, Experimental])添加文本与箭头注释# 在数据坐标系中某点添加文本 ax.text(5, 0.5, Critical Point, fontsize12, styleitalic, bbox{boxstyle: round, facecolor: wheat, alpha: 0.8}) # 添加带箭头的注释 ax.annotate(Local Maximum, xy(np.pi/2, 1), # 箭头指向的点 xytext(np.pi/2 2, 0.5), # 文本起始位置 arrowpropsdict(facecolorblack, shrink0.05, width1.5, headwidth8), fontsize10, hacenter)annotate功能非常强大arrowprops字典可以详细控制箭头的样式颜色、宽度、头部大小、箭头样式等。5.3 样式与色彩告别“科研风”默认样式Matplotlib的默认样式‘default’常被调侃为“科研风”虽然清晰但不够美观。我们有多种方法提升颜值。使用预定义样式Styleprint(plt.style.available) # 查看所有可用的样式 plt.style.use(seaborn-v0_8-darkgrid) # 应用一个样式如seaborn风格的深色网格 # 其他流行样式seaborn-whitegrid, ggplot, fivethirtyeight, grayscale # 样式会影响当前会话中所有后续创建的图形的颜色、网格、背景等。样式是全局设置一次use后续所有图都会应用该样式是快速改变图表整体观感的最简单方法。手动设置颜色、线型、标记的循环# 自定义属性循环让plot自动按你定义的顺序取用颜色/线型 from cycler import cycler custom_cycler (cycler(color[#1f77b4, #ff7f0e, #2ca02c, #d62728]) cycler(linestyle[-, --, -., :])) ax.set_prop_cycle(custom_cycler) # 然后多次plot会自动循环使用上面定义的4种颜色和4种线型组合 for i in range(4): ax.plot(x, y i*0.2)使用色彩映射Colormap色彩映射对于显示第三维数据如热度、高度至关重要。# 1. 在绘图时指定cmap参数如之前的scatter, contourf例子 # 2. 获取色彩映射对象并从中取色 cmap plt.cm.get_cmap(viridis) # 获取viridis色彩映射对象 color_for_value_0_5 cmap(0.5) # 获取映射到数值0.5处的颜色(RGBA值) ax.plot(x, y, colorcolor_for_value_0_5) # 3. 创建自定义的离散色彩映射 from matplotlib.colors import ListedColormap my_cmap ListedColormap([darkblue, blue, lightblue, white]) # 这个色彩映射只有4种颜色适用于分类数据。5.4 图形Figure级别的全局设置与保存图形属性设置fig plt.figure(figsize(10, 6), dpi150, facecolorwhitesmoke, edgecolorblack) # figsize: 图形尺寸宽高英寸。dpi: 每英寸点数影响输出分辨率。 # facecolor: 图形背景色。edgecolor: 图形边框颜色。 fig.suptitle(Overall Figure Title, fontsize16, y0.98) # 添加图形总标题y控制垂直位置保存图形到文件这是最终产出的一步参数设置直接影响图片质量。fig.savefig(my_plot.png, # 文件名支持.png, .jpg, .pdf, .svg等格式 dpi300, # 分辨率用于出版或打印建议300以上 bbox_inchestight, # **关键参数**紧凑型边界框自动裁剪图形周围的白边 pad_inches0.1, # 在bbox_inchestight时保留的额外边距英寸 facecolorwhite, # 保存时图形的背景色可与显示时不同 edgecolornone, transparentFalse) # 背景是否透明对于.png和.svg格式有用bbox_inchestight是解决保存图片时边缘有过多空白的万能钥匙务必养成习惯使用。美化核心技巧少即是多避免图表元素过度拥挤。如果图例项太多考虑分列或放在图形外部。字体一致性使用plt.rcParams全局设置字体确保所有文本标题、标签、刻度、图例风格一致。例如plt.rcParams[font.sans-serif] [SimHei] # 用来正常显示中文标签 plt.rcParams[axes.unicode_minus] False # 用来正常显示负号 plt.rcParams[font.size] 12颜色无障碍考虑色盲人群避免同时使用红绿色对比。可以使用viridis,plasma,cividis等感知均匀的色彩映射。保存为矢量图如果图表用于印刷或需要无限缩放优先保存为.pdf或.svg格式。它们是基于矢量的放大不会失真。6. 实战组合运用——创建一个完整的分析图表让我们把以上所有方法组合起来完成一个稍复杂的任务可视化一个简单数据集并包含多个子图、自定义样式和注释。假设我们有一组模拟的传感器时间序列数据包含温度、湿度和一个计算出的“舒适度指数”。我们将创建一张包含三个子图的仪表板式图表。import matplotlib.pyplot as plt import numpy as np import matplotlib.dates as mdates from datetime import datetime, timedelta # 1. 生成模拟数据 np.random.seed(2024) num_points 100 base_time datetime(2024, 5, 1, 8, 0, 0) time_stamps [base_time timedelta(minutes10*i) for i in range(num_points)] # 温度数据有日间波动和随机噪声 temperature 20 5 * np.sin(2 * np.pi * np.arange(num_points) / 50) np.random.randn(num_points) * 0.5 # 湿度数据 humidity 50 10 * np.cos(2 * np.pi * np.arange(num_points) / 70) np.random.randn(num_points) * 2 # 一个虚构的“舒适度指数”与温湿度负相关 comfort_index 100 - 0.8 * np.abs(temperature - 22) - 0.2 * np.abs(humidity - 55) comfort_index np.clip(comfort_index, 0, 100) # 限制在0-100之间 # 2. 创建图形和复杂的子图布局 (使用 subplot_mosaic) mosaic AA BC fig, axd plt.subplot_mosaic(mosaic, figsize(12, 10), constrained_layoutTrue, gridspec_kw{height_ratios: [2, 1]}) # 第一行高度是第二行的两倍 # 应用一个美观的样式 plt.style.use(seaborn-v0_8-talk) # 这个样式适合演示有较大的字体和清晰的线条 # 3. 绘制第一个子图A温度与湿度时间序列共享X轴 ax_a axd[A] # 绘制温度左轴 color_temp tab:red line_temp ax_a.plot(time_stamps, temperature, colorcolor_temp, linewidth2.5, markero, markersize4, labelTemperature (°C))[0] ax_a.set_ylabel(Temperature (°C), colorcolor_temp, fontsize12) ax_a.tick_params(axisy, labelcolorcolor_temp) ax_a.set_xlabel(Time) ax_a.xaxis.set_major_formatter(mdates.DateFormatter(%H:%M)) # 格式化时间显示为 时:分 fig.autofmt_xdate(rotation45, haright) # 自动旋转日期标签避免重叠 # 创建第二个Y轴右轴用于湿度 ax_a_hum ax_a.twinx() color_hum tab:blue line_hum ax_a_hum.plot(time_stamps, humidity, colorcolor_hum, linewidth2.5, linestyle--, labelHumidity (%))[0] ax_a_hum.set_ylabel(Relative Humidity (%), colorcolor_hum, fontsize12) ax_a_hum.tick_params(axisy, labelcolorcolor_hum) # 为第一个子图添加图例合并两个轴的句柄 lines_a [line_temp, line_hum] labels_a [l.get_label() for l in lines_a] ax_a.legend(lines_a, labels_a, locupper left, framealpha0.9) ax_a.set_title(Sensor Readings: Temperature Humidity over Time, fontsize14, pad15) ax_a.grid(True, alpha0.3) # 4. 绘制第二个子图B舒适度指数分布直方图 ax_b axd[B] # 计算一些统计量用于标注 mean_ci comfort_index.mean() std_ci comfort_index.std() n, bins, patches ax_b.hist(comfort_index, bins15, colorlightgreen, edgecolordarkgreen, alpha0.7, densityTrue) ax_b.axvline(mean_ci, colorred, linestyle-, linewidth2, labelfMean: {mean_ci:.1f}) ax_b.axvline(mean_ci std_ci, colororange, linestyle--, linewidth1.5, alpha0.7) ax_b.axvline(mean_ci - std_ci, colororange, linestyle--, linewidth1.5, alpha0.7, labelf±1 Std Dev) # 在直方图上添加文本标注 ax_b.text(0.05, 0.95, fMean: {mean_ci:.1f}\nStd: {std_ci:.1f}, transformax_b.transAxes, # 使用坐标轴相对坐标 verticalalignmenttop, bboxdict(boxstyleround, facecolorwheat, alpha0.8)) ax_b.set_xlabel(Comfort Index) ax_b.set_ylabel(Density) ax_b.set_title(Distribution of Comfort Index, fontsize13) ax_b.legend() ax_b.grid(True, alpha0.3) # 5. 绘制第三个子图C温湿度与舒适度的散点图颜色编码 ax_c axd[C] # 散点图点的大小固定颜色由舒适度指数决定 scatter ax_c.scatter(temperature, humidity, ccomfort_index, cmapRdYlGn, s50, alpha0.8, edgecolorsk, linewidth0.3) # 添加颜色条 cbar fig.colorbar(scatter, axax_c, orientationvertical, pad0.02) cbar.set_label(Comfort Index, rotation270, labelpad15) # 添加一条表示“理想温湿度”的十字线 ax_c.axhline(55, colorgray, linestyle:, alpha0.5, linewidth1) ax_c.axvline(22, colorgray, linestyle:, alpha0.5, linewidth1) ax_c.text(22.2, 56, Ideal Point (22°C, 55%), fontsize9, colorgray) ax_c.set_xlabel(Temperature (°C)) ax_c.set_ylabel(Humidity (%)) ax_c.set_title(Temperature vs Humidity (Colored by Comfort), fontsize13) ax_c.grid(True, alpha0.3) # 6. 为整个图形添加一个总标题 fig.suptitle(Environmental Sensor Data Analysis Dashboard, fontsize16, y1.02) # 7. 保存和显示 plt.savefig(sensor_analysis_dashboard.png, dpi300, bbox_inchestight) plt.show()这个实战案例融合了以下关键方法复杂布局使用subplot_mosaic创建了非对称的2x2网格第一行占两列。双Y轴在子图A中使用twinx()创建共享X轴的双Y轴分别绘制温度和湿度。时间序列处理使用matplotlib.dates格式化X轴的时间刻度标签。统计标注在直方图子图B中添加了均值线、标准差线以及文本框注释。颜色映射与颜色条在散点图子图C中用颜色编码第三维数据舒适度指数并添加了颜色条。全局样式与保存应用了seaborn样式提升美观度最后以高分辨率保存图形。通过这样一个完整的例子你可以看到各种绘图方法、控制方法和布局方法是如何有机组合在一起共同讲述一个数据故事的。掌握这些“方法”而非孤立的“函数”你就能在面对任何可视化需求时清晰地知道该如何拆解任务、选择工具并组合实现。
返回列表