Metabase Treemap 可视化完全指南:从数据形态、查询构建到图表设置的源码级解析
【免费下载链接】metabaseThe easy-to-use open source Business Intelligence and Embedded Analytics tool that lets everyone work with data :bar_chart:项目地址: https://gitcode.com/GitHub_Trending/me/metabase
Treemap(矩形树图)是 Metabase 中用于展示层级数据的可视化类型,它把一棵"树"呈现为嵌套矩形,用矩形的面积表示某个指标的数值大小。本文以官方文档 docs/questions/visualizations/treemap.md 为主体,结合前端源码与示例图片,系统讲解 Treemap 的适用场景、数据形态、查询构建步骤、钻取交互与图表设置项,并深入剖析其底层实现原理,帮助你准确判断"何时用、怎么配、如何排查",在提问(Question)与仪表盘(Dashboard)中快速产出可读性强的占比类分析图。
什么是 Treemap
Treemap 可视化把层级数据(树形结构)显示为嵌套矩形。层级最底层的矩形称为叶子(leaf),叶子矩形的大小由所选指标(metric)的数值决定:数值越大,占据的面积越大,反之越小。
当查询按两个维度分组时:
- 第一个维度决定父级类别,表现为较大的外圈矩形;
- 第二个维度嵌套在父级类别内部,表现为叶子矩形。
下图是一个典型的双维度 Treemap:指标为订单数(Count of orders),按两个维度分组——第一维度是产品类别(大矩形),第二维度是供应商(每个类别内部的嵌套叶子)。
何时使用 Treemap
Treemap 适合用来比较"一个总量如何按某个分类维度拆分",或在第一个分类维度之下再嵌套第二个分类维度进行细分。
它的优势在于展示部分与整体(part-to-whole)的关系,并且特别擅长处理类别数量很多的场景:
- 当类别很多时,饼图会被切成细得难以辨认的薄片,条形图则会变得很长;
- Treemap 能在紧凑的空间里容纳大量类别,还能在第一个维度内部再嵌套第二层维度。例如先按产品类别统计订单数,再在每个类别下按供应商细分。
Treemap 的劣势在于不擅长类别之间的精确对比,因为人眼判断"面积大小"比判断"条形长度"更困难。因此:
- 如果只有一个维度且类别数量很少,推荐改用饼图或条形图;
- 如果核心诉求是逐项对比各个类别的数值,条形图或饼图通常是更好的选择。
从实现层面看,Metabase 在 TreemapChart 的 definition.ts 中通过isSensible定义了 Treemap 的"合理数据前提":至少 1 行数据、至少 1 个维度列、至少 1 个指标列。只有满足该条件,可视化选择器中才会把 Treemap 判定为合适的图表。
Treemap 需要的数据形态
要创建 Treemap,你需要构建一个返回单一指标、并按一个或两个维度分组的查询。
按一个维度分组:扁平叶子
当指标只按一个维度分组时,Treemap 显示为一组扁平的叶子,没有父级类别。以下是"按类别统计订单数"的数据形态:
| Category | Count |
|---|---|
| Widget | 612 |
| Gadget | 562 |
| Gizmo | 491 |
| Doohickey | 462 |
按两个维度分组:嵌套结构
当指标按两个维度分组时,Treemap 会把第二个维度嵌套进第一个维度内部:
- 第一个维度决定外圈矩形及其颜色;
- 第二个维度决定外圈矩形内部嵌套的叶子;
- 指标数值决定每个叶子的大小。
以下是"按类别(父级)和供应商(叶子)统计订单数"的数据形态:
| Category | Vendor | Count |
|---|---|---|
| Widget | Jerrell Gulgowski Inc | 26 |
| Widget | Brittany Mueller Inc | 25 |
| Gadget | Jerrod McLaughlin LLC | 24 |
| Gadget | Miles Ryan Group | 22 |
| Gizmo | Mr. Tanya Stracke and Sons | 30 |
| Doohickey | Kuhn-O'Reilly | 27 |
对应的图形即上文第一张图:每个产品类别是一个带颜色的父矩形,内部按供应商拆分成多个叶子,叶子面积正比于订单数。
在底层实现中,viz-core 的 treemap/model/data.ts 通过getTreemapChartColumns从treemap.grouping、treemap.sub_grouping、treemap.value三个设置项解析出图表所需的列描述(column descriptors);只有当分组列与指标列都存在时才会返回有效结果,否则返回null,图表无法渲染。随后的getTreemapData会把查询返回的原始行数据逐行归并成TreemapTree树结构,父节点数值恒等于其存活叶子数值之和,从而保证矩形面积能够正确嵌套包含(源码注释特别指出,这与饼图"丢弃整个负值组但仍在聚合中求和"的处理方式不同,Treemap 是按行过滤负值后再求和)。
构建 Treemap 查询
在查询构建器(Query Builder)中构建一个嵌套 Treemap 的步骤如下:
- 在查询构建器中点击Summarize(汇总);
- 选择一个指标,例如Count of rows(行数计数);
- 按第一个维度分组,例如Category(类别);
- 添加第二个分组维度,例如Vendor(供应商);
- 在可视化选择器中选择Treemap。
分组顺序至关重要:第一个分组决定外圈矩形,第二个分组决定嵌套在每个外圈矩形内部的叶子。
查询构建完成后,点击任意父级类别即可放大(zoom)进入该类别,查看其内部的叶子分布。
源码中对该交互的实现位于 TreemapChart/events.ts 的dispatchTreemapViewRoot:它通过 ECharts 的treemapRootToNode动作把视口根节点切换到目标父节点;对应的点击处理逻辑getTreemapClickHandler会先判断当前是否处于"有子节点且未下钻"的概览状态,若是则触发下钻(drill to group),否则生成钻取数据交给onVisualizationClick回调。导航状态本身由 use-treemap-navigation.ts 管理。
钻取(Drill-through)
Treemap 支持两类点击钻取行为:
- 父级类别(Parent category):点击父级类别可放大进入该类别;要返回完整 Treemap,点击页面左上角出现的返回箭头(面包屑导航)即可。面包屑由 TreemapBreadcrumb.tsx 渲染,其中会显示当前分组标签与对应指标值。
- 叶子(Leaf):点击叶子可以查看底层记录、按另一个维度拆分(breakout)、查看自动洞察(automatic insights),或按该叶子数值进行过滤。
需要特别注意的是,钻取文档中指出:用 SQL 编写的原生查询(native query)可用的钻取选项十分有限,这一点同样适用于 Treemap。从源码看,events.ts 中getTreemapClickData仅对非原生(non-native)查询附加dimensions钻取维度信息,正是这一限制的具体体现。
Treemap 图表设置
在提问页面中点击图表左下角的齿轮图标即可打开图表设置(chart settings),其中包含Data(数据)与Display(显示)两个标签页。
Data 数据页
在图表设置的Data标签页中,可以配置图表的三个核心要素:
- Grouping(分组):决定父级类别的维度。可以对每个父级类别进行两类自定义:
- 自定义颜色:点击类别名称旁的颜色色块(color swatch),即可为该父类别设置独立颜色;
- 重命名:点击类别名称旁的...菜单,可修改父类别在图中显示的名称。
- Sub-grouping(子分组):嵌套在每个父类别内部、作为叶子的维度。该选项要求查询至少包含两个维度。
- Value(数值):决定每个叶子大小的指标。点击...打开格式化菜单,可设置数字样式(number style)、分隔符样式(separator style)、小数位数(number of decimal places)、乘数(multiplier),以及前缀/后缀(prefix/suffix)。
这些设置项在源码中有非常明确的对应关系。viz-core/shared/settings/treemap.ts 定义了全部设置键:
treemap.grouping的默认值取数据中第一个"是维度且非指标"的列(definition.ts),并在变更分组时擦除treemap.rows中持久化的行设置;treemap.sub_grouping默认取第一个"与分组和数值列都不同"的维度列,并且仅当查询中存在这样的维度列时才显示该选项(getHidden逻辑);treemap.value默认取第一个"与分组列不同"的指标列;treemap.rows存储每个父类别行(key、名称、颜色、是否启用)的持久化设置,由getTreemapRows基于原始数据聚合生成——shared/settings/treemap.ts 中可以看到,它按分组键(key)对行进行聚合、按数值降序排列、为每个键分配颜色,并保留用户此前保存的启用/禁用与自定义名称状态。
Display 显示页
在图表设置的Display标签页中,可以控制标签与数值的显示与隐藏,共有四个开关:
- Show parent labels(显示父级标签):默认开启,仅当存在子分组(sub-grouping)时可用;
- Show parent values(显示父级数值):默认开启,依赖父级标签开关——当父级标签被关闭时该开关会被禁用(disabled);
- Show leaf labels(显示叶子标签):默认开启;
- Show leaf values(显示叶子数值):默认开启,依赖叶子标签开关——当叶子标签被关闭时该开关会被禁用。
上述依赖关系与默认值均可在 definition.ts 的 SETTINGS_DEFINITIONS 中逐一核对。值得注意的是,父级标签的实际显示还会受图表尺寸影响:在 TreemapChart.tsx 中调用shouldShowParentLabels(gridSize, settings)判断,即在仪表盘或文档等紧凑布局(compact)下,父级标签可能被自动隐藏以保持可读性。
局限与替代方案
使用 Treemap 时需要注意以下限制:
- 只能展示一个指标:Treemap 展示的是按一个或两个维度分组的单一指标。如果查询返回多个指标,Treemap 只使用你在Value槽位中选择的那个指标。源码层面,definition.ts 的
checkRenderable会要求至少存在一个"非分组列"的指标列,否则抛出ChartSettingsError,提示"请至少添加一个指标列作为数值(Add at least one metric column to use as the value)"。 - 不支持超过两层嵌套:当查询返回的维度超过两个时,Treemap 只使用Grouping与Sub-grouping槽位中的两个维度,其余未使用的维度会被合并进每个叶子中,不会产生第三层嵌套。
- 小叶子可能难以观察:当一个叶子远大于其他叶子时,较小的叶子会变得太小,以至于难以看清或无法点击选中。
- 适合部分-整体对比:Treemap 擅长展示"各组成部分对总量的贡献"。如果需要类别之间的精确数值对比,请改用条形图;类别数量很少时,可改用饼图。
另外从源码可以确认两个细节行为:其一,若某行指标的数值为负数,该行会在求和前被过滤(model/data.ts 注释说明),以保证父节点面积恒等于叶子面积之和;其二,若用户曾手动关闭过某个父类别(在treemap.rows中标记enabled: false),该类别会从图中隐藏,但相关行数据仍会保留在设置中,待重新启用后恢复显示。
小结
Treemap 是 Metabase 中处理"多类别占比 + 双维度嵌套"场景的首选图表:一个指标、一个或两个分组维度,即可在紧凑空间内呈现清晰的部分-整体结构,并支持父级放大下钻与叶子级钻取。掌握"分组顺序决定嵌套层级""Value 槽位决定叶子大小""Display 开关控制标签与数值"这三个要点,配合 Data 页的颜色自定义与数值格式化,就能快速产出高质量的占比分析图;而当需要精确对比类别数值时,记得回到条形图或饼图。
【免费下载链接】metabaseThe easy-to-use open source Business Intelligence and Embedded Analytics tool that lets everyone work with data :bar_chart:项目地址: https://gitcode.com/GitHub_Trending/me/metabase
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考