如何 PowerBI 中构建任意复杂桑基图

PowerBI战友联盟

共 4693字,需浏览 10分钟

 ·

2020-11-08 22:29

有小伙伴问如何在 PowerBI 中实现复杂的桑基图 (Sankey diagram),本文来做介绍。

桑基图,可以相当复杂,例如:

桑基图,可以非常直观地反应出:

  • 流向

  • 流量

  • 节点

这可以应用到很多场景。

本文先来介绍如何在 PowerBI 中实现复杂桑基图的通用原理,后续再做复杂案例的介绍。

二阶段桑基图

先看一个案例,用 PowerBI 实现如下:

这可以清楚地看到我们如何将分组汇总的数据逐步转化到可视化,它的过程是:

  • 纵向分组汇总(俗称:一维透视表)

  • 桑基图

这个过程实现起来非常简单。

首先,从 PowerBI 视觉对象库(AppSource)导入桑基图,如下:

找到桑基图,如下:

如果您无法找到桑基图,请严格按照上述操作顺序进行即可。

桑基图只需要三个字段就可以构建完成,如下:

例如,可以将产品[类别]作为源,将地理位置[地区]作为目标以及度量值[销售额]作为权重来进行设置,如下:

构建完毕。

称 A->B 类型的桑基图为 二阶桑基图

在很多应用中,我们需要多阶桑基图,例如三阶桑基图如下:

甚至四阶桑基图如下:

这些案例都是用同一个桑基图视觉对象在 PowerBI 中实现,不难推而广之,这可以实现任意阶段桑基图(如果不构成性能问题的话)。这里是否存在通用的构建逻辑呢,答案是肯定的。

桑基图通用构建原理

从多阶(三阶或四阶)桑基图可以观察到这样的规律:

  • 桑基图只能设置来源,去向以及权重大小;

  • 同一元素序列可能同时作为来源和去向;

  • 权重只发生在有意义的连接上。

在清楚了这三条桑基图铁律后,就可以构建复杂的桑基图了。我们的思考如下:

  • 有且仅有一个字段作为来源,涵盖所有来源

  • 有且仅有一个字段作为去向,涵盖所有去向

  • 有且仅有一个度量值计算权重,且仅仅发生在有意义的连接上

有了这样的思考,我们就可以用 PowerBI DAX 来做实现了。

构建三阶桑基图

为了构建如下的桑基图,如下:

根据已经分析得到的规律,会引导我们做进一步思考:

  • 产品[类别]地理位置[地区]都需要被纳入某个字段作为桑基图的来源字段使用;

  • 地理位置[地区]客户[行业]都需要被纳入某个字段作为桑基图的目标字段使用。

于是用 PowerBI DAX 构建,如下:

// 第一步,新建一个计算表,用来构建三阶桑基图的来源字段

SanKey_Source_3 =
UNION(
SELECTCOLUMNS( VALUES( Geo[Region] ) , "SourceName" , [Region] ),
SELECTCOLUMNS( VALUES( 'Product'[Category] ) , "SourceName" , [Category] )
)

// 第二步,新建一个计算表,用来构建三阶桑基图的去向字段

SanKey_Dest_3 =
UNION(
SELECTCOLUMNS( VALUES( Geo[Region] ) , "DestName" , [Region] ),
SELECTCOLUMNS( VALUES( Customer[Industry]) , "DestName" , [Industry] )
)

// 第三步,构建仅仅在有意义的连接上的权重度量值,新建一个度量值

Sankey3.KPI =
VAR _source = SELECTEDVALUE( SanKey_Source_3[SourceName] )
VAR _dest = SELECTEDVALUE( SanKey_Dest_3[DestName] )
VAR _source_as_product = TREATAS( { _source } , 'Product'[Category] )
VAR _source_as_geo = TREATAS( { _source } , 'Geo'[Region] )
VAR _dest_as_geo = TREATAS( { _dest } , 'Geo'[Region] )
VAR _dest_as_customer = TREATAS( { _dest } , 'Customer'[Industry] )
RETURN SWITCH( TRUE(),
_source IN VALUES( 'Product'[Category] ) && _dest IN VALUES( Geo[Region] ) ,
CALCULATE( [KPI] , _source_as_product , _dest_as_geo ) ,
_source IN VALUES( 'Geo'[Region] ) && _dest IN VALUES( Customer[Industry] ) ,
CALCULATE( [KPI] , _source_as_geo , _dest_as_customer ) ,
BLANK()
)

这里的核心就在度量值的逻辑,它具有如下特点:

  • 用 _source获取来源

  • 用 _dest获取去向

  • 再用 TREATAS 构建可能的动态挂载器

  • 再用 SWITCH TRUE 的结构来判断连接是否有意义

    • 如果有意义,则用事先准备好的动态挂载器去计算度量值

    • 如果没有意义,则返回空,图表则不会在无意义的连接上形成计算

动态挂载器,并不出现在 PowerBI 的官方说明文档中,由于这里的逻辑是一种动态挂载的非侵入式设计(可参考BI佐罗此前的文章),这样的结构在编写中通常被名词化后形象地称为:动态挂载器或钩子。

如果这里的 DAX 公式没有特别理解,再来看四阶桑基图的构建过程,再次体会下。

构建四阶桑基图

与构建三阶桑基图的思考过程完全一样,这里构建如下的桑基图:

这需要在来源字段和去向字段装入更多的可能,并在度量值的构建中做更多的判断,用 PowerBI DAX 实现如下:

// 第一步,构建来源
SanKey_Source_4 =
UNION(
SELECTCOLUMNS( VALUES( Geo[Region] ) , "SourceName" , [Region] ),
SELECTCOLUMNS( VALUES( 'Product'[Category] ) , "SourceName" , [Category] ),
SELECTCOLUMNS( VALUES( 'Customer'[Industry] ) , "SourceName" , [Industry] )
)

// 第二步,构建去向
SanKey_Dest_4 =
UNION(
SELECTCOLUMNS( VALUES( Geo[Region] ) , "DestName" , [Region] ),
SELECTCOLUMNS( VALUES( Customer[Industry]) , "DestName" , [Industry] ),
SELECTCOLUMNS( VALUES( Customer[Occupation]) , "DestName" , [Occupation] )
)

// 第三步,构建度量值
Sankey4.KPI =
VAR _source = SELECTEDVALUE( SanKey_Source_4[SourceName] )
VAR _dest = SELECTEDVALUE( SanKey_Dest_4[DestName] )
VAR _source_as_product = TREATAS( { _source } , 'Product'[Category] )
VAR _source_as_geo = TREATAS( { _source } , 'Geo'[Region] )
VAR _source_as_customer_industry = TREATAS( { _source } , 'Customer'[Industry] )
VAR _dest_as_geo = TREATAS( { _dest } , 'Geo'[Region] )
VAR _dest_as_customer_industry = TREATAS( { _dest } , 'Customer'[Industry] )
VAR _dest_as_customer_occupation = TREATAS( { _dest } , 'Customer'[Occupation] )

RETURN SWITCH( TRUE(),
_source IN VALUES( 'Product'[Category] ) && _dest IN VALUES( Geo[Region] ) ,
CALCULATE( [KPI] , _source_as_product , _dest_as_geo ) ,
_source IN VALUES( 'Geo'[Region] ) && _dest IN VALUES( Customer[Industry] ) ,
CALCULATE( [KPI] , _source_as_geo , _dest_as_customer_industry ) ,
_source IN VALUES( 'Customer'[Industry] ) && _dest IN VALUES( Customer[Occupation] ) ,
CALCULATE( [KPI] , _source_as_customer_industry , _dest_as_customer_occupation ) ,
BLANK()
)

构建完成。类似地,可以用完全一样的套路构建多阶段桑基图。

桑基图的意义

如果用透视表来表达三阶桑基图,会是这样:

上面的透视表和下面的桑基图是完全等价的,但桑基图给出的可视化效果可以让人立刻直觉式思考得到想要的信息。

总结

本文给出了构建桑基图的一般方法和套路,结合 PowerBI DAX 可以构建任意阶桑基图。

在本文给出的案例中,还有以下特点需要注意:

  • 桑基图的每层节点的总流量是相等的

  • 不存在跨层流动

  • 不存在回流

后续,我们将做更一般化的讨论,将本文原理扩展到:

  • 流量不一定相当的场景

  • 存在跨层流动的场景

  • 存在回流的场景

那就更具有一般化了。

桑基图,可以用来构建很多业务场景的可视化,我们也将在后续文章给出案例。

在订阅了BI佐罗讲授的《BI进行时》课程区,除了可以下载本文案例,还可以观看视频讲解。

让数据真正成为你的力量

Create value through simple and easy with fun by PowerBI

Excel BI | DAX Pro | DAX 权威指南 | 线下VIP学习

扫码与PBI精英一起学习,验证码:data2020

PowerBI MVP 带你正确而高效地学习 PowerBI
点击“阅读原文”,即刻开始

浏览 362
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报