尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
matplotlib底层逻辑:NumPy数组如何变图形,一文讲透
1. 先搞懂一件事matplotlib不是画图工具是数组的呈现器我第一次真正意识到matplotlib和NumPy是一对离不开的搭档是在某个数据展示项目返工的时候。当时我拿到一份现成的绘图代码前两行是导入库第三行突然冒出一个np.linspace(0, 10, 200)我当时心里还挺不屑——画个图而已为什么要先搞一个NumPy数组出来后来被一个前置任务按着头处理了一段时间数据我才彻底明白matplotlib的所有绘图操作本质上都是把已经存在的数组变成视觉符号它自己并不擅长生成数据。你喂给它的是一个x坐标数组、一个y坐标数组或者一个二维数值矩阵它做的事情只是把这些数值翻译成屏幕上的点、线、面和颜色块。至于这些数值怎么算出来、怎么清洗、怎么变换那是NumPy的地盘。这个认知转变特别关键。很多初学者学matplotlib觉得痛苦是因为他们总想背API——背plot有哪些参数、scatter有哪些参数却忽略了底层的数据形状决定图形形态这条主线。一旦你把matplotlib理解成面向NumPy数组的渲染器你会发现90%的绘图问题其实是在问自己三个问题我的数据是什么形状一维数组还是二维矩阵我的数据代表什么坐标系均匀采样还是不规则点集我的数据是连续数值还是离散分类该用颜色映射还是直接分块这篇文章就来拆一拆这条主线。我尽量少讲那些文档里一搜就能看到的参数清单多讲为什么这么做和实际画图时你会踩到什么坑适合已经会跑基础Demo、但总觉得自己在背API的读者也适合想把绘图逻辑彻底理清再动手的人。2. 从数组到图像一张图的底层是点集的可视化2.1 数学曲线和电脑曲线的区别你可能在数学课本上见过y x²这种光滑曲线课本上那条线是有无数个点的连续轨迹。可电脑屏幕是离散的像素网格你不可能让显卡去画无数个点只能画有限个点然后连起来。这就解释了为什么几乎所有matplotlib示例里都会先出现这样一段代码import numpy as np import matplotlib.pyplot as plt x np.linspace(-3, 3, 200) y x ** 2 plt.plot(x, y) plt.show()np.linspace(-3, 3, 200)干了什么它在-3到3之间均匀生成200个x坐标值。200个点已经足够让一条抛物线在屏幕上看起来是光滑的曲线但它的本质仍是折线——一段一段的短直线连接而成的折线。所以分辨率这个概念在绘图中非常重要。同样是画一条正弦曲线x_few np.linspace(0, 2 * np.pi, 5) x_many np.linspace(0, 2 * np.pi, 200) plt.plot(x_few, np.sin(x_few), o-, label5 points) plt.plot(x_many, np.sin(x_many), label200 points) plt.legend() plt.show()5个点画出来是歪歪扭扭的五边形200个点画出来才像正弦波。这就是为什么先用NumPy生成离散坐标再交给matplotlib绘制是所有绘图的起点——你不需要把数学公式告诉matplotlib只需要把公式在一组离散点上的取值告诉它。2.2 用np.linspace还是np.arange这是老生常谈但我想从实际绘图的稳定性角度多说两句。np.arange是按步长生成比如np.arange(0, 10, 0.1)看起来没问题但它存在浮点累积误差步长0.1在二进制里不是精确值累加多了末尾可能会冒出9.999999999这种数虽然不影响画线但一旦你要用这些坐标去做索引、分段、对齐误差就会传染。np.linspace是按总点数生成它内部处理了端点包含和均匀分布的问题。绘图场景里你通常更关心这段区间上取多少个点而不是每隔多少取一个所以我的习惯是画函数曲线、平滑过渡、归一化轴一律用linspace只有当我确实想按固定间隔生成网格比如直方图的边界数组时才用arange。还有一个细节linspace默认包含终点。如果你不想让首尾重合比如画极坐标的环形路径记得传endpointFalse。2.3 一次画多条曲线和NumPy的广播机制NumPy的广播broadcasting机制让一次算出一组曲线变得极其自然。你不必写循环去逐条计算y值x np.linspace(0, 2 * np.pi, 100) # 一次生成3条曲线的y值得到形状为 (3, 100) 的二维数组 y np.array([np.sin(x), np.sin(2 * x), np.sin(3 * x)]) for row in y: plt.plot(x, row) plt.show()这里matplotlib的plot可以接受一个二维数组作为y此时它会把每一行当成一条独立曲线自动匹配相同的x。这个用法在批量画不同参数下的结果时特别省事而且比逐个调用plot性能更好。3. 两套接口的真相plt.与ax.的取舍不是风格问题3.1 pyplot状态机的隐式便利几乎所有人入门时用的都是plt.plot()、plt.xlabel()这种写法。pyplot底层维护了一个当前坐标轴的状态你每调一次plt.plot它就把图画到当前的坐标轴上。好处是代码短交互式环境里往前推进特别顺手坏处是当一张图里有多个子图、多个坐标轴时状态非常容易串。比如你画了两个子图想回头给第一个子图加标题用plt.title()会把标题加到当前处于激活状态的子图上而不是你脑子里的第一个子图。这个坑我见过太多次了不知道有多少人深夜调试发现标题总跑错位置。3.2 面向对象接口的显式控制另一套是显式创建Figure和Axes对象fig, ax plt.subplots(1, 2, figsize(10, 4)) ax[0].plot(x, np.sin(x)) ax[0].set_title(sin(x)) ax[1].plot(x, np.cos(x)) ax[1].set_title(cos(x))每条指令都明确指定了作用于哪个坐标轴可读性和可维护性都强很多。尤其在函数封装里你完全可以把ax作为参数传进去让一个函数负责画某一类图再也不用担心污染其他子图。3.3 我的选择建议不搞花活直接给结论在Jupyter里做快速探索用plt.完全可以效率优先。任何超过10行的绘图代码、任何要复用或封装成函数的绘图逻辑、任何涉及多子图或共享坐标轴的场景一律用fig, ax plt.subplots()开场。我甚至建议所有读者从第二个Demo开始就直接养成fig, ax plt.subplots()的习惯。虽然多打几个字母但它让你从一开始就把这张图画在哪个画布、哪个子图上这件事拿捏在手。等你想画复杂图的时候根本不需要再改一版代码——这个账怎么算都划算。4. 形状即信息读懂一维、二维数组和图像语义4.1plot配对一维数组pcolormesh面向二维矩阵matplotlib的每种绘图函数对数据的形状要求是不同的。这是初学者最容易搞混的地方。plt.plot(x, y)x和y都是一维数组长度必须一致。它适合画折线、散点趋势。plt.scatter(x, y)同样一维但每个点独立绘制适合表达离散观测。plt.pcolormesh(X, Y, C)/plt.imshow(C)接受二维数组用颜色表达数值高低适合画热力图、温度场、地形。plt.contour/plt.contourf同样需要X、Y、Z三个二维数组画等值线或填充等值面。所以当你拿到一批数据先别急着选函数先问自己这数据是一组点还是一张网格上的值点用plot/scatter场用pcolormesh/contour。4.2 网格数据的核心np.meshgrid到底在干什么很多人在等值线、三维曲面这里卡住因为不理解为什么需要np.meshgrid。我尽量用一句话说透你需要把两个一维坐标扩展成二维网格上的两个坐标矩阵让网格上每个点都有一对(x, y)坐标。看这个例子。假如我们想画出函数z sin(sqrt(x²y²))在方形区域内的分布x np.linspace(-5, 5, 100) y np.linspace(-5, 5, 100) X, Y np.meshgrid(x, y) Z np.sin(np.sqrt(X**2 Y**2)) plt.contourf(X, Y, Z, levels20, cmapviridis) plt.colorbar() plt.show()X是一个100行100列的数组每一行都相同存的是横坐标Y同样100行100列每一列都相同存的是纵坐标。于是X[i, j]和Y[i, j]正好组成平面上第(i, j)个网格点的坐标。Z是由这两组广播坐标计算出来的函数值矩阵。如果你不用meshgrid只用一维x和y去计算Z结果是一个长度为100的数组根本撑不起二维图像所需的矩阵形态。记住一个口诀画平面场图先把一维坐标变二维网格再算每个网格点的数值。4.3imshow的隐式坐标和显式坐标imshow比较特殊它默认认为数组的行列索引就是坐标所以不需要传X和Y。但对科研绘图来说这不够直观——你通常想知道第0行对应哪个温度、第0列对应哪个时间。解决办法有data np.random.rand(10, 15) plt.imshow(data, aspectauto, extent[0, 15, 10, 0], cmapplasma) plt.colorbar() plt.show()extent[left, right, top, bottom]就把数组的边界映射到真实坐标上了。注意顺序后两个是上和下因为图像的行是从上往下编的如果你用extent[0, 15, 0, 10]图像会被上下翻转。这个坑我在实际项目里踩过一次排查了半天才发现是extent顺序的问题。5. 连续数值变成颜色cmap与norm的内在逻辑5.1 颜色映射表是怎么取色的颜色映射colormap本质上是一个函数输入一个标量数值输出一个RGBA颜色元组。matplotlib内置了几十个cmap比如viridis、plasma、inferno它们把数值范围线性映射到0到1之间再去查色表。所以当你说这个图有点看不清第一直觉不该是换颜色而该是检查数据范围是否挤压在色表的一小段上举个例子如果数据大部分集中在0到1但有个异常点是100线性映射之后正常区域全部变成接近色表底部的颜色画出来的图当然糊成一片。处理方法有两种一是把异常点剔除或截断二是用非线性归一化。matplotlib提供了matplotlib.colors.LogNorm、SymLogNorm等专门处理动态范围大的数据。import matplotlib.colors as mcolors plt.imshow(data, cmapviridis, normmcolors.LogNorm(vmin1, vmax1000)) plt.colorbar()5.2 归一化对象不只是vmin/vmaxnorm参数远不止传两个边界值那么简单。matplotlib.colors.Normalize(vmin, vmax)是最常用的线性归一对BoundaryNorm则可以按你指定的分段边界把数值映射到离散颜色这在画分类地图、分箱统计图时特别好用。比如我把温度分成低温、中温、高温三档想用蓝、黄、红三色表示import matplotlib.colors as mcolors bounds [-10, 5, 20, 40] cmap mcolors.ListedColormap([blue, yellow, red]) norm mcolors.BoundaryNorm(bounds, cmap.N) plt.imshow(temp_data, cmapcmap, normnorm) plt.colorbar(ticks[-10, 5, 20, 40])注意BoundaryNorm需要知道你要几种颜色所以传了cmap.N。如果你直接用vmin/vmax配ListedColormap颜色用起来多半不按你预想的分段走这个我建议实操一次体会一下。5.3 离散分类怎么用cmap把标签编码成数值散点图里如果有一列类别变量比如品种、产地很多人会写一长串条件判断来手动染色。其实可以更优雅先用pd.factorize或者np.unique把类别转成整数编码然后直接把编码数组传给c参数配合cmap使用。categories np.array([A, B, C, B, A]) codes, uniques pd.factorize(categories) plt.scatter(x, y, ccodes, cmapSet1) cbar plt.colorbar(ticksrange(len(uniques))) cbar.ax.set_yticklabels(uniques)这里colorbar的刻度标签不能再显示数值而应该显示原始类别名。这一步看似小但在做报告时非常加分——审图的人一眼就能看出颜色对应什么类别而不是对着图例猜。我在实际项目中就遇到过有人用plt.scatter直接传字符串数组给c结果报错。原因很简单matplotlib的颜色参数在底层期望的是数值字符串必须经过编码或直接传color。这个先编码再映射的思路是处理任何非数值标签进颜色通道的标准解法。6. 别信默认刻度坐标轴、ticker与精修6.1 默认刻度的盲区matplotlib确实很智能会自动帮你选刻度位置和刻度标签格式。但智能不等于合适。默认刻度经常出现的问题是横轴日期显示成一长串数字完全没法读。纵轴范围过大时刻度挤在一起读不出有效数字。你想强调某些关键点默认刻度却没有覆盖到。处理这些问题的正确姿势是显式控制刻度。核心工具是matplotlib.ticker模块和坐标轴对象ax.xaxis/ax.yaxis。6.2 ticker的几种常用策略MultipleLocator可以按固定间隔放置刻度这是最直觉的用法from matplotlib.ticker import MultipleLocator, FormatStrFormatter fig, ax plt.subplots() x np.linspace(0, 10, 100) ax.plot(x, np.cos(x)) ax.xaxis.set_major_locator(MultipleLocator(2)) # 每2个单位放一个刻度 ax.yaxis.set_major_locator(MultipleLocator(0.5)) # 每0.5个单位放一个刻度 ax.yaxis.set_major_formatter(FormatStrFormatter(%.1f)) # 强制保留1位小数MaxNLocator则更聪明——你只告诉它最多放几个刻度它自动在合适的间隔里挑选适合数据范围千奇百怪、你又懒得手动调节的场景。6.3 格式化字符串和自定义函数FuncFormatter允许你写一个函数来生成刻度标签这在处理百分比、单位换算时很常用。比如把弧度值换成角度显示from matplotlib.ticker import FuncFormatter def rad_to_deg(x, pos): return f{x * 180 / np.pi:.0f}° ax.xaxis.set_major_formatter(FuncFormatter(rad_to_deg))记住FuncFormatter的第一个参数是刻度值第二个参数是位置通常可以忽略。这种方法比直接改数据灵活得多——你不需要真的把x数据换成角度图上的原始几何关系还是弧度只是标签显示成角度避免你为了好看而改变数据结构。7. 第三维和会动的图数组思维仍然适用7.1 3D曲面就是二维网格再加一个Z用mplot3d画三维曲面时你会发现依然需要np.meshgrid。道理和等值线一样X、Y必须是网格矩阵Z是在每个网格点上计算的高度然后plot_surface把这些点连成小平面。from mpl_toolkits.mplot3d import Axes3D fig plt.figure() ax fig.add_subplot(111, projection3d) x np.linspace(-2, 2, 60) y np.linspace(-2, 2, 60) X, Y np.meshgrid(x, y) Z np.exp(-X**2 - Y**2) ax.plot_surface(X, Y, Z, cmapcoolwarm, edgecolornone) plt.show()有人问为什么不能只用一维x、一维y和Z的三元组画曲面能但你得把每个三维点都单独列出来数据量会从60*603600个网格点变成凌乱的点云列表。matplotlib的plot_surface需要的是规则网格因为只有网格才能推断出相邻点之间怎么连面——这又回到了数组形状决定图形语义这条主线。7.2 动画的本质逐帧替换数组matplotlib.animation.FuncAnimation会让很多人觉得高深但拆开看它的核心就一句话每一帧更新某个绘图对象的data。from matplotlib.animation import FuncAnimation fig, ax plt.subplots() x np.linspace(0, 2 * np.pi, 100) line, ax.plot(x, np.sin(x)) def update(frame): line.set_ydata(np.sin(x frame / 10)) return line, ani FuncAnimation(fig, update, frames100, interval30, blitTrue) plt.show()frames100本质就是0到99的整数数组。每一帧我们基于同一个x数组重新计算一组新的y数组然后调用set_ydata替换掉旧数据。动画不过是快速连续地换数组。要说性能第一原则是不要在动画循环里创建新的绘图对象只更新已有对象的数据。line.set_ydata比ax.plot(...)快一个量级因为前者只修改底层数组后者要重新走一遍图元创建流程。第二原则是开blitTrue它只重绘发生变化的部分省下的开销在小规模图上可能感觉不到但点数过万时差距非常明显。8. 实战管线一个传感器温度场项目怎么从数据到成图前面几节都在讲概念这一节我把它们串成一个真实项目流程。假设我们有这样一个模拟任务某冷库内有4条温度传感器链每条链上10个测点每10分钟记录一次温度需要画一张货架区域温度分布图。8.1 第一步用NumPy模拟并清洗数据没有真实数据器时可以先构造模拟数组来跑通流程。rng np.random.default_rng(42) time_steps 24 * 6 # 24小时每10分钟一个点 # 传感器布局4行 × 10列 sensor_x np.tile(np.linspace(0, 9, 10), 4) sensor_y np.repeat(np.linspace(0, 3, 4), 10) # 模拟温度基础温度 空间波动 时间噪声 base_temp 4.0 spatial_pattern 0.5 * np.sin(sensor_x / 2) 0.3 * np.cos(sensor_y * 2) noise rng.normal(0, 0.1, size40) temp_now base_temp spatial_pattern noise这里np.tile和np.repeat的组合非常典型tile负责把x坐标横向复制repeat负责把y坐标纵向复制拼出一张网格点列表。数据清洗阶段我们要把超过合理范围的读数标记出来。这个环节最容易踩坑的是用np.where做条件筛选时得到的索引数组和原始经纬度坐标对不上导致画图时点漂移。我的建议是永远保留一个掩码数组而不是直接删掉异常值valid (temp_now -5) (temp_now 15) draw_x, draw_y sensor_x[valid], sensor_y[valid] draw_temp temp_now[valid]这样画散点或插值时坐标与数值始终是一一对应的不会错位。8.2 第二步选择正确的图形语义这个项目里每种把数组变成图的方案各有适用场景图形类型适用数据优缺点scatter散点稀疏传感器点信息保真能看到原始测点位置不适合看起来连续的汇报pcolormesh网格色块规则网格场天然适合均匀网格填充感强但需要插值contourf等值线填充任意网格场视觉平滑适合展示温度梯度边界处理要小心tricontourf三角等值线不规则散点专治没有规则网格的散点数据最省事如果你就画40个测点的温度散点图足够但领导通常想看哪里最冷、哪里温度波动大这就得插值成连续场。matplotlib里最省事的路径是tricontourf它直接用散点做Delaunay三角剖分不需要你手动插值fig, ax plt.subplots(figsize(8, 4)) cf ax.tricontourf(sensor_x, sensor_y, temp_now, levels20, cmapcoolwarm) cbar plt.colorbar(cf, axax, labelTemperature (°C)) ax.set_aspect(equal) plt.show()注意三点tricontourf要求x、y、temp三个一维数组长度相同这正是我们之前用valid保住的对应关系levels20控制分层数量太密会噪太疏会失掉细节可以根据实际数值范围增减set_aspect(equal)让坐标轴比例一致否则x轴范围远大于y轴时图会被拉得很扁。8.3 第三步坐标、刻度、单位和美观的一次到位这一步常常决定项目图和随手涂鸦的差距。我画这类图时的固定动作是ax.set_xlabel(Shelf Position (m)) ax.set_ylabel(Layer) ax.set_title(Cold Storage Temperature Distribution) ax.xaxis.set_major_locator(MultipleLocator(2)) ax.yaxis.set_major_locator(MultipleLocator(1))如果传感器坐标不是从0开始的我更愿意用ax.set_xlim手动收一下边界避免图边上一圈空白。另外数值单位写进坐标轴标签比放在colorbar标签那里更醒目。8.4 第四步多子图对比的封装套路项目到了一定阶段你要在同一张图里对比多个时刻的分布。我的做法是写一个小函数把画一张分布图的逻辑收进去def draw_temp_map(ax, x, y, temp, title): cf ax.tricontourf(x, y, temp, levels20, cmapcoolwarm) ax.set_title(title) ax.set_aspect(equal) return cf fig, axes plt.subplots(1, 3, figsize(15, 4)) for i, ax in enumerate(axes): cf draw_temp_map(ax, sensor_x, sensor_y, temp_history[i], fT{i*2}h) fig.colorbar(cf, axaxes, labelTemperature (°C)) plt.tight_layout() plt.show()注意最后调用fig.colorbar时传入的是axes列表这样colorbar会自动横跨所有子图高度对齐看起来特别专业。如果分别给每张子图加colorbar不仅挤占空间三张图的颜色范围还不一致读者很容易误读对比。9. 性能、大数组和常见报错我代你踩过的坑9.1 数据量一大就卡先减密度而不是换工具画几万点可能还没感觉一旦到了几十万、上百万点matplotlib的渲染瓶颈就很明显了。很多人第一反应是换Plotly、换Bokeh但其实很多场景下你只需要降采样。降采样不是简单切片而是要保留视觉特征。对波动剧烈的时间序列直接每N个点取一个会造成峰值丢失看起来像被削平了。稳妥的做法是用np.interp做重采样或者对数据做分箱聚合。t np.linspace(0, 100, 2_000_000) y np.sin(t) 0.1 * np.sin(50 * t) # 降到2000个点 t_down np.linspace(t[0], t[-1], 2000) y_down np.interp(t_down, t, y) plt.plot(t_down, y_down)np.interp本质是在原始曲线上重新采样它比隔几个取一个更能保留曲线形状。实测下来200万点降到2000点图像看起来几乎没差别但渲染时间从几秒降到毫秒级。如果是二维热力图太大优先用pcolormesh而不是imshow并且适当降低网格分辨率。pcolormesh对坐标数组的容忍度也更高不均一网格也能处理这是imshow比不了的。9.2 高频报错的排查思路我把自己见过最多的几个matplotlib错误整理成了一个排查清单按出错顺序来报错信息原因处理x and y must have same first dimension传给plot的两个数组长度不一致打印len(x)和len(y)看是谁多谁少ValueError: shape mismatch传到pcolormesh/contour的X、Y、Z形状对不上确认是否坐标用了1D、数值却用了2D或者忘了meshgridTypeError: Invalid shape ... for image dataimshow收到3D数组例如RGB叠加了通道检查是否把多张图叠在一起用data[0]取单张No handles with labels foundlegend找不到带label的图元每个plot至少要有一个传了label的曲线AssertionError: Image size ... does not matchcolorbar和axes尺寸不匹配换用fig.colorbar(cf, axaxes)而不是plt.colorbar()裸调RuntimeWarning: More than 20 figures have been opened循环里反复plt.figure()没关闭避免在循环里新建Figure尽量复用Axes或循环末尾plt.close(fig)最后这条我特别想多说一句。很多人画批量图时习惯for i in range(100): plt.figure() plt.plot(...) plt.savefig(fout_{i}.png)这样跑完一次内存里挂着100个Figure对象轻则警告重则内存爆炸。正确写法是for i in range(100): fig, ax plt.subplots() ax.plot(...) fig.savefig(fout_{i}.png) plt.close(fig)plt.close(fig)会释放这个Figure占用的内存。这是一个不起眼但非常关键的习惯批量出图场景下尤其重要。10. 几段如果早点知道就好了的代码片段10.1 一张图里共享坐标轴借助sharex和sharey参数可以让多个子图在缩放时保持联动。这在对比同一时间轴下不同变量的趋势时特别有用避免了手动对齐坐标范围的痛苦。fig, axes plt.subplots(2, 1, sharexTrue, figsize(8, 6)) axes[0].plot(t, temp) axes[1].plot(t, humidity)10.2 用np.where高亮特定区间在某个项目里我需要把温度超过阈值的时段在图上标出来。与其写一堆循环判断不如用布尔数组直接做掩码x np.linspace(0, 24, 1000) y 10 5 * np.sin(x / 3) over y 12 plt.plot(x, y) plt.fill_between(x, 10, y, whereover, colorred, alpha0.3)fill_between配合where参数一行代码画出超限区域。注意我传的y是填充上边界10是下边界这样阴影刚好从基线延伸到超限曲线视觉上非常清晰。10.3 坐标轴比例锁定时防止图形变形金融时序图和遥感影像图里x和y的物理单位不一致是很常见的事这时千万别用set_aspect(equal)它会把扭曲的映射变成拉伸的图。你应该根据业务含义设置合适的比例比如1个单位x对应2个单位yax.set_aspect(2.0) # 表示y轴单位长度是x轴的2倍10.4 用axvspan标注事件区间画时间线、事件标记时用垂直线axvline标注瞬间用axvspan标注区间。这两个函数都是数据坐标里标注不会因为坐标轴范围变化而偏移ax.axvspan(8, 10, colorgreen, alpha0.2, labelMaintenance Window) ax.axvline(12, colorred, linestyle--, labelPeak Time)11. 回到标题NumPy和matplotlib到底什么关系写到这里我想把最核心的一句话再强调一次matplotlib就是个看图工具它的所有输入本质上是NumPy数组所有输出本质上是数组的可视化映射。你不需要把函数交给matplotlib你需要把函数在离散采样点上的值交给它。你不需要告诉它这是正弦波你需要给它一组按规律变化的y数组。它负责把数组变成线、面、颜色NumPy负责把现实问题变成数组。这种思维转变比记住任何单个API参数都重要。一旦你开始习惯先构造数组、再选择图形语义、再精修坐标和样式这个流程你会发现matplotlib的地图突然变得清晰画线图准备好长度相同的一维x和y。画场图准备好三个二维网格矩阵或者用tricontourf偷懒。画分类散点把类别编码成数值再映射到cmap。画动画写好一个每帧返回新数组的更新函数。这四件事可以覆盖掉90%以上的日常绘图需求。剩下10%基本都是在跟样式、刻度、标签较劲那属于审美和经验范畴多画几次自然就熟了。最后分享一个我个人的小习惯每次新拿到一批数据我会先写一行代码查看数组的shape然后写一行print(np.nanmin(data), np.nanmax(data))看数值范围再决定该用线性色表还是对数色表、该设置多少levels。这个先看形状、再看范围、然后选择图形语义的三步检查法帮我省掉了大量反复改代码的时间。希望对你也有用。
RELATED

相关推荐

Three.js WebGL粮仓3D可视化:物联网温湿度空间定位实战

Three.js WebGL粮仓3D可视化:物联网温湿度空间定位实战

简介:本资源是一个基于Vue.js与Three.js开发的物联网粮仓3D可视化管理系统,面向前端开发者、WebGL初学者及智慧农业/仓储数字化从业者,解决传统粮仓监控信息抽象、空间感知弱、数据交互不直观等管理痛点。项目完整实现3D场景渲染、实时环境数…

📅 2026/10/14 23:01:56
诺瓦星云科技与老虎集团Java笔试对比:底层原理与业务场景的备考策略

诺瓦星云科技与老虎集团Java笔试对比:底层原理与业务场景的备考策略

1. 拆解这场笔试:两家公司到底在考什么先说说我为什么想聊这个话题。前段时间帮几个学弟学妹复盘秋招,发现一个很普遍的现象:很多人刷了几百道LeetCode,结果一到真实笔试就懵了。尤其是像诺瓦星云科技和老虎集团这种,笔…

📅 2026/10/14 23:01:56
Wireshark入门实验:从HTTP抓包到TCP三次握手全解析

Wireshark入门实验:从HTTP抓包到TCP三次握手全解析

如果你正准备开始学习网络协议分析,第一次接触到 Wireshark 的 Introduction 实验,这篇文章就是为你准备的。Wireshark 是目前最主流的图形化抓包工具,它能把网卡上经过的每一个数据包“拍下来”并解析成人类能看懂的协议字段。这个入门实验的…

📅 2026/10/14 23:01:56
MORE NEWS

更多资讯

📰

手动部署OpenTelemetry Collector:从配置到运维的实践指南

简介:基于C#编写的winform Socket通信示例,面向需要掌握网络编程的C#初学者,演示了服务端与多个客户端同时连接与数据交互的实现过程。压缩包内包含服务端与客户端两个独立工程,可对照学习服务端监听、客户端连接以及多客户端并发…

📰

SpringBoot整合knife4j实战:接口文档与在线调试完整指南

1. 为什么接口文档这件事值得单独拎出来聊做后端开发的人都有一个共识:写接口不难,难的是让前端、测试、甚至未来的自己看懂接口。早些年大家用 Swagger 那一套,ApiOperation、ApiModel注解满天飞,UI 长得像上世纪的产物&#xff…

📰

二次规划QP实战:从调包到手撕,解决不可行与病态问题

1. 从“调包”到“手撕”:为什么QP值得你花时间很多做算法落地的朋友,第一次接触二次规划(Quadratic Programming,简称QP)都是在MATLAB的quadprog或者Python的cvxopt.solvers.qp里填几个矩阵就完事了。模型跑通那一刻觉…

📰

差分进化算法MATLAB源码详解:用Sphere函数跑通单目标优化

很多刚开始接触智能优化算法的朋友,第一段需要亲手写的 MATLAB 程序,往往就是“差分进化算法matlab源码,跑单目标 sphere 函数”。这个组合看起来简单,其实非常值得认真对待:差分进化(DE)是连续…

📰

Dubbo整合Spring服务注册与暴露机制深度解析

1. 从一次线上服务找不到Provider说起很多同学第一次接触分布式服务框架时,都会遇到一个很迷惑的现象:本地写好了服务实现类,Spring 容器也启动了,日志里没报任何错,但消费者端就是调用不到,报No provider …

📰

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬