这个DAX思路,帮你轻松解决连续数据计算

本文是PowerBI星球嘉宾天行的投稿作品,使用DAX巧妙计算连续数据的问题,下面直接进入他的精彩分享。

-我是分割线-

这个DAX思路,帮你轻松解决连续数据计算

作者:天行

工作中经常会遇到判断连续的问题,比如连续打卡的天数、连续购买的顾客,或者连续正增长的销售单位等等。

在普通的编程语言里面是很简单的事情,一个循环加一个判断就可以解决。

但在DAX里面,确实有点难度:

首先,DAX里面没有循环语法;其次,DAX里面调试是个问题,特别是循环结构的调试,对了,DAX里面叫做迭代器(iterator)。我们经常会用到一个词-”脑补“,这个过程真是谁用谁知道,非常需要六个核桃啊!

而判断连续的问题需要至少两重循环嵌套,还是有点挑战性的。

好在,佐罗老师在《PowerBI DAX处理复杂业务到性能优化1000倍》一文将问题抽象到处理【Index】和【Flag】两列数据的程度,并提供了解决思路和实际算法,同时还将整个原理和优化算法都解析得很清楚。

非常推荐大家去阅读学习,至少我个人是获益匪浅的,在这给佐罗老师说声谢谢了!

问题是解决了,但我一直在想还有没有优化的可能,因为DAX的数据存储原理是列存储方式,即对列的聚合计算进行优化。

也就是说,如果能把算法从普通的循环判断变换成对某一列的汇总(sum)、计数(Count)或者最大、小值(Max、Min)上,才应该是最优化的DAX算法。

【寻找规律】

从上图可以发现:

1、【Index】是步进值为1的整数

2、【Flag】有效位是1,无效位是空

稍做分析,可以得出结论:

【Index】列实际是全部按1的累加,如果将【Flag】列也累加,那么连续有效行的累加值也会呈现和【Index】一样的规律。

在Excel里面很容易可以做个验证:

增加两列:

1、【累计Flag】

2、【累计Flag-Index】

相信一眼就可以发现规律:连续有效行的【累计Flag-Index】值是一样的!

有了这个规律,对连续区间求最大值、最小值、计数,延伸至进一步对连续规律的分析就有办法了。

下面就以最大连续天数为目标,尝试解决DAX编码的问题。

【编码思路】

1、首先写出【累计Flag】的度量值

2、其次写出【累计Flag-Index】的度量值

3、构建一个计算表,对【累计Flag-Index】进行计数

4、取【计数】列最大值

这里,简单说一句关于DAX调试的经验,就是将复杂问题拆分成一个个零件,生成零件后再进行封装。

其中,借用计算表进行过程的调试,一定程度上可以减轻脑补的压力。

【度量值封装】

基本上将写好的度量值进行组合封装没有什么难度,但在这个案例中的三个迭代器的用法还是值得一说。

1、SUMX

在【累计Flag】度量值中是用的Calculate+Filter的传统组合

而在最终在ADDCOLUMNS的应用中,用到的是行上下文,所以应该使用SUMX迭代器函数来替代

相应的写法也有所不同,即通过IF函数对【Index】值进行判断后,取小于等于当前【Index】值的行进行迭代求和。

2、COUNTX

同上理由,生成聚合表时,不能使用常规的COUNT函数进行【累计Flag-Index】值的计数,只能使用COUNTX函数。

那么问题来了,使用常用的聚合函数SUMMARIZE和SUMMARIZECOLUMNS,居然不能正常进行计数!

通过构建临时计算表,发现所有的【累计Flag-Index】计数都是一样的。

难道这是DAX的Bug吗?

通过查找sqlbi的大神文章,

https://www.sqlbi.com/articles/nested-grouping-using-groupby-vs-summarize/

大致明白原理和解决方法:

基于SUMMARIZE和SUMMARIZECOLUMNS函数的设计原理,不能对SUMMARIZE动态生成的表再进行聚合计算。

解决方法就是使用GROUPBY函数,通过CURRENTGROUP关键字代表动态生成的表,作为COUNTX函数的第一个表参数。

3、MAXX

此处同样应该使用迭代器函数处理行上下文,而不能使用MAX函数。

至此,度量值封装完成,看看最后的成果。

【性能测试】

从原理上分析,本算法只用到了一个累计求和、一个聚合计数和一个最大值,都是DAX非常擅长的基本功,而且随着数据量的增加,计算量应该只是线性增加,而不会出现指数级增长。

将佐罗老师的普通算法和优化算法稍作修改成两个度量值

利用Power BI5月份版本中新增的性能分析器进行测试

500条和1000条数据测试结果如下:

结果证实了推断,新算法效率更高,而且数据量越大,优势越明显。

【收获小结】

1、加深了对DAX原理的理解:尽量利用DAX列存储的特点,将问题抽象成对列的聚合操作,能大幅提升性能。

2、新掌握了一个聚合函数GROUPBY,针对聚合表中的迭代操作很管用。不过,通过查询相关资料得知,三个聚合函数的性能比较是SUMMRIZECOLUMNS>SUMMRIZE>GROUPBY,基本上是1:2:3(消耗时间)的差别,切莫杀鸡用牛刀。

3、进一步熟悉迭代器(X)函数的用法和用处:SUM的本质是SUMX,这是使用聚合函数的核心概念。

开心!收工!

【后记】

文章写完后,很唐突地请佐罗老师把把关。很快,他就给出了更优化的算法,从DAX的角度来说,更优雅更高效,经过测试,可以再提升30%,真是山外有人,人外有人,学无止境啊!

请大家欣赏大神的作品。

佐罗的代码优化了三个地方:

1、一次性得出【累计Flag】与【Index】的差额,减少了一个SUMX的损耗;

2、在SUMX中使用乘法取代IF函数,减少了公式引擎判断的损耗;

3、用SUMMARIZE+COUNTROWS+FILTER的组合替代了GOUPBY+COUNTX+CURRENTGROUP的组合,大幅减少了损耗。

性能测试结果:

感谢佐罗老师的指点,知道自己又进步了的感觉真好!


结尾的话

感谢天行的分享,从寻找规律出发,逐步深入,帮我们在判断连续性的问题上提供了解决方案。

(0)

相关推荐