1 可视化图表分几个层次,底层图表库

  • echarts
  • g2
  • d3
  • chartjs 使用canvas绘制
  • nvd3

2 基于可视化图表封装的可视化组件

React系的

  • G2Plot
  • BizCharts
  • XRender的ChartRender

Vue系的

  • VCharts

计划

  • 基本部分的编写,各方案实现需求1
  • BI配置平台如何实现的?
  • 三部分组件的封装 7:30 ~ 9:00
  • 最终的组件配置方案
  • 三个图标部分解释,以及需求2的实现
  • BI平台如何配置的先删除掉?
  • 最后的总结
  • 本质的思考?

先了解数据是什么样

数据分析中两个重要的概念,指标(metrics)和维度(dimension)

如何理解数据分析中经常提到的维度和指标? - 知乎

  • 指标,一般指可量化的数据,是进行统计后的结果
  • 维度,一个分类值,事物的某种特征

以一个分析网站PV和UV的案例来看

date和page两个就是维度,日期和发生的页面

pv和uv就是指标,是根据制定条件聚合计算后的统计结果

下面是服务端返回的数据

  1. [{
  2. date: "2021-07-21",
  3. page: "home",
  4. pv: 200,
  5. uv: 100
  6. },
  7. {
  8. date: "2021-07-21",
  9. page: "detail",
  10. pv: 100,
  11. uv: 50
  12. },
  13. {
  14. date: "2021-07-22",
  15. page: "home",
  16. pv: 300,
  17. uv: 120,
  18. }
  19. {
  20. date: "2021-07-22",
  21. page: "detail",
  22. pv: 180,
  23. uv: 60,
  24. }]

以一个需求看各图表如何配置

需求1,x轴是日期,y周是pv数值,折线1是home的,折线2是detail的。即多折线图是按某一维度展开。

需求2,x周是日期,y周是数值,折线1是pv,折线2是uv。即多折线图是多个维度值。

使用Echarts

需要填入这些数据

  • 使用xAxis来描述X轴,data填入x轴的数据
  • 使用series来表述每条线,type写上图表类型,data填入数据
  • legend描述上顶部legend的内容
  1. {
  2. legend: {
  3. data: ['home', 'detail']
  4. },
  5. xAxis: {
  6. type: 'category',
  7. boundaryGap: false,
  8. data: ['2021-07-21', '2021-07-22']
  9. },
  10. yAxis: {
  11. type: 'value'
  12. },
  13. series: [
  14. {
  15. name: 'home',
  16. type: 'line',
  17. stack: '总量',
  18. data: [200, 300]
  19. },
  20. {
  21. name: 'detail',
  22. type: 'line',
  23. stack: '总量',
  24. data: [100, 180]
  25. },
  26. }

优点

  • 学习成本低,只要记住series代表一条线
  • API文档非常详细和易用
  • 生态,使用者众多,一般问题搜搜都有各种解法

存在的问题

  • 即需要将服务端数据进行转换,转换为echarts这样需要的数据格式
    • 即使echarts支持了dataset,但最终还是每个series一条线的思维,在多指标和多维度时不同的处理方式,有一定的转换成本。
  • 数据信息的丢失
    • 经过数据转换,原始的一条数据记录变成了数字
    • 如果一条记录中还有其它字段,在tooltip时想要展示更多信息,需要先找到原始数据,再进行提示。

使用g2

  1. const chart = new Chart({
  2. container: 'container',
  3. autoFit: true,
  4. height: 500,
  5. });
  6. // 传入数据
  7. chart.data(data);
  8. // 映射到图表
  9. chart
  10. .line()
  11. .position('date*pv')
  12. .color('page')
  13. .shape('smooth');
  14. chart.render();

优点

  • 如果有进一步定制能力,使用了图表garmmar语法,echarts无法满足,可以较快的进行创造。
  • garmmar语法,简洁易于推理,在坐标轴画点,然后上色就成了不同的线。

存在的问题

  • 使用的garmmar语法,如果常用图表一般都会进行开发封装,还是会封装成一般的API

使用G2Plot

需求1 多指标单维度

  1. const line = new Line('container', {
  2. data,
  3. xField: 'date',
  4. yField: 'pv',
  5. seriesField: 'page'
  6. }

需求2 单维度多指标

  • 需要先转换数据

如果是

{ date: “2021-07-22”, pv: 300, uv: 120,

}

转换为

{

date: “2021-07-22”, type: ‘pv’, value: ‘300’ }, { date: “2021-07-22”,

  1. type: 'uv',
  2. value: '300'

}

  1. // 转换数据
  2. const transformedData = transform(data)
  3. // 渲染图表
  4. const line = new Line('container', {
  5. transformedData,
  6. xField: 'date',
  7. yField: 'value',
  8. seriesField: 'type'
  9. }

总结

  • 在多指标时需要进行数据转换
  • 不同图形时,数据参数差异比较多,记忆成本 (这里没展开介绍)

使用BizCharts的ChartRender

  1. <Line
  2. meta={[
  3. { id: 'date', name: '日期', isDim: true },
  4. { id: 'page', name: '页面', isDim: true },
  5. { id: 'pv', name: '页面访问数' },
  6. }],
  7. data=data
  8. />

优点

  • 参数非常简洁,背后自动转换

只需要传入说明是否是dimension,line中如果两个dimension,则一个会自动作为分类的线。

如果两个指标,则会自动将不同指标作为多条线

缺点

  • 暂时只封装了折线图,柱状图,不知道其他图形的适用性如何

总结对比

设计思想 问题和缺点
Echarts
g2 通过
G2Plot 自动数据映射,指明x轴y轴,指明分类维度
- 需要记忆的
ChartRender 自动数据映射,指明维度和指标,自动帮助选择了x,y轴,以及分类维度 这个新加入这

需求2对比

参考各BI平台如何配置的

对比几个常见的商业BI平台

  • finebi
  • tableau
  • powerbi
案例1 两个维度 案例2 两个指标
finebi
- 横轴 date
- 纵轴 pv
- 颜色 (page)

- 横轴 date
- 纵轴 pv, uv
tableau
- 横轴 date
- 纵轴 pv
- 颜色 (page)

-

| | powerbi |
- 轴(axis) -> date
- 图例(legend) -> page
- 值(value) -> pv
|
- 轴(axis) date
- 值 pv, uv
|

同时在其他几个不同图形(条形图,柱状图)时,powerbi都是用轴和值来表述,而不是用坐标系的横轴和纵轴来进行表述。

所以综合上,从个人体验讲,powerbi的在不同图形时,描述的通用性比较强,也比较容易的理解和推理各配置。

最终我们的封装方案

我们基于echarts,因为成熟度比较高,再进行一定的封装,不需要用户做数据的转换操作。映射参数借鉴了PowerBI的表达方式。

下面列出不同图形的传入

同时还暴露了config参数,如果需要配置一些echarts的配置,此参数可以进行覆盖。

更进一步封装

一个图表的呈现,还有哪几步?

主要是三大部分

  1. api请求数据
    1. 可能和容器内或容器外筛选功能关联
    2. lazyload,未展示时,不请求和展示
    3. 并发控制,同时请求的不能超过5个 (大量并发服务器计算压力)
  2. 图表展示卡片容器
    1. 筛选空间,时间筛选等
    2. 对比功能,对比昨天,对比上周
    3. 功能按钮,筛选
  3. 图表的绘制
    1. 不同种类的图形

因此,设计了三个组件,保证职责的分离,ChartContainer和ChartCard通过context来进行提供和消费

ChartContainer控制api请求和数据

  • lazyload,以及监听参数变更请求数据
  • 通过context,提供了相应的数据和api
    • 数据,loading,data
    • api,refresh,switchCompare

ChartCard负责图表卡片,以及里面的功能按钮

  • 通过context,消费chartContainer提供的api,在各功能按钮点击时调用

Line图表组件

即图表的展示,为了通用性,保证低耦合,data数据通过 chart-container 提供的 slot-scope 暴露的data参数传入。

下面是组合代码的展示

  1. <chart-container
  2. api="api/pageView"
  3. :params="params"
  4. >
  5. <template v-slot:default="{ data }">
  6. <chart-card>
  7. <my-line
  8. :data="data"
  9. ></my-line>
  10. </chart-card>
  11. </template>
  12. </chart-container>

图表数据映射支持

  • chartjs中的数据parsing

https://www.chartjs.org/docs/latest/general/data-structures.html

  • echarts中的dataset

  • g2中直接映射的描述

对数据 & 图表本质的思考

总结

在设计封装时,需要考虑自己是否了解所有需求,所有的场景,否则设计就会有问题。

如刚开始未考虑到 单维度多指标需求 ,即指标有 pvuv 两个