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

理解 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 的计值过程比较繁琐,但这是个经过优化的函数,实际执行时的效率非常高

88
说点什么

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

老师,《DAX模式设计》16章216页Rank in Category是产品表中的计算列,RANKX在计算第一个参数的时候应该还没发生行上下文转换,但计算结果似乎验证了ALLEXCEPT中的Product[Category]是被筛选上下文给筛选了,望老师指教,谢谢~~

allexcept.jpg
成员
chen1997

高老师请教一个排名问题,公式筛选器和矩阵如图所示,我想问一下就是这两个公式的完整计值流是这么样的,我感觉第一个公式也是按照产品类别排名的,可是的结果是按住所有产品排名,想问一下我的计值流是哪里出了问题
我的理解,比如按住视觉对象第一个举例,初始上下文有产品类别的手机配件,产品名称的充电宝,对于第一个公式
类别中产品绝对排名 = RANKX(ALL(‘产品'[产品名称]),[销售额]) 中
ALL(‘产品'[产品名称])作为表函数忽略外部上下文,返回一个不重复单列的表返回去重完整的’产品'[产品名称],
然后第二参数[销售额]的上下文;为ALL(‘产品'[产品名称])的行上下文,和产品类别为手机配件
与外部产品名称的充电宝。
在计算第二参数[销售额],应该按照ALL(‘产品'[产品名称])逐行计算,这是每行的’产品'[产品名称]发生上下文转换会覆盖同列的外部上下文(产品名称充电宝),
最终在计算第二参数时上下文为产品类别为手机配件与’产品'[产品名称],并且逐行计算,生成一个单表
然后第二参数[销售额]在外部上下文(产品类别为手机配件,产品名称的充电宝)计算
最后前面的单表比较在排名
因为产品类别即为手机配件,产品名称又为电脑设备的不存在,所以最终排名为只排相应类别的,可结果却是所有产品的排名

0275b07b08c0d544c2e42be262f8f67.png
a9515dac4589e19e79e951c4446d443.png
成员
jb870610

高老师,这几张图片中的红框中的HASONEVALUE的参数都不对吧?

2.png
1.png
成员
jb870610

高老师,红框中应该是产品品牌吧?

1.png
游客
839838408

老师,根据例子“理解 RANKX 计值流”,关于更准确的描述是否应该是“参数在的行上下文中计值,同时考虑外部筛选/行上下文。”?

成员
今晚的月色很美

老师,我有一个疑问,如果我要新建计算列,在计算列中表示当前产品在【当前季度下】的【售价排名】,应该怎么做呢?

下面是我创建这个表的时候用到的数据

商品表 =
DATATABLE(“编号”,INTEGER,
“商品名称”,STRING,
“成本”,CURRENCY,
“售价”,CURRENCY,
“品类”,STRING,
“库存”,INTEGER,
“单位”,STRING,
“季度”,INTEGER,
{
{001,”苹果”,1.25,4.50,”水果”,500,”斤”,1},
{002,”丝瓜”,0.65,2.50,”蔬菜”,300,”公斤”,1},
{003,”车厘子”,20.00,48.00,”水果”,500,”斤”,4},
{004,”茄子”,2.50,5.20,”蔬菜”,400,”公斤”,1},
{005,”鸡蛋”,0.40,0.80,”生禽”,500,”个”,2},
{006,”凤梨”,5.00,10.0,”水果”,200,”个”,2},
{007,”糖果”,0.50,1.00,”零食”,300,”斤”,3}
}
)

QQ图片20230320175135.png
成员
做一名学霸

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

老师好, RANKX 的第二参数表达式是个度量值,第二参数在行上下文和外部筛选上下文环境中计值,由于是度量值,将行上下文进行转换为筛选上下文,所以现在第二参数计值的筛选上下文应该是外部的筛选上下文(行标签)和由行上下文转换后的筛选上下文。
但下图的结果由于行标签(一个是Brand、一个是COLOR)的不一样导致显示的结果不一样,是否存在筛选器覆盖问题——即当
RANX内部的行上下文转换为的筛选上下文,与外部筛选器一致时(比如图1,两个筛选上下文都是Brand),RANX的第二参数的计值就不受外部筛选上下文的影响?

123.jpg
Snipaste_2023-02-01_00-49-51.jpg
成员
卫东

老师,研究了下,感觉这个图片应该更能说明RANKX的计算机制吧,建议多用图片或者GIF说明原理

f78cf5ac78c4df2c582e15b8b0f4e1d.jpg
游客
zenchenzxc

老师好,请问一下我行选入两层排序,升序为什么不能按照降序一样在组内从1开始分别排序呢?当我把最外层去掉就对了,加上外层分组就错了

游客
oz

老师好,如下一张表:
类别 销量
a 1
b 2
a 10
b 20
排名列=RANKX(ALL(‘表'[类别]),CALCULATE(SUM(‘表'[销量]))),此时排名如下:
类别 销量 排名列
a 1 1
b 2 1
a 10 1
b 20 1
我的疑问是:为什么排序列显示的都是1?我的理解是 ->以第一行(类别=a,销量=1)为例。外层行上下文为:类别=a,销量=1,RANKX为第一参数的每一行创建内层行上下文(类别={a,b},共2个值),由于内层迭代替换外呈同名的“类别”列,此时内层和外层的交互结果为{a:1,b:1}的筛选上下文,接着RANKX中省略的第三参数拿着这个转换而来的筛选上下文去筛选原表,得到的结果是(a=1,空),最后RANKX再次拿外部的行上下文(类别=a,销量=1)求(a=1,空)次序值,得到排名列=1,。老师,请问以上计值流描述正确吗?若有哪个地方漏掉或者错误,麻烦老师帮忙指正出来,感谢万分!

成员
暮色

大佬 如果将 RANKX 用作度量值,则第三参数没有行上下文。这句话是我图中说的那个意思么,rankx创建的行上下文只能被第二参数应用???

rankx.png
成员
陈欣

老师您好,两个商品的销售额出现相同的情况时,怎么让排名单调递增,而不是这俩商品排名相同?

成员
성난 새들

高老师你好,我用Date表去排序销售额,只有列名为Date[Date]排序才正确,这个怎么也没搞明白,谢谢。

微信截图_20210112141601.png
成员
qingkou55

高总,咨询一下在“函数解析”的“示例”对应图8.2部分您提到“为什么 Rank by Brand B 的排名只有几行是 1 呢?原因是某个颜色的产品只属于一个品牌时(如 Azure 和 Transparent colors),才满足 HASONEVALUE 条件,否则该颜色的品牌不止一个,IF 条件判断使得结果为空。”我有不解:不考虑HASONEVALUE,单看RANKS函数,RANKX函数中参数返回的是标量,当行为颜色而该颜色品牌不止一个时参数[Sales Amount]会返回每个品牌对应的销售额排名么?这样的话返回多个值了,应该报错呀,我的理解哪里出了问题?

游客
James

如何在卡片图中显示某个品牌的动态排名?(根据筛选项变化,卡片图中排名变化)
尝试了几次,卡片图里显示的排名都和表格中显示的排名不一致。

成员
风自由

老师,为什么我这样写第二个参数有问题啊?如果是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
DAX 圣经

导读

初识 DAX

DAX 基础知识

DAX 原理

DAX 高级原理

基础函数类型

迭代函数

CALCULATE 函数

CALCULATE 调节器

基础表函数

条件判断函数

查找匹配函数

时间智能函数

统计类函数

投影函数

分组/连接函数

集合函数

其他函数