对于初学者,总是会把度量值和计算列搞混,我也经常碰到这样的问题,有些星友用文章中的代码总是报错,发给我一看,才知道TA把本来是度量值的DAX用在了计算列上,或者相反。
即使是已经学习了很长时间的人,对于计算列和度量值的写法也未必轻车熟路,还是有可能不明白其中到底有什么不同,这篇文章就试图把度量值和计算列彻底讲清楚。
什么是计算列、什么是度量值
计算列是直接在现有的表上添加一个新列,
新建的计算列,和表中之前已经存在的列一样,可以作为表格的行、列,图表的轴以及切片器的字段等等,也可以使用计算列与其他表建立关系。
而度量值,如果不放到上下文环境中,它就是一个孤立的公式,并没有视觉上的计算结果,因此,很多人会觉得度量值比较难以理解,尤其是Excel用习惯的人,都会倾向于使用计算列。
字段标识不同
如下图的字段面板
倒数第二个字段,前面有fx符号,代表的是计算列;而最下面一个,前面是个计算器的标识,表明[最后订单日期]是一个度量值。
怎么写计算列、怎么写度量值?
这个才是大家更关注的问题,也是平时容易出错的地方。下面以一个实例来介绍计算列与度量值的写法有什么不同。
以这个简化的订单表为例,如何计算出每个客户的最后下单时间?
计算列的写法
不少人也许直接这样写,
结果都是订单表中最后一个订单日期,显然与我们的期望不符,为什么会这样呢?
计算列的上下文就是当前行,但是MAX函数(包括SUM、MIN等聚合函数)并不能将行上下文转化为筛选上下文,也就是说当前行没有起到筛选作用,它就会返回整个表的最大订单日期。
关于上下文,不要理解的太复杂,完全可以按中文字面意思来理解,简单来说就是当前的计算环境,一般分为行上下文和筛选上下文,行上下文可以简单的理解为当前行,行上下文并不会必选产生筛选作用,只有筛选上下文才会产生筛选作用,筛选上下文有很多种形式,比如表格的当前行,矩阵的行和列、图表的坐标轴、报表的切片器、筛选器等。
如何让当前行发生筛选作用呢?在MAX函数外围再套个CALCULATE函数就可以了,
这样返回的下单日期都不同了,但是仔细看就会发现,返回的日期和当前行的下单日期是一样的,并没有按照客户进行计算。
再强调一遍,计算列的上下文就是当前行,包括当前行的每一个值,在上表中,每一行的数据是唯一的,因此只能返回当前行的订单日期。
那么到底如何按照客户来计算,返回当前客户的最后下单日期呢?
解决这个问题的DAX写法有很多种,这里用个相对简洁的代码,
ALLEXCEPT函数的意思是,清除订单表其他列的筛选作用,只保留客户列的筛选,这样当前行的上下文,其他列的筛选作用清除了,只有客户发生筛选作用。
比如客户甲,有两笔订单,使用MAX计算出最大的订单日期,也就是甲的最后下单日期。
上面说了这么多计算列的写法,那么度量值该怎么写呢?
最后下单日期=MAX('订单表'[订单日期])
是不是简单到不敢相信,那么如何看到计算结果呢,最简便的方式就是直接用个表格或者矩阵展现结果,
再举个例子,依然是这个订单表,如何计算每个产品的最后卖出日期?
如果你理解了上面计算列的原理,这个问题是不是也很容易就出来了,
那么度量值又是如何写才能解决这个问题的呢?
其实度量值的写法可以完全不变,依然可以用上面的代码(MAX('订单表'[订单日期]),只要把表格的维度改为产品名称就可以了,
通过上面两个问题,你看出来计算列和度量值有哪里不同了吗?
在计算列中,其上下文是明确的,就是当前行,并且可以根据需要缩小为当前行的某一列;
而度量值,编写时并没有明确的上下文,度量值写好之后,可以放在任意的上下文环境中,执行不同的计算;
计算列写好代码,数据自动计算出来,并且不受外部筛选器的影响;
而度量值写好之后,并没有立即计算,只有放到上下文中时,才进行计算并返回结果。
所以利用DAX解决问题时,首先应想到上下文是什么,新建列时如何利用当前行的上下文?
而度量值虽然暂时没有上下文,但在建度量值之前,也首先应该想清楚,我需要构建怎样的上下文环境才能得到期望的结果,然后利用心中所想的上下文环境,来对应的编写度量值的DAX代码。
在PowerBI中,除了计算列和度量值,还有个地方可以用到DAX,就是使用DAX新建表,
使用建表的方式同时可以解决上面的问题,
其中VALUES('订单表'[客户]),返回客户的不重复列表,就是为了给后面的代码提供上下文,有了这个上下文,后面的代码与新建列的写法是一样的。
通过这三种使用DAX的环境,以及上面的几个例子,应该能发现,无论是度量值、计算列还是建新表,其DAX代码本质上并没有什么不同,不同的只是下上文而已。
计算列只能利用当前行上下文;新建表可以使用DAX构建上下文;’而度量值最灵活,先不管上下文,写好代码之后,给什么上下文,就执行什么运算,或者说需要什么计算,就给度量值提供什么上下文。
你以后再遇到问题打算用DAX实现,一定要先想清楚,是通过计算列、新表还是度量值来解决,然后再开始写对应的DAX。
计算列和度量值写法的不同点
关于计算列和度量值在写法上有一些不同,比如在计算列中使用列,可以直接使用,默认就是该列的当前行,比如返回当前订单日期的前一天,可以直接写:
而在度量值中使用列,必须在列上套一个聚合函数,否则不能自动转换为筛选上下文,计算订单日期前一天的度量值应该这样写:
订单日期前一天 = MAX('订单表'[订单日期])-1
什么时候使用新建列,什么时候使用度量值
一般来说,为了性能考虑,能用度量值解决的问题就尽量不用计算列(参考:高效使用Power BI的15条建议)。只有当你需要利用新建的计算列生成切片器(度量值不能用于切片器),或者需要用这个字段与其他表建立关系时,才需要新建列。
另外当需要创建很复杂的字段时,尽量使用计算列,因为它需要时间来运算,提前运算完成,而不是在需要用的时候再进行计算(度量值是用的时候才计算),可以带来更流畅的体验。
总结
计算列和度量值在DAX代码的写法上并没有本质不同,形式上的不同只是由于上下文环境的区别引起的。
二者的本质不同在于其运算特点,是否根据上下文动态运算、以及计算时间上的不同,才是根本的差异。
如果你看到这里还是有不明白的地方,那么从头再仔细看一遍吧。
干货推荐:
采悟:PowerBI分析模型:产品关联度分析zhuanlan.zhihu.com如果你刚开始接触Power BI,可在微信公众号后台回复"PowerBI",获取《七天入门PowerBI》电子书,轻松上手。
如果文章对你有帮助,别忘了点赞哦。