基于 VTable 的多维数据展示的原理与实践

技术

多维表格介绍

多维表格又名透视表、交叉表、Pivot Table,指的是可以在行维度和列维度放入一个或多个维度,显示维度之间相互关系的一种表格。用户可以一目了然地分析出各种场景指标以及对比,旨在帮助业务分析推动决策。

假设需要分析如下表格所示的销售数据:

picture.image

在对这样的表数据进行分析时,不同的数据分析师或者不同角色都会基于自己感兴趣的业务角度提出相关的问题,比如:有人关心各个地区的销售额,希望找出销售情况较低的地区;有人需要了解近期内不同产品类别的销售额对比,便于做后期的产品研发······

这些问题中的业务角度,比如地区、类别、时间年份就是维度,“销售额”就是指标。

多维表格概念

理解 BI 多维分析中的几个核心概念:

  • 维度 :用来对数据进行分类和人们观察业务情况的角度;
  • 维度的层次 :根据维度细节程度不同,划分出来的一类属性,是维度预先定义的不同级别。例如,日期维度的层次包括年、月、日;地区维度的层次包括:国家、省份、城市;维度层次各级别也都属于维度 ,但维度成员之间具有一定的关系,一般在分析中常作为钻取的方向。
  • 维度成员 :是各维度上数据项的取值,即维度值。例如,日期维度的层次月的维度成员有:1月、2月、3月等,地区的成员有:东北,华北,华中等;
  • 指标 :用来描述业务情况的数据,例如,销售额、成本、利润等度量值。

picture.image

在多维分析表中如何展示维度 的呢?下图中一共有四个业务维度:地区省份年份季度,看数指标:销售额利润

picture.image

针对图中销售数据,位置在单元格[5, 5],即列5行5的数据:代表了2016年Q2季度下东北地区黑龙江省的销售利润值。也就是对应到行维度值:['东北', '黑龙江'],列维度:['2016', '2016-Q2'],指标:'利润'。接下来将介绍如何用VTable实现这种多维表格。

VTable实现多维表格

概念映射到配置项

上图透视表的配置如下:


          
const option={  
  rows:['region','province'], //行维度  
  columns:['year','quarter'], //列维度  
  indicators:['sales','profit'], //指标  
  records:[ //数据源  
    {  
      region:'东北',  
      province:'黑龙江',  
      year:'2016',  
      quarter:'2016-Q1',  
      sales:1243,  
      profit:546  
    },  
    ...  
  ]  
}  

      

该配置是多维表格最简配置。随着对功能要求的复杂性可以针对各功能点来添加各项配置来满足需求。

数据分析相关配置:

配置项类型描述
rowsstring[]IDimension[]
columnsstring[]IDimension[]
indicatorsstring[]IIndicator[]
dataConfig.aggregationRulesaggregationRule[]按照行列维度聚合值计算规则
dataConfig.derivedFieldRulesDerivedFieldRule[]派生字段
dataConfig.sortRulessortRule[]排序规则
dataConfig.filterRulesfilterRule[]过滤规则
dataConfig.totalstotalRule[]小计或总计

dataConfig配置定义:


          
/**  
 * 数据处理配置  
 */  
export interface IDataConfig {  
  aggregationRules?: AggregationRules; //按照行列维度聚合值计算规则;  
  sortRules?: SortRules; //排序规则;  
  filterRules?: FilterRules; //过滤规则;  
  totals?: Totals; //小计或总计;  
  derivedFieldRules?: DerivedFieldRules; //派生字段定义  
  ...  
}  

      

dataConfig 应用举例:

  1. 数据汇总规则

具体示例:https://visactor.io/vtable/demo/data-analysis/pivot-analysis-total


          
dataConfig: {  
      totals: {  
        row: {  
          showGrandTotals: true,  
          showSubTotals: true,  
          subTotalsDimensions: ['province'],  
          grandTotalLabel: '行总计',  
          subTotalLabel: '小计'  
        },  
        column: {  
          showGrandTotals: true,  
          showSubTotals: true,  
          subTotalsDimensions: ['quarter'],  
          grandTotalLabel: '列总计',  
          subTotalLabel: '小计'  
        }  
      }  
    },  

      
  1. 排序规则

具体示例:https://visactor.io/vtable/demo/data-analysis/pivot-analysis-sort-dimension


          
      sortRules: [  
        {  
          sortField: 'city',  
          sortByIndicator: 'sales',  
          sortType: VTable.TYPES.SortType.DESC,  
          query: ['办公用品', '笔']  
        } as VTable.TYPES.SortByIndicatorRule  
      ]  

      
  1. 过滤数据

具体示例:https://visactor.io/vtable/demo/data-analysis/pivot-analysis-filter


          
filterRules: [  
        {  
          filterFunc: (record: Record<string, any>) => {  
            return record.province !== '四川省' || record.category !== '家具';  
          }  
        }  
      ]  

      
  1. 聚合方式

具体示例:https://visactor.io/vtable/demo/data-analysis/pivot-analysis-aggregation


          
    aggregationRules: [  
        //做聚合计算的依据,如销售额如果没有配置则默认按聚合sum计算结果显示单元格内容  
        {  
          indicatorKey: 'TotalSales', //指标名称  
          field: 'Sales', //指标依据字段  
          aggregationType: VTable.TYPES.AggregationType.SUM, //计算类型  
          formatFun: sumNumberFormat  
        },  
        {  
          indicatorKey: 'OrderCount', //指标名称  
          field: 'Sales', //指标依据字段  
          aggregationType: VTable.TYPES.AggregationType.COUNT, //计算类型  
          formatFun: countNumberFormat  
        },  
        {  
          indicatorKey: 'AverageOrderSales', //指标名称  
          field: 'Sales', //指标依据字段  
          aggregationType: VTable.TYPES.AggregationType.AVG, //计算类型  
          formatFun: sumNumberFormat  
        }  
      ]  

      
  1. 派生字段

具体示例:https://visactor.io/vtable/demo/data-analysis/pivot-analysis-derivedField


          
  derivedFieldRules: [  
      {  
        fieldName: 'Year',  
        derivedFunc: VTable.DataStatistics.dateFormat('Order Date', '%y', true),  
      },  
      {  
        fieldName: 'Month',  
        derivedFunc: VTable.DataStatistics.dateFormat('Order Date', '%n', true),  
      }  
    ]  

      

数据分析过程

依赖配置:维度,指标及dataConfig。

遍历数据的流程:

遍历一遍records ,解析出行列表头维度值用于展示表头单元格,将records中所有数据分配到对应的行列路径集合中并计算出body部分指标单元格的聚合值。

picture.image

数据维度tree

根据上述遍历的结构,将产生一棵维度树,从这棵树可以查找到单元格的值及值的原始数据条目。

picture.image

经过对record分组聚合的分析计算,最终呈现到表格中单元格数据和records数据源的对应关系:

picture.image

自定义维度树

虽然具有分析能力的多维表格可以自动分析各个维度的维度值组成行列表头的树形结构,并且可以根据dataConfig.sortRules进行排序,但具有复杂业务逻辑的场景还是期望可以能够自定义行列表头维度值 及其顺序。那么可以通过rowTree和columnTree来实现这些业务需求场景。

picture.image

自定义树的配置详情:


          
const option = {  
    rowTree: [{  
        dimensionKey: 'region',  
        value: '中南',  
        children: [  
            {  
                dimensionKey: 'province',  
                value: '广东',  
            },  
            {  
                dimensionKey: 'province',  
                value: '广西',  
            }  
        ]  
    },  
    {  
        dimensionKey: 'region',  
        value: '华东',  
        children: [  
            {  
                dimensionKey: 'province',  
                value: '上海',  
            },  
            {  
                dimensionKey: 'province',  
                value: '山东',  
            }  
        ]  
    }],  
    columnTree: [{  
        dimensionKey: 'year',  
        value: '2016',  
        children: [  
            {  
                dimensionKey: 'quarter',  
                value: '2016-Q1',  
                children: [  
                    {  
                        indicatorKey: 'sales',  
                        value: 'sales'  
                    },  
                    {  
                        indicatorKey: 'profit',  
                        value: 'profit'  
                    }  
                ]  
            },  
            {  
                dimensionKey: 'quarter',  
                value: '2016-Q2',  
                children: [  
                    {  
                        indicatorKey: 'sales',  
                        value: 'sales'  
                    },  
                    {  
                        indicatorKey: 'profit',  
                        value: 'profit'  
                    }  
                ]  
            }  
        ]  
    }],  
    indicators: ['sales', 'profit'],  
    //enableDataAnalysis:true,  
    corner: {  
        titleOnDimension: 'none'  
    },  
    records: [  
        {  
            region: '中南',  
            province: '广东',  
            year: '2016',  
            quarter: '2016-Q1',  
            sales: 1243,  
            profit: 546  
        },  
        {  
            region: '中南',  
            province: '广东',  
            year: '2016',  
            quarter: '2016-Q2',  
            sales: 2243,  
            profit: 169  
        }, {  
            region: '中南',  
            province: '广西',  
            year: '2016',  
            quarter: '2016-Q1',  
            sales: 3043,  
            profit: 1546  
        },  
        {  
            region: '中南',  
            province: '广西',  
            year: '2016',  
            quarter: '2016-Q2',  
            sales: 1463,  
            profit: 609  
        },  
        {  
            region: '华东',  
            province: '上海',  
            year: '2016',  
            quarter: '2016-Q1',  
            sales: 4003,  
            profit: 1045  
        },  
        {  
            region: '华东',  
            province: '上海',  
            year: '2016',  
            quarter: '2016-Q2',  
            sales: 5243,  
            profit: 3169  
        }, {  
            region: '华东',  
            province: '山东',  
            year: '2016',  
            quarter: '2016-Q1',  
            sales: 4543,  
            profit: 3456  
        },  
        {  
            region: '华东',  
            province: '山东',  
            year: '2016',  
            quarter: '2016-Q2',  
            sales: 6563,  
            profit: 3409  
        }  
    ]  
};  

      

最终效果如:

picture.image

VTable官网示例:https://visactor.io/vtable/demo/table-type/pivot-table

自定义树的复杂在于组建行列维度树,可酌情根据业务场景来选择使用,如果具有复杂的排序、汇总或分页规则可选择使用自定义方式。

注意选择自定义树的配置方式将没有数据聚合能力,即匹配到的数据条目中的某一条即作为单元格指标值。

业务场景配置示例

下面介绍几个常见需求配置内容:

  1. 需求: 不同指标设置不同格式

配置方式: 为指标设置不同的cellType。具体示例代码:https://codesandbox.io/s/vtable-pivot-table-indicators-pxphqx

picture.image

  1. 需求: 不同维度设置不同字号,行维度的region维度值设置字号为20,province维度值设置字号为16。

配置方式: 可以通过在对应维度的headerStyle中配置样式来实现。

picture.image

  1. 需求: 角头配置显示行维度名称。

配置方式: 默认角头会显示列维度columns中的维度名,如果想以行维度的维度名显示在角头,配置titleOnDimension为row。

picture.image

  1. 需求: 趋势分析表——展示不同时间段的销售情况和同环比及趋势图

配置方式: 将其中某一个指标cellType设置为'sparkline',对于图表趋势可以使用icon进行配置

示例地址: https://visactor.io/vtable/demo/business/trend

picture.image

  1. 需求: 表格中集成图表来更形象的表达数据
  2. 配置方式: indicator指标的cellType设置为'chart'
  3. 示例地址: https://visactor.io/vtable/demo/cell-type/chart

picture.image

  1. 需求: 透视组合图,分维度分指标综合看数据分布和趋势
  2. 配置方式: 使用表格类型PivotChart,indicator指标的cellType设置为'chart'
  3. 示例地址: https://visactor.io/vtable/demo/table-type/pivot-chart

picture.image

VTable 可以无缝集成VChart,将 VChart 的图表类型作为单元格的元素类型进行渲染,极大的提升 了表格的可视化表现力。同时,也为解决单页面多图表渲染的性能问题提供了新的解决方案。

欢迎交流

联系方式

项目官网:https://www.visactor.io/vtable

微信公众号(通过公众号菜单可以加入微信群和飞书群):

今夜无月,期待你点亮星空,感谢Star:

githubhttps://github.com/VisActor/VTable

picture.image

更多参考:

  1. VTable——不只是高性能的多维数据分析表格

  2. 火山引擎DataWind产品可视化能力揭秘

  3. VisActor——面向叙事的智能可视化解决方案

0
0
0
0
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论