计算列中使用 RANKX 函数的理解

昨天在QQ上有人问RANKX函数在计算列中使用时计算的过程。从计算返回的值来看它的计算过程难以理解。
问题原文:“rankx为all(table[value])生成的内层的行上下文,是不是只包含[value]列的值{1,2,3,6,7,8}并且逐行迭代?
那calculate(sum(table[value]))在用转换后的上下文沿着all(table[value])计值的时候,不是只有value本身作为条件吗?”
附原问题图片:
file
DAX中许多对于计算结果难以理解的问题,其实都是没搞清楚计算时的上下文是啥,这个问题也是上下文的问题。
可能是因为大多数人是跟着Excel一路走过来,在刚接触DAX的时候喜欢在计算列中学习DAX公式,这不是一个好习惯。在DAX模型的表中,虽然看起来与Excel工作表很像,它们也有许多相似的函数,实际上他们俩存在着很大的不同。按照Excel的逻辑来理解,会给DAX学习带来很多障碍。初学DAX,计算列中稀奇古怪的计算结果就是一个难以翻越的障碍。
回到这个问题,先了解RANKX函数的作用。(附上微软官方说明截图,尽管它的说明始终晦涩难懂)
file
再附上一张Power-BI极客网站《DAX圣经》中对RANKX函数的解析:
file
RANKX函数的参数有很5个,前两个是必选的。它是一个迭代器,作用是“为表(第一参)的每一行计算表达式(第二参)以生成所有可能的值(第三参)来进行排名”,至于排名的其他规则,可以通过后面三个参数来设置更多。
从上面两张截图看来,RANKX函数的主要难点是其使用了比较复杂的计算上下文。碰巧此问题又是在计算列中使用RANKX,计算列本身自带行上下文,这对计算过程的理解增加了不少难度。接下来我比照原问题模拟了一张类似的表,来解释它的计算过程。
先创建一个计算列:'表15'[RKX]=RANKX(ALL('表15'),CALCULATE(SUM('表15'[值])))
file
提示“检测到循环依赖关系”,原因是:创建的计算列RANKX公式的第一参引用了整张表,这整张表也包含了正在创建的'表15'[RKX]列(计算自己引用自己),因此报错。
接下来模仿原问题创建一个计算列:'表15'[RKX]=RANKX(ALL('表15'[值]),CALCULATE(SUM('表15'[值]))),得到了与原问题相同的计算结果。
file
将这个公式的第一参ALL改成VALUES('表15'[RKX]=RANKX(ALL('表15'[值]),CALCULATE(SUM('表15'[值]))) ),也会得到相同的结果。原因是:在计算列中没有筛选上下文,因此ALL('表15'[值])与VALUES('表15'[值])都会得到相同的表。
下面以'表15'[RKX]第一个格子内的来说明
第一参VALUES('表15'[值])得到的结果如下图:
file
RANKX是一个迭代器函数,它迭代第一参创建的表。要迭代的表达式就是第二参。第二参具体是怎么迭代的呢?引用Power-BI极客网站《DAX圣经》中对RANKX函数的解析来回答:第二参在第一参的行上下文中计值,同时考虑外部筛选上下文。
第二参数:CALCULATE(SUM('表15'[值]),我们知道calculate函数的特性,它会将行上下文转换成筛选上下文。那么计算列的第一个格子内它的上下文有哪些?由于文字描述它比较抽象,我们以图表方式说明
file
第二参的计算结果来看,它返回的是与第一参具有相同行数的一个列表,解决了第二个参数的计算接下来就轻松多了,这个过程再一次证实了DAX公式与Excel的不同,它并不是所见即所得的单元格之间的引用计算。
接下来分析RANKX第三参数,虽然我们在公式中省略了第三参,但它依然对计算结果很重要。
根据官方说明,忽略第三参时,将改用当前行的第二参表达式的值。 这句话说明了两点:1、在忽略第三参时,第二参和第三参使用的计算表达式是一样的。2、虽然它们表达式一样,但是第二参和第三参使用的上下文不一样。第三参返回的是一个值(value),它使用的上下文正是上图中“行上下文2”和“行上下文3”。根据calculate转换行上下文,在对'表15'进行筛选计算得到第三参计算结果=1。这就回到了排序的本质:计算一个值(第三参)在一列值(第二参)中所处的序位
在第四,五参省略的情况下默认降序且稀疏排列。第四,第五参没有上下文参与,仅仅是排序的方式选择。
整个RANKX计算结果:1在{1,2,0,0,0,0,0}中从大到小降序稀疏排在第2,即'表15'[RKX]列中第一个值的由来。
如果此处将公式改成从小到大升序稀疏排列,公式=RANKX(VALUES('表15'[值]),CALCULATE(SUM('表15'[值])),CALCULATE(SUM('表15'[值])),ASC),则返回值为6;
如果此处将公式改成从小到大升序稠密排列,=RANKX(VALUES('表15'[值]),CALCULATE(SUM('表15'[值])),CALCULATE(SUM('表15'[值])),ASC,Dense),形如1在{1,2,0}中升序稠密排在第二位。
要想在计算列中得类似Excel表里面排序的表,则应该使用公式:RANKX(ALL('表15'[类],'表15'[值]),CALCULATE(SUM('表15'[值]))),计算逻辑依然是上面解释的步骤。
RANKX函数难点在于对多个上下文的理解,有了行上下文基础再理解它会变得容易一些。