Thinking in DAX with PowerBI - 邏輯框架 - 計(jì)算邏輯

PowerBI 目前作為商業(yè)智能工具,其核心功能特性是:分析。我們會(huì)開設(shè)一個(gè)系列《Thinking in DAX》和大家一起從思想和計(jì)算的抽象層面來再次深入理解這個(gè)過程。此前,與此有關(guān)的內(nèi)容,也會(huì)納入進(jìn)來。
要分析和處理一個(gè)問題,需要有解決它的邏輯框架,這涉及兩個(gè)內(nèi)容:
數(shù)據(jù)結(jié)構(gòu) - 數(shù)據(jù)以什么形式擺放
計(jì)算方法 - 如何基于數(shù)據(jù)擺放的結(jié)構(gòu)進(jìn)行計(jì)算
有過大學(xué)計(jì)算機(jī)相關(guān)背景的伙伴會(huì)非常清楚:數(shù)據(jù)結(jié)構(gòu)和算法,是一個(gè)程序員(軟件開發(fā)工程師)的絕對(duì)內(nèi)功心法。
大家日??吹胶芏鄦栴}的解答,例如:如何做同比分析,如何做一個(gè)圖,如果實(shí)現(xiàn)一個(gè)技巧,屬于外顯招式。
如果你看過《神雕俠侶》(古天樂版),應(yīng)該記得一件事:
有了內(nèi)功心法,不會(huì)招式,無法解決實(shí)際問題
有了招式技巧,不會(huì)心法,無法對(duì)戰(zhàn)真正高手
在目前市面上講解 DAX 的資料中,大部分講的是招式以及招式的細(xì)節(jié),學(xué)習(xí)招式的體會(huì)是:
一個(gè)技巧解決一個(gè)問題,爽。
但新的問題來了,如果不看老師怎么操作,自己還是不會(huì)。
沒有舉一反三的能力,無法應(yīng)對(duì)復(fù)雜問題的處理。
創(chuàng)新性思維受到限制,因?yàn)闆]有系統(tǒng)性建立根基,沒有思考的發(fā)力點(diǎn)。
那么,結(jié)合內(nèi)功心法和外顯招式,可以真正理解外顯招式為什么是那樣的。總之:
外顯招式
好處:拿來就用
壞處:無法洞察本質(zhì),當(dāng)人習(xí)慣了招式,很難去思考更本質(zhì)的東西
內(nèi)功心法
好處:抽象本質(zhì),可以幫助人們開發(fā)出更多技巧和模式
壞處:往往不能拿來就用
說白了,這特別像是:短期回報(bào)和長期回報(bào)的感覺。就如同短視頻可以讓人們在幾秒high起來,但它畢竟是短視頻。長視頻需要人們付出更多時(shí)間去觀看和思考,但可能將一個(gè)問題揭示得更加透徹。
邏輯框架
本文不準(zhǔn)備展開講邏輯框架,太抽象。但我們可以得到這樣的共識(shí),邏輯框架,這涉及兩個(gè)內(nèi)容:
數(shù)據(jù)結(jié)構(gòu) - 數(shù)據(jù)以什么形式擺放
計(jì)算方法 - 如何基于數(shù)據(jù)擺放的結(jié)構(gòu)進(jìn)行計(jì)算
數(shù)據(jù)結(jié)構(gòu)和算法,在大學(xué)課程中,有兩本厚厚的書與之對(duì)應(yīng),例如:

(這里只是示例,不推薦大家購買書籍學(xué)習(xí),太繞遠(yuǎn)路。)
我們并不用按照本科甚至考研的要求來講解算法和數(shù)據(jù)結(jié)構(gòu),但這里面最精華的思想我們可以提煉并與 DAX 結(jié)合,為大家呈現(xiàn),讓大家從一個(gè)更嚴(yán)謹(jǐn)更上帝視角來理解 DAX。
很多人會(huì)去使勁學(xué)習(xí):篩選上下文和行上下文。在任何關(guān)于 DAX 的資料里,只會(huì)告訴你 DAX 中存在兩個(gè)上下文:篩選上下文和行上下文,但沒有任何資料講它們?yōu)槭裁匆嬖?。就好像是上帝,說要有光,于是就有了光,那為什么要有光呢?我們需要一個(gè)更本質(zhì)的認(rèn)知,沿著線索,找尋 DAX 被設(shè)計(jì)時(shí)經(jīng)過的心路,這不但有趣,而且可以讓你忘卻 DAX 的表象,而逼近它的基因本質(zhì)。
數(shù)據(jù)結(jié)構(gòu),之所以存在,就是為了基于它創(chuàng)建更優(yōu)良的計(jì)算方法(算法);
計(jì)算方法,之所以存在,必須依賴于一個(gè)數(shù)據(jù)結(jié)構(gòu)才能發(fā)揮作用。
這兩者是共生共滅的。
感受 DAX 中的算法與數(shù)據(jù)結(jié)構(gòu)
由于 DAX 的設(shè)計(jì)初衷是給商業(yè)分析師的,也就是業(yè)務(wù)人員,所以,我們不會(huì)把大家搞成程序員,但這絲毫不影響我們?nèi)ダ斫馑枷?。?DAX 中,你其實(shí)已經(jīng)用過了很多算法,你編寫的任何 DAX 公式都是一個(gè)算法,都是一個(gè)計(jì)算方法,這些計(jì)算方法被定義成了一個(gè)核心部件,叫:度量值。
從這個(gè)意義上來說,度量值,是算法(計(jì)算方法)的定義。僅此而已。
你還記得這個(gè)折磨你的函數(shù)嗎?CALCULATE,就是計(jì)算的意思。CALCULATE 從一定意義上也在揭示,它負(fù)責(zé)一個(gè)算法。
你覺得自己沒有見過 DAX 中的數(shù)據(jù)結(jié)構(gòu)嗎?
數(shù)據(jù)結(jié)構(gòu),是數(shù)據(jù)擺放的形態(tài)。
DAX 中,的數(shù)據(jù)結(jié)構(gòu)天然就是一個(gè)表。
你也許已經(jīng)看過星型模型的說法,這是多個(gè)表所形成的數(shù)據(jù)結(jié)構(gòu)。
你可能覺得沒有什么新意。沒錯(cuò),在有的時(shí)候,我們不是按照表的思維。我來舉兩個(gè)例子。
視為列表,列表(List),強(qiáng)調(diào)的不是表,而是一個(gè)列,例如:VALUES( Product[SKU] )。往往下一步就是對(duì)列表的迭代。
視為集合,集合(Set),強(qiáng)調(diào)了不重復(fù)的無序元素,例如:VAR UsersLastYear = ... VAR UsersNow = ... 。往往下一步就是集合的交并補(bǔ)運(yùn)算,如:RETURN INTERSECT( UsersLastYear , UsersNow ) ,這就是去年用戶在現(xiàn)在的留存。
雖然我們眼睛看到的都是一個(gè)物理結(jié)構(gòu):表。僅僅只有這個(gè)結(jié)構(gòu),但它可以被理解成的數(shù)據(jù)結(jié)構(gòu)包括但不限于:
值,一行一列的表。
列表,往往要施加迭代運(yùn)算。
集合,往往要施加交集等運(yùn)算。
那么現(xiàn)在,你應(yīng)該可以感受到,很多時(shí)候往往你思考一個(gè)問題而不得解,是沒有想好數(shù)據(jù)的結(jié)構(gòu)。例如,要計(jì)算留存用戶數(shù)的思路就是要使用集合的結(jié)構(gòu)。
我們后續(xù)會(huì)計(jì)算展示不同數(shù)據(jù)結(jié)構(gòu)的使用,但這些僅僅是《Thinking in DAX》的一個(gè)部分哦。
計(jì)算邏輯
這是本文的重點(diǎn)內(nèi)容了。
在學(xué)習(xí) DAX 之前,我們是否懷疑過一件事:DAX 的函數(shù)是有限的,那么對(duì)于任何一個(gè)復(fù)雜的業(yè)務(wù)問題,都可以用 DAX 求解嗎?如果不能,那 DAX 的能力豈不是很有限嗎?有限到什么程度呢?那 DAX 這么弱的話,是不是我還是去學(xué)其他的工具好了。
我們剛剛講過解決任何問題,都需要邏輯框架,它包括:
數(shù)據(jù)結(jié)構(gòu)
計(jì)算方法
經(jīng)過科學(xué)家論證,如果某種計(jì)算方法能夠充分提供三個(gè)計(jì)算邏輯,在理論上是可以表示任何計(jì)算方法的,這三個(gè)邏輯就是:
順序邏輯
分支邏輯
循環(huán)邏輯
那么,問題來了,DAX 中有沒有這幾種邏輯的表達(dá)呢?如果沒有或者缺失,那么 DAX 就很有限了;如果有,那么豈不是可以這么來思考問題了。
DAX 中的順序邏輯
首先,我們要看懂什么是順序邏輯,如下:

在 DAX 中,如何表示順序邏輯呢?
有兩種方法。
方法一,DAX 本身就是順序邏輯。DAX 的函數(shù)是可以嵌套的,嵌套就是一種順序邏輯,先執(zhí)行內(nèi)部函數(shù),再執(zhí)行包裹內(nèi)部函數(shù)的外層函數(shù),依次類推。
方法二,使用 VAR ... RTURN ... 結(jié)構(gòu)。下面給出使用 VAR ... RTURN ... 結(jié)構(gòu)的四種形態(tài)。
// 基本形態(tài)
VAR X = ...
VAR Y = ...
RETURN ...
// VAR 中帶有 VAR ... RETURN ... 結(jié)構(gòu)
VAR X =
VAR X1 = ...
VAR X2 = ...
RETURN ...
VAR Y = ...
RETURN ...
// RETURN 中帶有 VAR ... RETURN ... 結(jié)構(gòu)
VAR X = ...
VAR Y = ...
RETURN
VAR A = ...
VAR B = ...
RETURN ...
// VAR 和 RETURN 中分別帶有 VAR ... RETURN ... 結(jié)構(gòu)
VAR X =
VAR X1 = ...
VAR X2 = ...
RETURN ...
VAR Y = ...
RETURN
VAR A = ...
VAR B = ...
RETURN ...很多小伙伴更喜歡使用 VAR ... RETURN ... 結(jié)構(gòu),就是因?yàn)楫?dāng)你習(xí)慣大腦用順序思考問題時(shí),自然用這種結(jié)構(gòu)很貼合人的思考過程。
DAX 中的分支邏輯
首先,我們要看懂什么是順序邏輯,如下:

在 DAX 中,如何表示分支邏輯呢?
你應(yīng)該想到兩個(gè)函數(shù):IF 和 SWITCH。
本質(zhì)上,SWITCH 只是 IF 的變體,因?yàn)椋琒WITCH 總可以表示成更復(fù)雜的 IF 結(jié)構(gòu)。
請(qǐng)參考上圖,注意其中的演化二字,雖然,編寫的公式一樣,但出現(xiàn)在人們大腦腦海的邏輯結(jié)構(gòu)可能不同,可能是上面的樣子,也可能是下面的樣子。但我們建議你用下面的模式來思考,它可以應(yīng)對(duì)任何情況。
對(duì)應(yīng)于編寫 DAX 公式,首先來看 IF 的雙分支結(jié)構(gòu),如下:
IF( A = B , A , B )很快地,我們就會(huì)遇到多個(gè)分支的情況,如下:
KPI =
// 通用類 KPI,具體 KPI 由用戶選擇決定
// KPI 的通用化,通常有切片器和它相配合
// 由 DAX Pro 生成,參考:www.excel120.com/daxpro/
SWITCH( SELECTEDVALUE( 'Option.KPI'[KPICode] ),
"Sales" , [KPI.銷售額] ,
"Profit" , [KPI.利潤] ,
"Volume" , [KPI.銷量] ,
"Profit%" , [KPI.利潤率] ,
[KPI.銷售額] // 如果用戶不選擇任何 KPI 選項(xiàng),所默認(rèn)使用的 KPI。
)這種形態(tài)還不夠通用,當(dāng) SWITCH 要對(duì)比的條件不是 A = B 這種邏輯,而是例如:銷售額 > 1000 這種更復(fù)雜的對(duì)比,就要使用更通用的結(jié)構(gòu)如下:
Item.ABC.Color =
// 由 DAX Pro 生成,參考:www.excel120.com/daxpro/
SWITCH( TRUE() ,
[Item.ABC.Value%] <= MIN( 'Option.X'[Option.X] ) / 100 , "Green" ,
[Item.ABC.Value%] <= MAX( 'Option.X'[Option.X] ) / 100 , "Orange" ,
"Red"
)這樣的通用結(jié)構(gòu)就可以確保,只要是一個(gè)分支邏輯不管是幾個(gè)分支,都可以表達(dá)。
DAX 中的循環(huán)邏輯
首先,我們要看懂什么是順序邏輯,也可以演變?yōu)榈壿?,如下?/p>

對(duì)于循環(huán)結(jié)構(gòu),用代碼表示,大概邏輯如下:
i = 1
for(i<=100){
...
i = i + 1;
}其中,i 就是一個(gè)循環(huán)變量,我們不用理解編程的過程,但我們可以知道 i 就表示了循環(huán)的次數(shù),也就是輪數(shù),它可以用另一種等價(jià)結(jié)構(gòu)表示,就成為了迭代,如下:
list = ...
foreach( line in list ){
...
}迭代結(jié)構(gòu)可以完全替換掉循環(huán)結(jié)構(gòu),而且有一個(gè)好處,這里并不需要一個(gè)所謂的循環(huán)變量 i。我們甚至可以這樣寫這個(gè)邏輯,如下:
Table = ...
ForEach( Table , fx( Row , ... ) )其中,F(xiàn)orEach,是一個(gè)迭代函數(shù),Table 是 ForEach 迭代的對(duì)象,該對(duì)象是一個(gè)列表,在迭代中,對(duì)于每次迭代的行 Row 施加一個(gè)函數(shù) fx 去處理它。
如果你看懂了上述的過程,那恭喜你,你應(yīng)該可以頓悟到底什么是行上下文了。
如果你說你沒見過在 DAX 有 ForEach 這個(gè)函數(shù),那沒有問題,我們自己來設(shè)計(jì)一下:
FILTER( Table , ... ) = ForEach( Table , fx( Row , ... ) )
// 其中,
fx( Row , ... ) = IF ( ... = FALSE , RemoveRowFromTable( Table , Row ) )
而作為使用者,我們只需要使用:
FILTER( Table , ... )我們永遠(yuǎn)不需要知道里面有個(gè) Row 的處理過程,而這個(gè) Row 的本質(zhì)其實(shí)是:Table[i],也就是表的第 i 行,等價(jià)于開始的循環(huán)結(jié)構(gòu)。
類似地,SUM,SUMX,MAX,MAXX,ADDCOLLUMN 等函數(shù)都內(nèi)置了:ForEach( Table , fx( Row , ... ) )。
因此,我們可以徹底回答兩個(gè)重要問題:
什么是行上下文?答案:ForEach( Table , fx( Row , ... ) ) 中正在被迭代的 Table 的第 i 行 Row。
為什么要有行上下文?答案:為了支持循環(huán)邏輯(迭代邏輯)的同時(shí)還不必考慮循環(huán)變量。
這樣,我們不僅搞清楚了行上下文就是 DAX 為了實(shí)現(xiàn)迭代邏輯來創(chuàng)建的內(nèi)部結(jié)構(gòu);還搞清楚了它存在的動(dòng)機(jī)是完成循環(huán)(迭代)來實(shí)現(xiàn)大規(guī)模運(yùn)算。
注意:上述的描述,在邏輯上是沒有問題的,在 DAX 引擎的底層實(shí)現(xiàn)上,有更復(fù)雜的優(yōu)化,但這根本不是業(yè)務(wù)分析師需要理解的,更不會(huì)影響我們用這里的邏輯來處理任何問題。正如每天生活在大地上,用牛頓運(yùn)動(dòng)定律就夠了,非要說它不嚴(yán)謹(jǐn),一定要用相對(duì)論算一輛車開起來的樣子只是自尋煩惱。
如果您是一個(gè)業(yè)務(wù)分析師,根本看不懂上面寫的是什么,也不要緊,您只需要明白一個(gè)重要的事情:
DAX 是支持循環(huán)邏輯的,這是構(gòu)成解決任何問題的計(jì)算方法必備的順序,分支,循環(huán)邏輯之一的最強(qiáng)大邏輯。
我們在面對(duì)數(shù)據(jù)分析時(shí),往往都不是一條數(shù)據(jù),而是成千上萬條數(shù)據(jù),因此,迭代邏輯是必須的。
那么,我們再來考大家一個(gè)問題:
SUM 中是否有迭代邏輯?
在 《DAX權(quán)威指南》4.8 中是這樣寫的:
Aggregators like SUM, MIN, and MAX only use the fi lter context, and they ignore the row context.
在線閱讀版,參考:http://www.excel120.com/#/dax2/c4/c4-2
SUM,MIN,MAX 等聚合函數(shù)使用篩選上下文,忽略了行上下文。
從這里的學(xué)習(xí)可以發(fā)現(xiàn),SUM 并不會(huì)忽略行上下文,而在 SUM 又構(gòu)建了自己的行上下文體系,迭代發(fā)生在 SUM 中。
也就是說:
SUM( T[C] )
等價(jià)于
SUMX( T , T[C] )那么對(duì)于:
SUMX( T , SUM( T[C] ) )其中的?SUM( T[C] )?位于行上下文中,但 SUM 會(huì)產(chǎn)生新的迭代,進(jìn)而創(chuàng)建自己的行上下文,如下:
SUMX( T , SUM( T[C] ) )
等價(jià)于
SUMX( T , SUMX( T , T[C] ) )
等價(jià)于
ForEach(
Table ,
Fx( RowX ,
// SUM 開始
ForEach(
Table,
Fx(
RowY ,
[C]
)
// SUM 執(zhí)行完畢
)
)因此,SUMX( T , SUM( T[C] ) )?中的 SUM 會(huì)迭代整個(gè) T ,但外部的確有一個(gè)行上下文的。
可能《DAX權(quán)威指南》的作者希望讀者更容易的記住這件事,用了忽略一詞,于是很多小伙伴問過這個(gè)問題。
因此,SUM 中是有迭代邏輯的。
那么這個(gè)迭代邏輯怎么用于生產(chǎn)實(shí)踐呢?
請(qǐng)這樣思考問題:對(duì) ... 進(jìn)行迭代,在每步中,...。
這樣的話語應(yīng)該出現(xiàn)在你的大腦中。
修煉建議
理解了三個(gè)計(jì)算邏輯:順序,分支,迭代。它分別對(duì)應(yīng)了你的:小學(xué),初中,高中。
小學(xué)三年級(jí),學(xué)習(xí)了:150 - { 90 - [ 5 + ( 3 - 2 ) × 2 ] } 這就是嵌套公式
小學(xué)五年級(jí),學(xué)習(xí)了:將上述算式分步,就是 VAR ... RETURN ...
初高中年級(jí),學(xué)習(xí)了:y = ax2 + bx + c ( a ≠ 0 ),a ≠ 0 或 = 0 就是分類討論思想。
初高中年級(jí),學(xué)習(xí)了:集合,數(shù)列,里面其實(shí)也蘊(yùn)含了迭代的思想。
很多初學(xué)者,甚至是有一定學(xué)習(xí)經(jīng)歷的伙伴還是沒有能真正駕馭 DAX,其實(shí) DAX 根本不難,也根本不需要糾結(jié)于上下文啊上下文。
下面給出,正確思考問題的流程套路:
第一步:用順序邏輯,建立解決問題的大框架。如:腦中暗暗想著第一大步做什么,第二大步做什么,就對(duì)了。
第二步:在順序邏輯的框架里,進(jìn)一步考慮細(xì)節(jié)。如:如果...怎么樣,我就...怎么樣,就對(duì)了。
第三步:在順序邏輯的框架里,進(jìn)一步考慮細(xì)節(jié)。如:迭代一個(gè)列表,在迭代的每步里,干...什么,就對(duì)了。
在上面的每一步的反復(fù)實(shí)踐中,您會(huì)慢慢地:
在每一步的最終細(xì)節(jié),使用 DAX 函數(shù)落地,具體可以參考 BI 佐羅的《DAX 36 個(gè)核心函數(shù)》。
在反復(fù)的重復(fù)中,這個(gè)思維模式會(huì)變成自然的習(xí)慣,從大腦進(jìn)入身體內(nèi)化成自然的身體反應(yīng)。
接著,大腦思考業(yè)務(wù)問題,手中流淌出 DAX 公式,如是而已。
總結(jié)
本文提出了我們會(huì)陸續(xù)給出《Thinking in DAX》的系列。本文只是其中一篇而已。
本文提出了邏輯框架,并揭示了數(shù)據(jù)結(jié)構(gòu)和計(jì)算方法在 DAX 的本質(zhì)重要性。
本文詳細(xì)闡述了計(jì)算方法中的三大邏輯以及在 DAX 中的實(shí)現(xiàn)并本質(zhì)地揭示了行上下文的運(yùn)行邏輯,最后給出了大家修煉 DAX 運(yùn)算能力的建議。
如果您在學(xué)習(xí) DAX 或解決業(yè)務(wù)問題中有什么實(shí)際通用問題,歡迎交流。
2020年10月 PowerBI VIP 實(shí)訓(xùn) 深圳 - 揭示全部 DAX 本質(zhì)及應(yīng)用技法。?

讓數(shù)據(jù)真正成為你的力量
Create value?through?simple and?easy?with fun?by PowerBI
Excel BI?|?DAX Pro?|?DAX?權(quán)威指南?|?線下VIP學(xué)習(xí)
掃碼與PBI精英一起學(xué)習(xí),驗(yàn)證碼:data2020
PowerBI MVP 帶你正確而高效地學(xué)習(xí) PowerBI
點(diǎn)擊“閱讀原文”,即刻開始
↙
