《表的多维世界技术篇-缨缨》演讲视频 + 文字版

简介: 《表的多维世界技术篇-缨缨》演讲视频 + 文字版
编者按:昨天与大家分享了 《表的多维世界-设计篇》,今天给大家带来蚂蚁集团 AntV 工程师缨缨在 SEE Conf 2022 的演讲内容《表的多维世界-技术篇》,包括演讲视频及文字内容,欢迎享用。

大家好,我是来自 AntV 的前端工程师缨缨。非常感谢白弦带来设计篇的分享。接下来由我带大家一起从技术的视角再来重新审视一下「表的多维世界」。

首先让我们再来回顾下这个问题,提到表格组件你会想到什么?前端同学们可能会马上想到像 Ant Design 这样的一维表格。因为只有一个维度,因此看数的时候是按照行维度一行一行,或者按照列维度一列一列去看的。还可能会想到像是语雀电子表格或者 Excel 电子工作薄,常用于数据的预处理加工。那我们这里所说的多维交叉表格指的是什么?我们先来看一个简单的列子。下图中圈出来的指标 「7789」,我们在看数的时候,会先找到其所在行维度为 「浙江省」下的 「杭州市」;列维度为 「家具」类目下的「桌子」。所以整个看数思路是跟一维表格不一样的。我们所谓的交叉表,其实就是在行和列可以不断累加维度。中间的数据单元格区域,就是由行列交叉而成的一个笛卡尔集。以上只是一个最基础的多维交叉表格,接下来我们来看一个更复杂一些的,真实业务场景中的多维交叉表格。这是一个「KPI趋势分析表」,面对这样一个表格,看数思路如下:首先纵向找到关注的指标:「A产品交易总金额」- 「DAU」,接着横向对比发现该指标的目标完成度是未达标,就会继续往右对比各个财年和季度的指标变化,找到未完成的原因,从而作出决策。到这里大家会发现,当我们从表的一维世界来到多维世界,无论是看数的思路,还是有限的可视空间能承载的信息量都变得不一样了。那接下来我们就从开发的视角来看下,如何实现一个多维交叉分析表。首先,让我们来看一个小故事,这是发生在产品经理和开发同学之间的对话。看起来确实不复杂,为了方便解构,我们按照行列分割线将表格划分为以下四个区域:
这个时候产品同学出来有话要说:产品经理:“真实数据都是万级别 的,不要卡顿哦~ ”出于性能的考虑,我们选择 Canvas 作为渲染载体。以此为基础,我们将整个实现步骤抽象为:「数据准备」、「布局计算」、「渲染」三个阶段。与之相对应,我们便得到了三个逻辑处理模块:「数据处理模块」、「布局模块」、「渲染模块」。

数据准备

首先我们先来看一下数据准备阶段我们做了什么。其实在这个阶段,我们做的事情就是用数据结构去把行列维度以及指标之间的关联关系给表达出来。那具体怎么做呢?观察表格不难发现,表格的行头和列头都存在树状结构,所以我们第一步就是分表构造行、列头的树结构。再来观察下原始数据会发现,都是维度平铺开来的一维数组,因此我们要做的就是根据行列维度信息查询单元格数据信息,这是该阶段最主要的事情,也是渲染阶段依赖的逻辑模块(绘制每个格子的时候都需要查询对应的单元格信息)。很自然会想到,循环遍历一遍就能做这个事情。对于 「3000」  这个指标,我们设置好查询条件可以由以下代码得到:时间复杂度为 O(n*m) ,随着数据量和行列维度增加,时间复杂度会变得越来越高。那有没有更好的方式呢?方法有很多,这里我们提供一种解法:具体做法就是给维值进行编码,遵循同层级下序号递增的原则,指标「3000」对应的行路径为 [0, 0], 列路径为[0], 合起来共同组成路径数组为 [0, 0, 0]映射到代码里面,首先进行数据的预处理,以行列的维度路径作为索引,分别构建行、列的树结构。然后我们再根据这一个结构关系,把原始的一位数据数组,转换成一个带有路径标识的多维数组。这样我们每一次取数的时候,只需要拿这个行列维度的路径组合直接去匹配就好了。如此一来时间复杂度就降到了 O(1)。

布局计算

接下来是布局计算了。在这个阶段我们要做的事情,就是去计算每一个单元格子的宽高以及坐标。我们可以先看一下,当列宽一定的情况下,是一个怎样的计算规则呢?通过观察可以发现行头的宽度其实就是每个层级的行头的叶子节点宽度的累加。列头的宽度也同理。同样地可以得到行头和列头的高度,是它们叶子节点的高度的累加。最后角头的宽度由行头的宽度所决定,高度由列头的高度所决定。根据这个准则就可以计算出每一个单元格子的坐标信息。但是在真实的场景下会有不同的列宽诉求。我们把其中常见几类总结为以下三种布局。第一种是紧凑布局。在这种布局场景下,强调的是屏效。换句话说就是在有限的空间范围内展示尽可能多的指标,把多余的留白都给去掉。在这种情况下,做法就是去进行列宽采样。比如在当前列选择前50个指标,分别进行文本宽度计算,然后选择最长的一个作为当前列的列宽。同时字体会存在大小、粗细等差别,所以我们在采样的过程中,需要把字体的样式也考虑进去。另一种布局,就是等宽布局。当指标列不是很多,但又希望它能够撑满平均的撑满整个画布的时候,我们就可以把行头和列头同时均分画布宽度。但对于数据分析师来说,更多时候会关注的是指标本身。所以还会有一个混合的布局:行头紧凑,列头均分剩余画布宽度。

渲染

讲完布局就到了渲染的阶段。如果想要提升性能的话,我们自然会想到要减少不必要的开销。也就说按需渲染,只绘制视窗内的单元格。比如说下面的表格,全量是九行乘以三列的单元格。但是只需要绘制视窗内三行乘两列的单元格。滚动的时候再去动态删减行列。比如现在向下滚动了两行,那会去动态的减掉一行,再增加三行。中间留白的区域,其实就是所谓的出血区。作用是为了防止用户滚过快的情况下发生白屏的现象。配合数据准备阶段提供的高效单元格信息查询模块,已经可以解决一些极端的情况下的渲染性能。以下是100万条明细数据,1000行乘1000列的交叉表。可以看到滚动效果是流畅的,不会出现卡帧或者白屏现象。当然在真实的业务场景中,一般不会到这个数据量级。因为到了这个数据量级,可读性已经很差了。所以说我们以上的这一套解决方案,基本可以覆盖大部分的业务场景。我们来看一下我们的用户是否满意呢?果然你的用户非常满意,并且向你提出了新的需求。我们先拆开来看一下,如果想要实现一个同环比的效果,应该怎么去做?目前看来渲染模块还缺少一个字段标记的子模块。具体做法就是在单元格绘制的这个过程中增加一个文本和图形的映射通道函数。对应到代码里,其实就是把单元格的数据信息向外去透出。这样的话外部就可以根据信息去写自己的染色逻辑。同理,也可以内置单元格背景色和基础条形图,这样就能通过简单的配置项实现指标进度监控、差值对比的需求。回头来看看比较复杂的子弹图需求,看到这里,开发同学开始思考了:今天想画一个子弹图,明天想画一个折线图,后天想画多行文本,这么多自定义的需求怎么去满足呢?那我干脆就把表格绘制的各个生命周期都开放出去,这样的话就想画什么画什么了。实现思路跟刚才字段标记本质是一样的,就是把当前单元格的数据信息开放出去,外部拿到这个信息去编写自己的绘制的逻辑。举个例子,如果把数据单元格绘制给开放出去,那就可以去实现如下图多行的文本绘制的效果。指标就不再局限于一个单元格子里只有一个指标。如果把整个表格框架的绘制生命周期开放出去,就可以实现一些像指标分组的需求。同理也可以去自定义行头或者列头。到这里,我们就有了一个相对完整的功能模块结构了。可以依靠「数据处理模块」、「布局模块」、「渲染模块」实现基础的交叉表格绘制。

交互与主题

到这里再来看一下我们的用户是否满意?然而,用户又提出了新的需求。因为在真实的场景下,数据量比较大,当用户在查找一个指标的时候,肉眼很难对应上行列维度信息,希望增加一个hover单元格高亮的十字器?这个需求听起来非常合理,肯定得冲。那怎么冲呢?看来,我们还需要一个独立的交互模块来专门处理交互相关的逻辑。首先需要一个状态机,用来存储当前交互的状态信息。当事件触发后,去判断当前的交互类型,然后执行对应的回调事件。最终通知到每一个单元格,让它检查一下自己是否需要更新。如果需要更新,要更新什么样式。这样一来就得到了一个基础交互和样式之间的映射关系。举个例子,比如我们刚刚说到的高亮查询,其实就是把当前需要高亮单元格子更改单元格背景色。再把所有相关样式信息抽离出来,就得到了一个主题模块。至此,我们的功能模块又变得丰富起来。增加了一个单独的交互模块来处理交互逻辑。把主题和渲染两个模块合并到UI这一个大模块里面去。这个时候我们其实就已经可以实现一个有交互、高性能的交叉表了。

单元格合并

满足了基础的交互,果然又有了新的需求。用户现在想要对不同的人群进行划分和打标。这时就需要去做一个不规则的单元格合并操作,合并区域可能会呈现 T型、F型、L型等。开发同学也有点犯难,回想一下,其实 excel 的单元格合并,也是一个比较规则的矩形。那这个需求要怎么满足呢?首先我们来分析一下,想要实现下图的单元格合并效果,就是把这个问题转换成绘制一个不规则的多边形。再转换一步,就是去计算出一个多边形的有向闭合路径,就是所谓的一笔描边。那这样看起来布局模块还缺少一个专门处理像单元格合并功能的逻辑模块。接下来看一下具体的做法。假如用户选择了如下四个单元格进行合并, 首先根据已经渲染出来的单元格的起点坐标和宽高,可以得到四个矩形的顶点坐标分别为:然后按照统一方向构造每个矩形的边坐标合集,这里用了顺时针方向,得到边合集如下:然后将重叠的边去掉,(边坐标相反的即为重叠边):得到不重叠边如下:接下来只需要从这些边中随便选一条边的第一个点作为起点,然后按照顺序连起来即可。这样做的一个好处就是不需要去判断当前用户选择的格子是否能够跟其他区域形成一个闭合路径。因为在连线的过程中,不在这个闭合区域内的边,就已经被自动过滤掉了。遵循这个规则:当前边的终点作为下一条边的起点,就可以得到一个不规则的多边形的路径的合集。这样的话我们就可以实现,单元格十字合的效果。现在,我们已经有了一个相对非常完整的功能模块结构。首先有一个数据处理的模块来构建维度和指标的关联关系。然后有一个布局的模块,然后分别会进行宽高、坐标的计算,以及去处理单元格的合并。还有一个UI模块,由主题和渲染两个子模块构成。渲染会开放出绘制表格的各个生命周期,以及去处理字段标记这样的逻辑。最后有一个交互的模块去实现一些基础的交互。至此,我们已经可以自由的绘制各种各样的表格了。白弦在设计篇给我们分享的小故事中的表格,就可以拿这一套方案快速实现。加亿点点交互细节,就能得到,行列头联动高亮,hover 出 tooltip, 拖拽调整行高列宽等交互效果。我们将以上的整套的方案都沉淀为 AntV 表可视化的解决方案 —— S2。感兴趣的同学可以扫码访问我们的官网。官网上有更多场景案例和 demo 的展示。如果大家有什么疑问,也可以扫码进我们的交流群。谢谢大家,我今天的分享就到这里结束了。

相关文章
|
9月前
|
数据采集 SQL 数据可视化
大数据可视化技巧:借助PowerBI提升数据故事讲述力
【4月更文挑战第8天】Power BI助力大数据可视化,支持多种数据源连接,如SQL Server、Excel,提供数据清洗与转换功能。通过选择合适图表类型、运用颜色和大小强化表达,创建交互式仪表板。讲述数据故事时,注重故事主线设计,利用叙事技巧引导观众,并添加文本说明。分享已完成报告,提升数据驱动决策能力。动手实践,体验Power BI的强大与易用。
231 0
|
10天前
|
存储 人工智能 自然语言处理
OmniThink:浙大联合阿里通义开源 AI 写作框架,基于深度思考扩展知识边界,实时展示思考过程
OmniThink 是浙江大学与阿里通义实验室联合开发的机器写作框架,通过模拟人类迭代扩展和反思过程,生成高质量长篇文章,显著提升知识密度和内容深度。
115 12
OmniThink:浙大联合阿里通义开源 AI 写作框架,基于深度思考扩展知识边界,实时展示思考过程
|
数据采集 运维 DataWorks
客户案例:数仓规范化-菜鸟数据模型管理实践(一)| 学习笔记
快速学习客户案例:数仓规范化-菜鸟数据模型管理实践。
客户案例:数仓规范化-菜鸟数据模型管理实践(一)| 学习笔记
|
监控 数据可视化 安全
《表的多维世界设计篇-白弦》演讲视频 + 文字版
《表的多维世界设计篇-白弦》演讲视频 + 文字版
170 0
|
存储 安全 数据管理
OushuDB 小课堂丨非结构化数据管理的关键:交流您的数据
OushuDB 小课堂丨非结构化数据管理的关键:交流您的数据
129 0
|
数据采集 自然语言处理 分布式计算
客户案例:数仓规范化-菜鸟数据模型管理实践(二)| 学习笔记
快速学习客户案例:数仓规范化-菜鸟数据模型管理实践。
客户案例:数仓规范化-菜鸟数据模型管理实践(二)| 学习笔记
|
SQL 数据采集 存储
客户案例:数仓规范化-菜鸟数据模型管理实践(三)| 学习笔记
快速学习客户案例:数仓规范化-菜鸟数据模型管理实践。
客户案例:数仓规范化-菜鸟数据模型管理实践(三)| 学习笔记
|
存储 SQL 关系型数据库
时空场景开发实践(一)|学习笔记
快速学习时空场景开发实践(一)
271 0
时空场景开发实践(一)|学习笔记
|
数据采集 存储 数据可视化
【数据可视化】数据之美---揭密优雅的数据解决方案背后的故事
【数据可视化】数据之美---揭密优雅的数据解决方案背后的故事
177 0
|
存储 传感器 消息中间件
【实践案例】Databricks 数据洞察在美的暖通与楼宇的应用实践
获取更详细的 Databricks 数据洞察相关信息,可至产品详情页查看:https://www.aliyun.com/product/bigdata/spark
【实践案例】Databricks 数据洞察在美的暖通与楼宇的应用实践