资讯详情

Bokeh 等高线绘图全解析:contour_data 与 from_contour 的原理与实战

📅 2026/9/13 16:19:49 | 华诺云谱 👁 阅读
Bokeh 等高线绘图全解析:contour_data 与 from_contour 的原理与实战
Bokeh 等高线绘图全解析contour_data 与 from_contour 的原理与实战【免费下载链接】bokehInteractive Data Visualization in the browser, from Python项目地址: https://gitcode.com/GitHub_Trending/bo/bokeh本文以 Bokeh 官方 API 参考文档 docs/bokeh/source/docs/reference/plotting/contour.rst 为核心骨架深入剖析bokeh.plotting.contour模块中contour_data与from_contour两个核心函数。Bokeh 的等高线Contour功能用于在二维四边形网格上计算并渲染等值线以及等值线之间的填充区域一次函数调用即可同时绘制两者。读完本文你将掌握figure.contour高层接口、底层数据对象ContourData的结构、ContourRenderer.set_data的更新机制、调色板插值规则、极坐标网格与动画等高线的完整实战方案。功能总览从等高线数据到渲染器Bokeh 的等高线功能自 3.0 版本引入整个调用链分为三层数据计算层contour_data()使用 ContourPy 库计算等高线的几何坐标返回一个不可变的ContourData数据对象渲染器构建层from_contour()在数据基础上创建ContourRenderer把填充多边形与等值线分别挂载到MultiPolygons与MultiLine两个 glyph 上高层便捷入口figure.contour()内部直接调用from_contour并将返回的渲染器自动追加到figure.renderers中见 src/bokeh/plotting/_figure.py。官方推荐用户直接使用figure.contourcontour_data与from_contour则服务于更底层的自定义场景——例如在 Bokeh Server 中实现动画等高线或在既有ContourRenderer上反复更新数据。模块的公开 API 只有这两个函数定义于 src/bokeh/plotting/contour.py__all__ ( contour_data, from_contour, )核心数据结构六个 dataclass 各司其职contour_data的输出链条由一组 frozen dataclass 组成见 src/bokeh/plotting/contour.py数据结构字段职责FillCoordsxs、yslist[list[list[np.ndarray]]]整个 level 序列上所有填充多边形的坐标LineCoordsxs、yslist[np.ndarray]整个 level 序列上所有等值线的坐标ContourCoordsfill_coords: FillCoords \| None、line_coords: LineCoords \| None填充与线两种坐标的组合FillData(FillCoords)继承坐标字段外加lower_levels、upper_levels填充多边形完整几何数据含每个多边形对应的上下界 levelLineData(LineCoords)继承坐标字段外加levels等值线完整几何数据含每条线对应的 levelContourDatafill_data: FillData \| None、line_data: LineData \| None最高层组合对象可直接传给ContourRenderer.set_data值得注意的一个实现细节FillData.asdict()刻意使用浅拷贝dict(entries(self))而不是标准库dataclasses.asdict的深拷贝因为坐标数组体积大且后续无需修改浅拷贝可避免不必要的内存复制开销。contour_data只算数据不建渲染器函数签名见 src/bokeh/plotting/contour.pydef contour_data( x: ArrayLike | None None, y: ArrayLike | None None, z: ArrayLike | np.ma.MaskedArray | None None, levels: ArrayLike | None None, *, want_fill: bool True, want_line: bool True, ) - ContourData:参数语义z必填形状为(ny, nx)的二维网格化数值数组是需要计算等高线的目标数据。可以是 NumPy 掩码数组masked array其中被掩码的网格点会被排除np.inf或np.nan等无效值同样会被自动掩掉。x、y可选z值对应的坐标。可以是与z.shape相同的二维数组也可以是一维数组长度分别为nx z.shape[1]与ny z.shape[0]。若不指定默认按np.arange(nx)、np.arange(ny)生成间距为 1 的笛卡尔网格。无论哪种形式都要求严格单调有序。levels必填计算等高线的 z 层级序列必须递增。等值线在每个 level 上各计算一组填充多边形则在每对相邻 level 之间计算一组因此等值线共有len(levels)组填充多边形共有len(levels)-1组。want_fill/want_line关键字专属参数控制是否计算填充与线。_validate_levels会在len(levels) 2时自动把want_fill置为False因为至少需要两个 level 才能夹出一个填充区间若两者同时为Falsecontour_data直接抛出ValueError(Neither fill nor line requested in contour_data)。内部执行流程contour_data内部调用私有函数_contour_coords见 src/bokeh/plotting/contour.py其核心是from contourpy import FillType, LineType, contour_generator cont_gen contour_generator(x, y, z, line_typeLineType.ChunkCombinedNan, fill_typeFillType.OuterOffset)填充计算对每一对相邻 level 调用cont_gen.filled(levels[i], levels[i1])采用FillType.OuterOffset格式——每个多边形的第一段轮廓是外边界后续各段是孔洞线计算对每个 level 调用cont_gen.lines(level)采用LineType.ChunkCombinedNan格式——所有等值线的 x/y 坐标各自合并进单个 NumPy 数组不同线段之间用np.nan分隔。计算完成后contour_data分别包装出FillData携带lower_levelslevels[:-1]、upper_levelslevels[1:]与LineData携带levelslevels组合成ContourData返回。from_contour一键构建 ContourRendererfrom_contour是模块的另一半核心见 src/bokeh/plotting/contour.pydef from_contour( x: ArrayLike | None None, y: ArrayLike | None None, z: ArrayLike | np.ma.MaskedArray | None None, levels: ArrayLike | None None, **visuals, # LineProps、FillProps、HatchProps 的并集 ) - ContourRenderer:它的x、y、z、levels语义与contour_data完全一致差别在于多出的**visuals视觉参数以及返回值类型。判定的核心规则在 docstring 中写得很明确设置了fill_color就计算填充多边形设置了line_color就计算等值线。视觉属性的拆分与校验from_contour把传入的**visuals按属性族拆分line_color存在 → 走want_lineTrue分支将LineProps族属性line_color、line_width、line_dash等剥离到line_visuals供MultiLineglyph 使用fill_color存在 → 走want_fillTrue分支FillProps与HatchProps族属性供MultiPolygonsglyph 使用两者都不存在时剩余未知关键字会被校验set(visuals) - set(FillProps.properties()) - set(HatchProps.properties())非空则抛出ValueError(Unknown keyword arguments in from_contour: ...)。填充多边形在视觉上有个刻意的设定构建完成后会把填充 glyph 的line_alpha设为 0、line_width设为 0见 src/bokeh/plotting/contour.py即默认不显示填充区域周围的描边线这与用户指南中填充多边形以MultiPolygonsglyph 实现且line_width置零的描述相互印证。颜色的灵活插值机制fill_color与line_color是所有视觉属性中最灵活的_color函数见 src/bokeh/plotting/contour.py按以下优先级处理调色板集合palette collection若传入的是字典如bokeh.palettes.Cividis键为颜色数量、值为颜色序列则调用_palette_from_collection按需取用长度恰好匹配则直接用所需长度超过集合中最长调色板则对最长调色板做线性插值interp_palette扩容所需长度小于最短调色板则插值缩容。空集合直接抛ValueError(PaletteCollection is empty)。长度不匹配的颜色序列传入的序列非 bytes/str长度与所需数量不符时用interp_palette重新采样到目标长度。长度 256 的调色板如Cividis256在这里特别有用——无论 level 数量多少都能平滑重采样。长度匹配的序列或标量原样使用。对应的目标长度规则是线相关颜色需要len(levels)个填充/填充图案相关颜色需要len(levels)-1个。其他向量化属性如line_width、line_dash、hatch_pattern则不做插值必须由用户提供精确长度的序列否则_process_sequence_literals在将序列字面量写入ColumnDataSource时会校验失败。返回值ContourRendererfrom_contour最终构造一个ContourRenderer见 src/bokeh/plotting/contour.pycontour_renderer ContourRenderer( fill_rendererGlyphRenderer(glyphMultiPolygons(), data_sourceColumnDataSource()), line_rendererGlyphRenderer(glyphMultiLine(), data_sourceColumnDataSource()), levelslist(levels)) contour_renderer.set_data(new_contour_data)随后把拆分好的视觉属性逐一setattr到对应 glyph把向量属性数据灌入对应的ColumnDataSource。注释中还预留了扩展空间Will be other possibilities here like logarithmic...未来可能有对数等其他 level 类型。figure.contour推荐的高层入口普通用户应优先使用figure.contour见 src/bokeh/plotting/_figure.py它本质上是from_contour的一层薄封装def contour(self, xNone, yNone, zNone, levelsNone, **visuals) - ContourRenderer: contour_renderer from_contour(x, y, z, levels, **visuals) self.renderers.append(contour_renderer) return contour_renderer它把渲染器自动挂到 figure 上其余全部行为含所有参数语义与颜色插值规则均与from_contour一致。唯一的强制参数组合是z、levels以及fill_color/line_color至少其一x、y均可省略。ContourRendererset_data 与 construct_color_barContourRenderer定义于 src/bokeh/models/renderers/contour_renderer.py继承自DataRenderer暴露三个核心属性line_renderer: Instance(GlyphRenderer)—— 等值线 glyph 渲染器fill_renderer: Instance(GlyphRenderer)—— 填充多边形 glyph 渲染器levels: Seq(Float)—— 计算等高线所用的 level 序列。set_data动画等高线的关键set_data(data: ContourData)接受contour_data的返回值把坐标与 level 边界数据写入两个内部数据源。它还有一层保真逻辑当新数据中缺少填充或线时会保留旧数据源中已有的视觉属性列xs、ys、lower_levels、upper_levels、levels之外的键并复制到新数据源避免每次更新都丢失用户设置的视觉样式若新数据完全没有填充部分则把填充数据源重置为空数组。construct_color_bar一行生成颜色条def construct_color_bar(self, **kwargs) - ContourColorBar: from ..annotations import ContourColorBar from ..tickers import FixedTicker return ContourColorBar( fill_rendererself.fill_renderer, line_rendererself.line_renderer, levelsself.levels, tickerFixedTicker(ticksself.levels), **kwargs, )颜色条会自动复用等高线渲染器的填充、填充图案与线视觉属性刻度固定为levels本身。**kwargs会透传给ContourColorBar构造函数可设置title、width等BaseColorBar属性。典型用法是配合p.add_layout(colorbar, right)将颜色条挂到图右侧。实战一最简等高线线 填充 颜色条来自官方示例 examples/topics/contour/contour_simple.pyimport numpy as np from bokeh.palettes import Sunset8 from bokeh.plotting import figure, show # 数据为两个高斯函数的叠加 x, y np.meshgrid(np.linspace(0, 3, 40), np.linspace(0, 2, 30)) z 1.3*np.exp(-2.5*((x-1.3)**2 (y-0.8)**2)) - 1.2*np.exp(-2*((x-1.8)**2 (y-1.3)**2)) p figure(width550, height300, x_range(0, 3), y_range(0, 2)) levels np.linspace(-1, 1, 9) contour_renderer p.contour(x, y, z, levels, fill_colorSunset8, line_colorblack) colorbar contour_renderer.construct_color_bar() p.add_layout(colorbar, right) show(p)要点拆解fill_colorSunset8是长度 8 的调色板而len(levels)-1 8长度恰好匹配直接按位对应每个填充区间line_colorblack是标量所有 9 条等值线统一渲染为黑色实线颜色条用construct_color_bar()一行生成自动继承填充配色与 level 刻度。实战二极坐标网格与向量化视觉属性官方示例 examples/topics/contour/contour_polar.py 展示了等高线的进阶能力——网格可以自行环绕极坐标且 line/fill/hatch 三类属性几乎全部支持向量化import numpy as np from bokeh.palettes import Cividis from bokeh.plotting import figure, show # 数据是极坐标网格上的二维正弦波 radius, angle np.meshgrid(np.linspace(0, 1, 20), np.linspace(0, 2*np.pi, 120)) x radius*np.cos(angle) y radius*np.sin(angle) z 1 np.sin(3*angle)*np.sin(np.pi*radius) p figure(width550, height400) levels np.linspace(0, 2, 11) contour_renderer p.contour( xx, yy, zz, levelslevels, fill_colorCividis, hatch_pattern[x]*5 [ ]*5, hatch_colorwhite, hatch_alpha0.5, line_color[white]*5 [black] [red]*5, line_dash[solid]*6 [dashed]*5, line_width[1]*6 [2]*5, ) colorbar contour_renderer.construct_color_bar(titleColorbar title) p.add_layout(colorbar, right) show(p)这里levels长度 11因此线相关向量属性必须恰好 11 个line_color用 5 白 1 黑 5 红区分不同区间line_dash前 6 实线后 5 虚线line_width前 6 细后 2 处加粗填充相关向量属性必须恰好 10 个hatch_pattern前 5 个打 x 叉线、后 5 个留空fill_colorCividis是调色板集合字典内部通过_palette_from_collection自动取出长度为 10 的调色板——这正是集合长度与所需数量不匹配时自动插值/取最近长度规则的用武之地。另一个值得参考的官方示例是 examples/topics/contour/contour.py它用OrRd调色板、[white]*4 [black]*5的线颜色和混合line_dash绘制z sin(πx) cos(πy)并在dark_minimal主题下展示带 LaTeX 标题的完整效果。实战三动画等高线Bokeh Server官方文档docs/bokeh/source/docs/user_guide/topics/contour.rst给出了一套在bokeh serve中驱动等高线动画的固定套路bokeh serve --show contour_animated.py关键操作序列如下照常调用figure.contour保存返回的ContourRenderer计算或从文件读取更新后的z数组把新的z与不变的x、y、levels传给contour_data生成新的ContourData对象调用contour_renderer.set_data(new_contour_data)更新渲染器回到第 2 步循环。这套流程之所以高效是因为等高线几何计算发生在 Python 侧ContourPy浏览器端只需接收更新后的坐标数据。文档同时提醒该套路假设网格、level 与视觉属性不变若要动态修改这些需小心处理绘图边界变化与视觉属性到 level 的重新分配更稳妥的做法是直接移除旧等高线图并新建一个。底层实现与边界行为计算引擎等高线几何计算由 ContourPy 完成contour_generator调用见 src/bokeh/plotting/contour.py填充采用FillType.OuterOffset外边界 孔洞偏移线采用LineType.ChunkCombinedNan跨线段用np.nan分隔。glyph 映射等值线对应MultiLineglyph填充多边形对应MultiPolygonsglyph 且line_width置 0见 docs/bokeh/source/docs/user_guide/topics/contour.rst 的 Advanced details 一节。排除无效点想从计算中排除网格点可用 NumPy 掩码数组mask 掉的点被剔除或直接把对应z值设为np.nan——两者等价且可混用。level 校验_validate_levels见 src/bokeh/plotting/contour.py要求levels非空、一维且严格递增np.diff(levels).min() 0.0即报错 Contour levels must be increasinglen(levels) 2时自动关闭填充计算。fill/line 互斥校验contour_data在两者都被关闭时抛出ValueErrorfrom_contour则在剩余未知关键字非空时抛出ValueError保证传参错误能被尽早暴露。小结bokeh.plotting.contour模块通过contour_data纯数据计算与from_contour渲染器构建两个函数把计算等高线几何与组织视觉呈现清晰解耦前者是纯函数式的数据管道适合反复调用以驱动动画后者是渲染器的工厂负责视觉属性拆分、颜色插值与数据源装配。日常绘图请直接使用figure.contour需要精细控制数据更新时再下沉到contour_dataContourRenderer.set_data的组合。结合construct_color_bar、调色板集合自动插值以及掩码数组排除点等能力Bokeh 的等高线功能足以覆盖从简单科学绘图到交互式 Server 动画的完整场景。【免费下载链接】bokehInteractive Data Visualization in the browser, from Python项目地址: https://gitcode.com/GitHub_Trending/bo/bokeh创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。