Power BI技巧:时间维度动态折叠显示

文/Beau

数据分析爱好者,擅长PBI数据分析

本文灵感来自于星球球友的一个求助帖,他的诉求是可以在矩阵中对于日期类型的列标题进行动态折叠显示。

何为折叠呢?

简单的说就是当切片器选择日期长度超过一周时,则折叠为周维度显示,以此类推,可由日折叠为周,周折叠为月,月折叠为季,季折叠为年,换句话说就是日期由低纬度向高维度的汇总。

还是不明白?那就直接看效果图。

特别提醒:

为了便于大家理解,核心度量值代码已在源文件中做了详细注释哦。

闲话不多说,切入正题。

制作步骤

1、模型概览

模型是不是很简单?原生的三个表以及一个自建维度表,这里只建了三个度量值,所以就没有建专门的度量值表。

关键点:

1.这里的日期表包含列的颗粒度应尽可能的细,如下图所示,原因在于这样处理会便于后期日期维度表的创建。

2.对于日期表中的日名称和周名称的建立是有技巧的,日名称要包含年月,周名称要包含年,如下图所示。这样处理的目的在于我们能够方便的锁定目标数据。

2、构造辅助表

使用DAX新建表,如下

日期维度表 =
VAR y =
ADDCOLUMNS (
SELECTCOLUMNS(
SUMMARIZE( '日期', '日期'[年份名称], '日期'[年份序号], '日期'[日期] ),
"年份", '日期'[年份名称],
"维度明细", '日期'[年份名称],
"日期", '日期'[日期],
"orderby",0
),
"类别名称", "年"
)
VAR q =
ADDCOLUMNS (
SELECTCOLUMNS(
SUMMARIZE( '日期', '日期'[年份名称], '日期'[季度名称], '日期'[季度序号], '日期'[日期] ),
"年份", '日期'[年份名称],
"维度明细", '日期'[季度名称],
"日期", '日期'[日期],
"orderby",1
),
"类别名称", "季"
)
VAR m =
ADDCOLUMNS (
SELECTCOLUMNS(
SUMMARIZE( '日期', '日期'[年份名称], '日期'[月份名称], '日期'[月份序号], '日期'[日期] ),
"年份", '日期'[年份名称],
"维度明细", '日期'[月份名称],
"日期", '日期'[日期],
"orderby",2
),
"类别名称", "月"
)
VAR w =
ADDCOLUMNS (
SELECTCOLUMNS(
SUMMARIZE( '日期', '日期'[年份名称], '日期'[周名称], '日期'[周序号], '日期'[日期] ),
"年份", '日期'[年份名称],
"维度明细", '日期'[周名称],
"日期", '日期'[日期],
"orderby",3
),
"类别名称", "周"
)
VAR d =
ADDCOLUMNS (
SELECTCOLUMNS(
SUMMARIZE( '日期', '日期'[年份名称], '日期'[日名称], '日期'[日值], '日期'[日期] ),
"年份", '日期'[年份名称],
"维度明细", '日期'[日名称],
"日期", '日期'[日期],
"orderby",4
),
"类别名称", "日"
)
RETURN
UNION ( y, q, m, w, d )

核心思想在于利用原生日期表日期数据进行汇总合并。

这里要注意"orderby"列字段的创建规则,从日-年按照1-4的顺序创建,这样处理是为了在可视化组件中可以按照从左往右的顺序逐级折叠。

3.建立度量值

需要建立的具体度量值如下所示。

[salse]为基础度量值,这里不再赘述。

[test]度量值为测试度量值,它并没有直接参与到最终的数据呈现中,那我为何要建立它呢?各位小伙伴可不要小看它的作用,在我们日常写特别复杂的度量时,经常会被上下文环境弄晕头,那么这个度量就是解决此问题的利器,你可以将复杂度量中的步骤一一拆解写入到测试度量中,以验证每一步的结果是否符合预期,这将使你的工作事半功倍。

前方高能!核心度量即将登场!(代码比较长,请耐心看完。)

view =
--根据切片器取对应值
VAR select_d =
SELECTEDVALUE ( '日期'[日名称])
VAR select_w =
SELECTEDVALUE ( '日期'[周名称])
VAR select_m =
SELECTEDVALUE ( '日期'[月份名称] )
VAR select_q =
SELECTEDVALUE ( '日期'[季度名称] )
VAR select_y =
SELECTEDVALUE ( '日期'[年份名称] )
--对周界点进行判断
VAR tf_w =
MOD (SELECTEDVALUE ( '日期'[周值] ), 7 )
--判断当前上下文字段
VAR findd =
FIND ("D", SELECTEDVALUE ( '日期维度表'[维度明细] ),, 0 ) = 7
VAR findw =
FIND ("W", SELECTEDVALUE ( '日期维度表'[维度明细] ),, 0 ) = 5
VAR findm =
FIND ("M", SELECTEDVALUE ( '日期维度表'[维度明细] ),, 0 ) = 5
VAR findq =
FIND ("Q", SELECTEDVALUE ( '日期维度表'[维度明细] ),, 0 ) = 5
VAR findy =
FIND ("Y", SELECTEDVALUE ( '日期维度表'[维度明细] ),, 0 ) = 1
--求当前维度的下级维度最小值
VAR min_w_d =
CALCULATE( MIN ( '日期'[日名称] ), FILTER (ALL ( '日期' ), [周名称] = select_w) )
VAR min_m_w =
CALCULATE( MIN ( '日期'[周名称] ), FILTER (ALL ( '日期' ), [月份名称] = select_m) )
VAR min_q_m =
CALCULATE( MIN ( '日期'[月份名称] ), FILTER (ALL ( '日期' ), [季度名称] = select_q) )
VAR min_y_q =
CALCULATE( MIN ( '日期'[季度名称] ), FILTER (ALL ( '日期' ), [年份名称] = select_y) )
--返回结果
RETURN
SWITCH (
TRUE(),
findd
&& SELECTEDVALUE ( '日期维度表'[维度明细] ) <= select_d
&& SELECTEDVALUE ( '日期维度表'[维度明细] ) >= min_w_d
&& tf_w = 0 , CALCULATE ( [salse], TREATAS ( VALUES ( '日期维度表'[日期] ), '日期'[日期] ) ),
findw
&& SELECTEDVALUE ( '日期维度表'[维度明细] ) <= select_w
&&SELECTEDVALUE ( '日期维度表'[维度明细] )>= min_m_w
&& tf_w = 0 , CALCULATE ( [salse], TREATAS ( VALUES ( '日期维度表'[日期] ), '日期'[日期] ) ),
findw
&& SELECTEDVALUE ( '日期维度表'[维度明细] ) < select_w
&& SELECTEDVALUE ( '日期维度表'[维度明细] ) >= min_m_w
&& tf_w > 0, CALCULATE ( [salse], TREATAS ( VALUES ( '日期维度表'[日期] ), '日期'[日期] ) ),
findm
&& SELECTEDVALUE ( '日期维度表'[维度明细] ) < select_m
&& SELECTEDVALUE ( '日期维度表'[维度明细] ) >= min_q_m,
CALCULATE ( [salse], TREATAS ( VALUES ( '日期维度表'[日期] ), '日期'[日期] ) ),
findq
&& SELECTEDVALUE ( '日期维度表'[维度明细] ) < select_q
&& SELECTEDVALUE ( '日期维度表'[维度明细] ) >= min_y_q,
CALCULATE ( [salse], TREATAS ( VALUES ( '日期维度表'[日期] ), '日期'[日期] ) ),
findy
&& SELECTEDVALUE ( '日期维度表'[维度明细] ) < select_y,
CALCULATE ( [salse], TREATAS ( VALUES ( '日期维度表'[日期] ), '日期'[日期] ) )
)

不要被它的长度吓到,代码的长度和难度并不成正比哦,下面重点对此度量进行剖析:

为了便于理解,我将代码分成了四个部分:

  • 提取切片值

  • 对周界点判断

  • 对当前上下文字段进行判断

  • 求当前日期维度的下级维度最小值

  • 返回结果

讲之前啰嗦几句,类似这种关于日期的动态展示,关键点在于逻辑的梳理,不要一上来就开始写代码,最好先在纸上将每一步需要满足的条件画出来,逐步拆解,以便于对各日期界点清晰的判断,从而做到全盘考量,不会遗漏任何细节,才能最终得出想要的结果。碍于篇幅所限,就不展开讲了。

这里重点说一下度量的第二部分和第五部分,其余部分可以看源文件中的注释自行理解。

对周界点判断部分

这里采用取余函数进行判断,例如我们这里需要判断2011年3月23号是其所在周的周几,如果余数是0,则表示该天是星期日。可能细心的小伙伴会问,为何这里你只对周界点进行判断,那月度,季度,和年份界点呢?别着急,往下继续看。

返回结果部分

看着虽长,但是通过代码格式化后可看出基本范式是一样的,会写一种,写其他的都手到擒来。

发现了没?为何这里的日维度和周维度的变量和别的都不一样?看着有些复杂?

揭晓答案,根据触发点的不同,其实这里我对日期是做了两种折叠展现方式,哪两种呢?

1.触发点在当日,比如1月9日为周日,当切片器选择1月9日时,会立即折叠为周,缺点显而易见,1月9日的数据无法看到。

2.触发点在次日,同样用上述例子,当切片器选择1月9日时,并不会触发折叠,而是当切片器选择1月10日时,会触发折叠,优点也是显而易见的,这样每天的数据都能得到展现。

个人倾向于使用第二种方式,数据会得到完整展示,而且代码量也会少很多。当然小伙伴们可以视情况自由选择。

这里同样采取了无侵入的设计思路,尽量少的建立表关系,而通过TREATAS实现虚拟关联。

这一部分是整体代码的核心所在,也是难点所在,不要奢望一下能写出结果,要勇于试错,同时要注意细节,多一个或者少一个"="都会让你原地打转很久,所以一定要提前梳理清楚逻辑。

4.制作可视化

可视化没什么好说的,将建好的度量放入矩阵中,放入柱状图也是不错的选择。效果如下图所示:

 写在最后

最后让我们一起对本文重点内容进行回顾总结:

1.动手之前做导图,理清逻辑。

2.可建立测试度量,方便验证。

3.写代码注意细节,避免遗漏。

可能有的小伙伴会说这种需求在实际工作中遇到的很少,将不同的日期维度放在一起展示进行比较没有实际意义,但我想说的是这里面的思维和实现过程是值得学习借鉴的,掌握了它,相信大家以后对于处理大部分的日期维度问题将会更游刃有余。

本文是基于对单日期切片,如果想对一段日期实现此效果呢?是否可以加入一个日期维度切片,更便于比较?等等一系列的问题,我在这里抛砖引玉,更多的可能等待着小伙伴们去探索发现。还是那句话:能做的还有很多,这仅仅只是开始。

本人能力有限,文中如有不当之处,请各位小伙伴给予批评指正。

最后感谢@佐罗老师的数据源,感谢@焦鹏子老师的珠玉在前,给了我很大的启发。

农历新年将至,在这里祝各位小伙伴在新的一年中,

存远志,常读书,乐交友,惜四时!

全家福安,一生长乐!


(0)

相关推荐