从数据到信息
从信息到洞察

理解 RANKX

RANKX 是计算排名的专用函数,它可以根据你指定的计算逻辑,返回当前成员在整个列表中的排名,RANKX 是非常灵活且强大的迭代函数,它的计值过程需要你仔细阅读和理解。如果你只需要根据模型已有的值计算排名,可以考虑使用它的简化版 RANK.EQ。

RANKX

RANKX(<table>, <expression>, [ <value> ], [ <order> ], [ <ties> ])

RANKX 为<table>的每一行计值表达式<expression>并得到一个值列表,<value>在当前筛选上下文中计值,将得到的结果与列表中的值进行比较,根据排名规则<order>和<ties>的设置,返回最终排名。

参数 属性 描述
Table 表或返回表的表达式
Expression 沿着 Table 每行计值的表达式
Value 可选 需要返回排名的 DAX 表达式,返回标量值。当<value>省略时,用<expression>代替
Order 可选 排名依据。0 或 False 代表降序;1 或 True 代表升序,默认使用降序
Ties 可选 处理相同排名时的依据,skip 代表稀疏排名,下一名的排序等于之前所有排序的数量+1;dense 代表稠密排名,只累加排序,不考虑数量。默认使用 skip

函数解析

<value>参数在根据<table>每行计值的<expression>返回的值中进行排序。如果省略<value>,则使用<expression>在调用 RANKX计值上下文中计算的结果用作排序依据。

<expression>和<value>在不同的上下文中计值,<expression>参数在<table>的行上下文中计值,同时考虑外部筛选上下文。<value>参数在调用 RANKX 的上下文环境中计值,可能包含行上下文(例如计算列)也可能只有筛选上下文(例如度量值)。可以使用 CALCULATE 将行上下文转换为筛选上下文(但请注意,第三参数可能不存在行上下文)。不要忘记,对度量值的引用总是隐式地执行上下文转换

排序参数<order>的值为 0 或 False(默认)时返回降序结果,为 1 或 True 时返回升序结果。字符串参数<ties>可以使用 DENSE 或 SKIP 来处理相同值的排名问题,DENSE(稠密排名)在平局后返回下一个名次,SKIP(稀疏排名)返回跳过所有相同值后的名次。例如,有四个值的排名都是 5,那么下一个值的排名可以返回 9(稀疏排名),也可以返回 6(稠密排名)。

如果 Value 为负数,排名可能会发生明显的变化,原因是空值在计算排名时自动转换为 0,原因是数据类型和运算符一文中介绍过的隐式转换。

示例

例如,你可以定义下面的度量值对产品品牌进行排序:

[Rank by Brand A] := RANKX ( ALL ( Product[Brand] ), [Sales Amount] )

结果是每个产品品牌的销售额排名,但我们发现包含了所有产品的总计行也返回了排名。你可以通过使用 HASONEVALUE 测试是否只选择了一个品牌来避免这种情况,这两个度量值的结果在图 8-1 中可见。

[Rank by Brand B] :=
IF (
    HASONEVALUE ( Product[Brand] ),
    RANKX ( ALL ( Product[Brand] ), [Sales Amount] )
)

图 8-1 Rank by Brand B 隐藏了总计行的排名

这里重要的是要考虑将哪个表参数<table>传递给 RANKX 以获得所需的结果。在上面的公式中,需要使用 ALL (Product[Brand]),因为你希望获得每个品牌的排名。如果将颜色放在行上,而不是品牌,则会得到如图 8-2 所示的奇怪结果。

图 8-2 度量值根据产品型号对销售额进行排名,但报告根据产品颜色对数据进行了划分

Rank by Brand A 总是返回 1,因为对于每种颜色,可能有一个或多个品牌,RANKX 计值第二参数[Sales Amount]的环境是 ALL ( Product[Brand] )的每一行和当前外部筛选上下文,结果受到品牌和颜色的共同约束,第三参数默认仍使用[Sales Amount],这一次[Sales Amount]在调用 RANKX 的外部上下文中计值,得到的是当前颜色的总销售额。这个值更大,总是大于或等于当前颜色下各个品牌的销售额(也就是第二参数的计值结果)。为什么 Rank by Brand B 的排名只有几行是 1 呢?原因是某个颜色的产品只属于一个品牌时(如 Azure 和 Transparent colors),才满足 HASONEVALUE 条件,否则该颜色的品牌不止一个,IF 条件判断使得结果为空。

只统计可见排名

按 Rank by Brand A 计算的排名是所有品牌的绝对排名。如果你在筛选器中只保留几个品牌,排名将总是考虑那些不可见的。例如,在图 8-3 中,你可以看到,如果你取消选择前三名的品牌,那么品牌排名将从第四名开始。

图 8-3 第一个品牌的排名是第四名,因为这里的排名始终按所有品牌计算

如果你只想统计可见品牌的排名,需要使用 ALLSELECTED 代替 ALL,正如你在下面的度量值中看到的那样,它产生了图 8-4 所示的结果。

图 8-4 第一个品牌排名第一,因为这一次排名只考虑可见的品牌

我们在 CALCULATE 调节器章节详细的介绍了 ALLSELECTED 函数,在这里,ALLSELECTED 移除了数据透视表的行和列成员引入的筛选上下文,保留了数据透视表的切片器和筛选器定义的上下文。
[Rank by Brand C] :=
IF (
    HASONEVALUE ( Product[Brand] ),
    RANKX ( ALLSELECTED ( Product[Brand] ), [Sales Amount] )
)

RANKX 中的陷阱

在度量值中使用 RANKX 可能会犯以下这些常见的错误。第一个陷阱是 RANKX 的第一参数通常对列或表使用 ALL 函数。当你使用表时,你可能会忘记使用 ALL,这种情况在指定单个列时不会发生,因为参数必须是表形式,如果简单的指定一个列名作为第一参数,你会收到错误提示。

忘记使用 ALL

例如,考虑以下按产品类别计算排名的度量值。错误写法将产品类别表指定为 RANKX 的第一参数,而正确的写法是使用 ALL 函数。这两个度量值的结果在图 8-5 中都可以看到。

[Wrong Rank by Category] :=
IF (
    HASONEVALUE ( 'Product Category'[ProductCategoryKey] ),
    RANKX ( 'Product Category', [Sales Amount] )
)
[Rank by Category] :=
IF (
    HASONEVALUE ( 'Product Category'[ProductCategoryKey] ),
    RANKX ( ALL ( 'Product Category' ), [Sales Amount] )
)

错误写法的结果始终为 1

忘记使用 CALCULATE

另一个常见的陷阱是使用 DAX 聚合行级数据时,没有将表达式嵌入 CALCULATE 函数里。在前面的例子中,我们一直使用销售额度量值作为计算排名的表达式。如果将度量值换成 SUM 这样的聚合函数,需要意识到表达式将在 RANKX 第一参数的每行计值。在这个迭代过程中,行上下文不会自动转换为筛选上下文,除非通过 CALCULATE 执行上下文转换,这是调用已有度量值时执行的隐式操作。因此,对于每一行,筛选上下文(即计算 RANKX 的单元格中现有的筛选器)总是相同的,以这种方式计算,所有类别的排名都是 1。正确的公式只需要在表达式外层调用 CALCULATE 函数,该函数为 RANKX 迭代的表的每一行执行上下文转换。你可以在图 8-6 中看到错误和正确公式的结果。

[Wrong Rank by Quantity] :=
IF (
    HASONEVALUE ( 'Product Category'[ProductCategoryKey] ),
    RANKX ( ALL ( 'Product Category' ), SUM ( Sales[Quantity] ) )
)
[Rank by Quantity] :=
IF (
    HASONEVALUE ( 'Product Category'[ProductCategoryKey] ),
    RANKX ( ALL ( 'Product Category' ), CALCULATE ( SUM ( Sales[Quantity] ) ) )
)

图 8-6 错误写法每行总是返回 1

 

使用 RANKX 的第三参数

在前面的示例中,你已经看到只用两个参数的 RANKX。默认情况下,第三参数使用第二参数,无论你是按 DAX 度量值还是聚合表达式进行排名,通常这都是正确的选择。但是,如果要根据表本身某一列的值对表执行排序,则可能需要在第三参数中指定不同的表达式。

本文隐藏内容查看价格为3G币,请先
注:加入VIP会员可享受全站权益,性价比更高。单独购买的内容长期有效,不受时间限制。

理解 RANKX 计值流

在理解上下文转换一文的案例二中,我使用了一个 RANKX 计值的示例,通过这个案例可以加深你对 RANKX 计值流的理解。

虽然 RANKX 的计值过程比较繁琐,但这是个经过优化的函数,实际执行时的效率非常高

42
说点什么

1000
 
鼓掌微笑开心憧憬爱你色并不觉得吃瓜doge二哈喵喵思考笑哭捂脸悲伤大哭抓狂汗偷笑打脸捂眼黑线问号晕拜拜闭嘴衰咒骂ok作揖
11 评论数
31 被回复的评论
10 订阅评论的人数
 
查看最近回复
查看最热评论
  订阅本文评论  
最新 最旧 得票最多
提醒
成员
风自由

老师,为什么我这样写第二个参数有问题啊?如果是Sales表中的一个度量值就不会报错,其他任何字段都会报这个错。是什么原因呢?

QQ截图20200901132402.png
成员
166****3052

老师,您好,RANKX ( ALL ( Product[Brand] ), [Sales Amount] ) 中all 是表函数返回品牌所有行的意思还是清楚外部筛选的意思呢?

成员
powersum

请问老师:假设有公司,首先计算每个销售人员每日销售额的排名,这个我用公式:每日销售额排名:=rankx(‘all(表[姓名]),calculate(max(‘表[销售额]))),然后选择合适的计算环境即可。(行区域:姓名,列区域:日期)
但是我又对日期进行了聚合,建立了一个计算列,月=month(‘表[日期]),然后想统计出每个销售人员每月中每日销售额排名的最大值,这个该如何设计公式呢,这时的计算环境是行区域:姓名,列区域:月

捕获2.PNG
捕获1.PNG
成员
Wander

如何对所有品牌中某几个特定的品牌进行排名

成员
lyliuyouyang

学习了这个函数以后,我自己尝试了在rankx的第二参数去调用虚拟表中去添加列,发现调用不了非限定性列名,然后我自己尝试了图中的写法,先定义度量值去求每个劳务队的末次计价,再通过末次计价计算排名。不定义度量值的话,求末次计价和排名里也要计算末次计价要写2遍sumx,非常繁琐,结果倒是得到了,不过我的疑问是,是否在我这种需求中,如果不定义度量值,写2遍sumx是不是无法避免?写两次也意味着计算2次,是不是会有更简练的方法?因为我的写法里rankx相当于又去重新把外面的addcolumns构造的虚拟表又构造了一遍(第一参数all+列名返回整个劳务队的表,在逐行计算sumx的末次计价,最后再用外层的当前行值去排序,其实前两步不就是外层addcolumns+values的作用吗),想起来是重复性的。
还请高飞老师解答!

批注 2020-05-13 210934.jpg
批注 2020-05-13 211500.jpg
成员
gh850020586

老师你好,我在使用RANKX时,想求绝对排序,但是我按照绝对排序的写法完成后。实际交互时,却是展示的动态排名。
我的表结构只有一张表。

Snipaste_2020-04-22_18-24-48.png
成员
肖肖肖二

请问老师,为什么子类别有负值参与排名时,子类别里的排名出错;函数:IF( ‘个人数据'[是否子类],RANKX(ALL(‘个人数据'[子类别]),’个人数据'[以下项目的总和:利润]),RANKX(ALL(‘个人数据'[类别]),’个人数据'[以下项目的总和:利润]))

企业微信截图_15872182396382.png
成员
powersum

请问老师:是否可以实现对同一行中不同列的排序呢?譬如有a,b,c三列,怎样可以设计一个计算列,返回结果是每一行中a列在a、b、c三列中的排名呢?

成员
lyliuyouyang

DEFINE VAR table1 = ADDCOLUMNS ( ‘人员考核’, “排名”, RANKX ( ‘人员考核’, ‘人员考核'[年龄], ‘人员考核'[年龄], DESC,… 阅读更多 »

成员
行者孙

[Rank by Brand B] :=
IF (
HASONEVALUE ( Product[Brand] ),
RANKX ( ALL ( Product[Brand] ), [Sales Amount] )
)HASONEVALUE这个函数怎么理解,字面意思是当指定列中只有一个值时,返回 true,那么总计行中列中也只有一个值呀,按理应该返回TURE呀,为什么是空值?

成员
悟空

你好,想问下,在列中能否使用rankx?

DAX 圣经

导读

初识 DAX

DAX 基础知识

DAX 原理

DAX 高级原理

基础函数类型

迭代函数

CALCULATE 函数

CALCULATE 调节器

基础表函数

条件判断函数

查找匹配函数

时间智能函数

统计类函数

投影函数

分组/连接函数

集合函数

其他函数