《DAX圣經(jīng)》第六章:DAX實例
? ? ? ? ? ? ? ? ? ------PowerBI非官方 簡體精簡筆記
? ? 計算比率與百分比
? ? ? 百分比:用一種度量的部分值去除以同一度量的總值。
? ? 第一種:銷售占總銷售額的百分比
? ? ? 包含百分比的典型報告如圖6-1所示,圖6-1一個典型的百分比報告,包含銷售額蛤肌,顯示的是絕對值和百分比。
[Sales %] := DIVIDE([Sales Amount],? ? ? ? ? ? ?
CALCULATE( [Sales Amount],ALLSELECTED() ))
? ? ? 該公式包含需要計算的兩個度量:一個是元度量[Sales Amount](作為分子)眨猎,一個是通過元度量加上ALLSELECTED()條件(受透視表行抑进、列、篩選器影響)的分母度量睡陪。分母處寺渗,由CALCULATE配合ALLSELECTED函數(shù)創(chuàng)建了一個新列表模型,維護了所有原始篩選(切片器和篩選器以及在透視表中的任何篩選器)兰迫。
? ? 第二種:有時信殊,不僅僅是計算銷售占總銷售額的百分比。
? ? ? 可能希望顏色列顯示在每個年份總數(shù)中所占的百分比汁果,以便生成如圖6-2所示的報告涡拘。圖6-2 在本報告中,所顯示的百分比是相對于年份的總數(shù)据德,而不是總計鳄乏。
VALUES函數(shù)返回當(dāng)前在列表篩選中可見的所有唯一值的列值:
[Yearly %] := DIVIDE([SalesAmount],
CALCULATE([Sales Amount],ALLSELECTED(), VALUES(Date[CalendarYear] )))?
? ? ? 值得注意的是仲吏,整個CALCULATE([SalesAmount],ALLSELECTED()),就是前面公式的分母蝌焚,其實就是一個度量值列表裹唆。我們只是在該度量的基礎(chǔ)上,加了一個VALUES ( Date[CalendarYear]列表篩選條件而已只洒。其實際是一個簡潔版的CALCULATE(度量+篩選)许帐,因為與上一個公式只是分母不同”锨矗可將分母定義成度量的簡寫公式:
[Sales %01]=[Sales Amount] (ALLSELECTED())
則前面的公式也可簡寫為:
[Yearly %]:=DIVIDE([Sales Amount],
[Sales %01] ( VALUES( Date[CalendarYear] )//度量+篩選成畦。
? ? ? ? 當(dāng)CaⅠcuⅠate的第一參數(shù)引用己存在的某個度量時,可以省略CaⅠcuⅠate涝开。
? ? ? ? 通過VALUES()篩選后循帐,返回正確的結(jié)果。唯一的例外是列的總計:本例中的每一行舀武,VALUES()的結(jié)果返回了兩個可見的年份(2007與2008)拄养。到目前,我們使用了兩種模式來計算百分比的分母:(1)使用ALLSELECTED()银舱,還原原始篩選器篩選瘪匿;(2) 重新在被還原的原始篩選中定義任何新的篩選器(例如,限于當(dāng)前年份等)寻馏。
? ? 第三種:也可以應(yīng)用反向篩選模式棋弥。
? ? ? 也就是說,可以直接從原關(guān)系列表模型開始操软,而不是使用原始篩選器嘁锯,然后重新應(yīng)用篩選,結(jié)果也將相同聂薪。例如家乘,再現(xiàn)圖6 - 1中的報告,可以恢復(fù)在[Color]以及[CalendarYear]列上的篩選藏澳,而不會影響其他篩選仁锯,針對需要維護的列分別使用ALLSELECTED如:
[Yearly %] :=DIVIDE([Sales Amount],
CALCULATE([Sales Amount],
ALLSELECTED( Product[Color] ),?
ALLSELECTED( Date[CalendarYear] )))
? ? ? 還可以使用以下代碼獲得圖6-2的度量,這將只從[Color]中刪除篩選器翔悠,保持該列的篩選器不受影響:
[Yearly %-2] := DIVIDE ([SalesAmount],
CALCULATE([SalesAmount],ALLSELECTED (Product[Color] )))
? ? ? 這些度量作為示例顯示在透視表時业崖,它們都返回相同的值野芒。但是,它們之間有很大的差別双炕。事實上狞悲,這個[Yearly %]的后一版本恢復(fù)了Color上的原始篩選器,但是保留了任何其他位置的篩選器妇斤。如果篩選的列只有年份摇锋,則結(jié)果是相同的,但是站超,一旦通過日歷表中添加更多篩選器列表時荸恕,則兩個度量返回不同的值,如圖6-3所示:
? 圖6-3如果向透視表添加更多篩選器列表死相,兩種度量將返回不同的值融求。
? ? ? 正如所看到的,Yearly%返回相對于當(dāng)年的銷售百分比算撮,而Yearly%-2則返回相對于月份的銷售百分比生宛,這是因為我們將月份添加到透視表中。但這并不表示這兩個計算一個正確一個錯誤钮惠,它取決于希望計算的數(shù)值茅糜。
這里需要記住的重要一點是:
每當(dāng)計算百分比時,需要非常清楚分母是什么素挽,以及當(dāng)用戶向透視表或報表添加的更多篩選器是什么蔑赘。
計算累計總數(shù)
另一種使用頻率較高的模式,可能是累積總數(shù)模式预明。每當(dāng)有一組事務(wù)處理缩赛,并且我們有興趣在某個序列(通常是時間)上累積它們的值時,都會討論累積總計撰糠。例如:
? ? ? (1)計算一個產(chǎn)品在所有時間內(nèi)的總銷售額酥馍,以此作為累計總額;
? ? ? (2)計算一段時間內(nèi)不同客戶的總數(shù)阅酪,來作為累計值等旨袒。
? ? ? 讓我們從分析一個簡單的透視表開始,該透視表顯示了一段時間內(nèi)銷售的產(chǎn)品數(shù)量术辐⊙饩。可以在圖6 - 4中看到它:
? ? ? 圖6-4這個透視表顯示了每年和每個月銷售的產(chǎn)品總數(shù)。度量非常簡單:
[NumOfProducts] := SUM( Sales[Quantity] )
? ? ? 我們知道:此度量值計算Sales中的Quantity列數(shù)量之和辉词。它是一個元度量(即在整個數(shù)據(jù)模型下計算)必孤。因為時期表與銷售表具有一對多的關(guān)系,如果選取May 2007的值瑞躺,它將篩選銷售表敷搪,得到May 2007的銷售額兴想。這很容易。
? ? ? ? 關(guān)鍵是赡勘,如果要計算May 2007的累積總數(shù)嫂便,我們需要一個新的列表篩選條件:即篩選2007年底之前的所有期間(用于累計),而不是僅篩選May 2007闸与。? 這句話雖然很簡單顽悼,但卻隱藏了較大的復(fù)雜性。將條件分解一下:
? ? ? (1)確定當(dāng)前日期的結(jié)束時期几迄,如表示2007年底的示例;
? ? ? (2) 使用該值冰评,創(chuàng)建一個篩選器映胁,并顯示2007年底之前的所有日期。
? ? ? ? 隱藏的復(fù)雜性在于:
? ? ? (1)新的列表篩選基于當(dāng)前的列表篩選甲雅;
? ? ? (2)新的列表篩選必須大于原始篩選解孙,因為它將包含2007及更早的所有日期。
圖6-5中抛人,可以看到需要檢索的日期集弛姜,以便計算銷售產(chǎn)品的累積數(shù)量:
圖6-5所售產(chǎn)品的累計數(shù)量需要考慮到當(dāng)前產(chǎn)品在結(jié)束前的所有日期。
考慮到算法妖枚,現(xiàn)在是研究解決方案公式的時候了:
[CumulativeProducts]:=
CALCULATE(SUM(Sales[Quantity]),
FILTER(ALL('Date'),? ?
'Date'[Datekey]<=MAX('Date'[Datekey])))
? ? ? ? 這是一個標準的列表關(guān)系+篩選+條件的DAX公式廷臼,很容易解釋該公式:該度量由CALCULATE構(gòu)建的列表數(shù)據(jù)模型是:
? ? (1)SUM ( Sales[Quantity]計算列表 + ALL('Date' ) 列表 + 'Date'[Datekey]<=MAX('Date'[Datekey])(條件列表),后兩個列表構(gòu)成新的篩選列表(通過時期關(guān)系列表)绝页,篩選出計算列表計算荠商。如下簡易關(guān)系圖:
? ? ? (2)這里關(guān)鍵是要理解條件:'Date'[Datekey]<=MAX ('Date'[Datekey])⌒回憶一下MAX的確切含義“當(dāng)前篩選列表DateKey列的最大值”莱没。
? ? ? ? 一方面,因為表達式是計算篩選的一部分酷鸦,所以它仍然回到原始列表篩選中工作饰躲。例如,如果篩選May 2007臼隔,那么嘹裂,對應(yīng)的最大日期是2007年的最后一天;
? ? ? 另一方面躬翁,左邊表達式Date[DateKey] 是一個列焦蘑,意思是“當(dāng)前行篩選下的DateKey中的列值”,該列值再構(gòu)成由FILTER創(chuàng)建的“當(dāng)前列表篩選”盒发。因此例嘱,該表達式表示為:“篩選所有低于或等于(<=)May 2007最后一天的日期狡逢。這正是我們需要的條件。你可以在圖6-6中看到實際的公式計算的結(jié)果:
? ? ? ? ? 圖6-6 CumulativeProducts將值加到當(dāng)前日期拼卵。
? ? ? ? 還有一個問題是奢浑,這個公式。實際上顯示了2010年及以后的數(shù)據(jù)腋腮,而數(shù)據(jù)庫中并沒有2010的數(shù)據(jù)雀彼。而且未來所有時期的數(shù)據(jù)都將會顯示。這種行為是正確的即寡,因為我們計算的就是:今后銷售的產(chǎn)品的累計數(shù)量=迄今為止銷售的總數(shù)徊哑。盡管如此,你可能不想顯示這些數(shù)字(這種業(yè)務(wù)場景是常見的)聪富。這可以通過用空白替換不需要的行來刪除它們:
? ? (1)通過透視表表默認隱式所有值為空的行(同樣也適用于列)莺丑;
? ? (2)要隱藏不需要的行,可使用IF函數(shù)檢查當(dāng)前篩選中是否存在Sales銷售墩蔓,如下代碼所示梢莽。? ? ? ? ? ? ? ? ? ? ?
[CumulativeProducts] :=IF(COUNTROWS( Sales ) >0,
CALCULATE ( SUM( Sales[Quantity] ),
FILTER(ALL( 'Date' ),
'Date'[Datekey] <=MAX( 'Date'[Datekey]))))
? ? ? 該度量中,我們基于這樣一個事實:即IF的Else分支如果缺失奸披,將返回一個空白昏名。
計算每天與工作日的銷售額
? ? ? 另一個常用的業(yè)務(wù)場景是如何在工作日內(nèi)執(zhí)行計算的例子。這是相對于時間智能計算來說的一個簡單的場景阵面。
如圖6-7所示轻局。希望生成一個報告:
? ? ? 圖6-7在這個透視表中,可以看到銷售額分別除以天數(shù)以及工作日天數(shù)样刷。
所顯示的數(shù)字分別包含不同的含義:
? ? (1)? [endif]Sales Amount--銷售是該期間的總銷售額嗽交;
? ? (2) [endif]DailySales--? 每日銷售是每天的平均銷售。
? ? ? ? WorkDailySales是按工作日銷售的平均銷售颂斜,以及它與非工作日銷售的比例夫壁,以預(yù)計這些工作日會不會產(chǎn)生盈利。這些度量的定義如下:
[NumOfDays] := COUNTROWS(Date)?
[NumOfWorkingDays] :=CALCULATE( [NumOfDays],? Date[WorkingDay] ="WorkDay")? ?
[Sales Amount] :=SUMX( Sales, Sales[Quantity] * Sales[Unit Price] )
[DailySales] :? ? =DIVIDE( [Sales Amount],? [NumOfDays] )?
[WorkDailySales] :=DIVIDE( [SalesAmount], [NumOfWorkingDays])? --工作日天數(shù)銷售占比
? ? ? ? 這些度量很簡單沃疮,但包含了一些我們必須解決的問題盒让。事實上,在前一張圖表中司蔬,我們只篩 選了兩年:2007和2008邑茄。這些數(shù)字符合計算。為更清楚地顯示出多個年份俊啼,我們移除篩選器肺缕。如圖6-13所示。
圖6-13這個透視表的總計顯示了錯誤的值。
? ? ? 如果查看DailySales和WorkDailySales的總計同木,會立即發(fā)現(xiàn)數(shù)字是錯誤的浮梢。并不是行中顯示的值的平均值。該問題應(yīng)該很容易理解:因為在透視表中顯示的是正常天數(shù)和工作天數(shù)彤路。按總計計算時秕硝,計算的是沒有銷售的期間,而且由于天數(shù)位于分母洲尊,因而最終值低于預(yù)期远豺。我們先不看最后正確的公式,展示幾個錯誤解決方案對問題的影響坞嘀。
? ? ? 因為這兩項措施都有相同的問題躯护,所以讓集中討論DailySales,公式正確后丽涩,同時會適用于NumOfWorkingDays度量榛做。
? ? 第一次試驗:
? ? ? 你可能想到:清除掉那些沒有銷售的天數(shù)(即反利用條件,該情況在實際中經(jīng)常出現(xiàn)内狸,但這里只是提起該概念,該示例里是假反利用條件)厘擂,這很容易昆淡,可以為NumOfDays嘗試IF處理:? ? ? ? ? [NumOfDays] := IF([Sales Amount] >0,COUNTROWS(Date))
? ? ? 此更新公式使得沒有銷售的行從其中消失,看起來好了一些刽严。但它不會修正總計行昂灵。因此,這不僅是錯誤的舞萄,也具有很大的誤導(dǎo)性眨补,而且使問題不容易發(fā)現(xiàn),如圖6-8所示倒脓。
? ? ? 圖6-8 沒有銷售的期間行消失了撑螺,但總計仍然是錯誤的,
? ? ? 正如所看到的崎弃,總計仍然是錯誤的甘晤,即使沒有銷售的所有行都從透視表中消失了。是的饲做,它 們消失了:
? ? (1)一方面线婚,將NumOfDays設(shè)置為Blank,透視表將不顯示這些行盆均;
? ? (2)另一方面塞弊,在總計銷售額中,因為銷售額大于零,所以我們的公式仍然考慮了所有的日期游沿。
? ? ? 結(jié)論是:第一次實驗顯然走錯了方向饰抒。
? ? ? 這一度量存在一個明顯的問題:我們稱之為“粒度不匹配”。如果仔細看結(jié)果奏候,在月和年兩個時期粒度上都是正確的循集,只是在總計上是錯誤的。原因是:有多個年份并沒有銷售蔗草,這些沒有銷售的年份是不需要計算的咒彤。因此,我們說咒精,在年份粒度時镶柱,數(shù)字是正確的,而在總體粒度上數(shù)字是錯誤的模叙。
? ? ? ? 所以歇拆,需要解決的問題是:
? ? ? ? (1) 需要以正確的粒度計算公式;
? ? ? ? (2) 然后將結(jié)果合并為單個值范咨;
? ? ? ? (3) 并能在更高的粒度上計算這種度量故觅。
這就產(chǎn)生了一個問題:這個正確的粒度是什么?
? ? 第二次試驗:
? ? ? 需要的時期粒度在年渠啊,月输吏,日上都有可能,這取決于實際需要替蛉,雖然我們已發(fā)現(xiàn)總計的錯誤贯溅,但我們?nèi)匀粚δ甓戎蹈械綕M意,因此躲查,可以使用以下新公式將粒度設(shè)置在年份級別:
[DailySales] :=DIVIDE([Sales Amount],?
SUMX(VALUES('Date'[Calendar Year] ),
IF([Sales Amount] >0, [NumOfDays])))?
? ? ? 該公式定義的分母檢查在特定年份是否有銷售它浅。如果存在,則IF函數(shù)返回存在的天數(shù)镣煮,否則姐霍,返回一個空白,并且不會影響分母的總數(shù)典唇。值得記住的是:公式里包含的分子分母的兩種度量--Sales Amoun和NumOfDays都會正常邮弹,因為存在DAX自動隱式存在的CALCULATE。如果沒有這種CALCULATE蚓聘,公式就不能正確工作腌乡。
? ? ? ? 兩個隱式行篩選:
? ? (1)[Sales Amount]度量值列表隱式行篩選,即CALCULATE(SUM([Sales Amount]))夜牡;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (2)SUMX与纽,迭代函數(shù)自帶的隱式行篩選侣签;
? ? ? ? 在圖6-9中,可以看到結(jié)果現(xiàn)在是正確的急迂,即使在總計上也是如此:
? ? ? 但在實際需求中卷中,你可能希望生成在幾年內(nèi)的不完全填充數(shù)據(jù)的報告。例如:在生成8月份報告時渊抽,只有到8月為止的那些數(shù)據(jù)蟆豫,而沒有8月以后并沒有的那些月的信息。
? ? ? 本例中懒闷,該度量仍將報告不正確的數(shù)字十减。因此,這項度量尚未最后確定愤估。圖6-10中帮辟,我們希望刪除掉2009年8月8之后(沒有)的銷售:
? ? ? ? 圖6-10當(dāng)一年沒有完成時,度量仍然報告不正確的結(jié)果玩焰,
? ? ? ? 可以看到由驹,8月是最后一個月(順便說一句,它是不完整的)震捣,所有高于它的總數(shù)(即季度、年份和總計)都報告了錯誤的數(shù)字闹炉。當(dāng)然這種行為是我們(有意)在代碼中使用了錯誤的粒度蒿赢,因為正確的粒度不是年份,而是月份渣触。我們使用這個年度粒度羡棵,是因為在年度水平上度量似乎是正確的。
? ? ? 但是嗅钻,即使我們確定了總計的公式皂冰,在年的粒度級一旦該年度里的某幾個月一消失,就會遇到同樣的問題养篓。既然問題在于粒度篩選不對秃流,那么。我們有了:
? ? 第三次試驗:
? ? ? 也就是柳弄,正確的度量表述式需要同時考慮到年份和月份舶胀,如:
[DailySales] :=DIVIDE([Sales Amount],
SUMX(VALUES( Date[Calendar Year] ),
SUMX(VALUES( Date[Month] ),?
IF([Sales Amount] >0, [NumOfDays]))))
? ? ? 現(xiàn)在,分母在年份和月份上執(zhí)行兩個迭代,它分別檢查每對(年份嚣伐、月份組合)是否存在該月份的銷售額糖赔,并正確地聚合出當(dāng)前存在銷售的月份的天數(shù)。圖6-11中結(jié)果:
圖6-11一旦正確設(shè)置了粒度后轩端,公式就會報告正確的數(shù)字放典。
? ? ? 第四次試驗:
使用CROSSJOIN表達式,可以更優(yōu)雅一些:
[DailySales] :=DIVIDE([Sales Amount],
SUMX(CROSSJOIN(?
VALUES( Date[Calendar Year] ),? ? ? ? ? VALUES( Date[Month] )),
IF([SalesAmount] >0, [NumOfDays])))
? ? ? ? 注意:粒度不匹配問題經(jīng)常出現(xiàn)在多個模型設(shè)計(業(yè)務(wù)場景)中基茵。
? ? ? 如果某些度量只能在定義的粒度上計算奋构,則需要迭代定義該粒度的列,最后聚合該粒度部分的結(jié)果耿导。建議詳細研究這個示例声怔,因為它將在多個數(shù)據(jù)模型中有用。
? ? ? 值得注意的是舱呻,在最近(最后)那個月里仍有一個小問題:NumOfDays的天數(shù)結(jié)果并沒有考慮到最后一個月可能不完整的情況醋火。例如,如果在8月15日提交了一份報告箱吕,就不應(yīng)該考慮到其未來的日子芥驳,因為這時候的銷售顯然是不存在的。如果還希望在最后一個月也產(chǎn)生此正確的結(jié)果茬高,需要進一步限制日期表兆旬,方法是在前面公式的后面加上一個篩選條件:刪除最后一次銷售之后的所有日期。
? ? 最后正確的度量:
? ? ? [DailySalesCorrected]:=? ?
? ? ? CALCULATE(DIVIDE([Sales Amount],
? ? ? SUMX(
? ? ? CROSSJOIN(VALUES( Date[Calendar Year] ),VALUES( Date[Month] )),
? ? ? IF( [Sales Amount] >0, [NumOfDays] ))),? FILTER('Date', 'Date'[Date] <=MAX(Sales[OrderDate] ) ))
? ? 計算工作日差異
? ? ? 接下來是是一個非常簡單單卻非常有用的例子怎栽,因為它闡述了一種使用DAX計算值的方式丽猬,這在其他分析引擎中并不常見(比如Analysis Services多維引擎)。
? ? ? 考慮在事實表中熏瞄,對于每個訂單脚祟,有兩個這樣的日期:
? ? ? (1)OrderDate是下訂單的日期;(訂貨時期)
? ? ? (2)ShipDate? 是訂單發(fā)運的日期(發(fā)貨時期)
? ? ? ? 業(yè)務(wù)場景:計算處理訂單業(yè)務(wù)所需的天數(shù)强饮。
? ? ? ? 這很容易做到由桌,這要歸功于DAX存儲日期的方式。實際上邮丰,執(zhí)行一個簡單的減法即可:ShipDate-OrderDate就足夠能計算天數(shù)了行您。話雖如此,一些更有實際有用的數(shù)值計算是:
? ? (1)兩個日期之間的業(yè)務(wù)日天數(shù):
? ? (2)排除假日期間的工作:
? ? (3)非公司工作時的工作日天數(shù)(比如加班天數(shù))等剪廉。
? ? ? 本例娃循,使用工作日而不是非工作日來計算處理訂單的時期要公平得多。
? ? ? 事實證明斗蒋,這比簡單的減法更具挑戰(zhàn)性淮野。與前面的示例一樣捧书,日歷表包含一個列,該列定義出特定的日期:是否為工作日骤星。我們需要找到一種方法來使用該列经瓷,以計算這兩個日期之間的差異(以工作日表示)。
? ? ? 在數(shù)據(jù)模型中洞难,Calendar表用于按訂單日期舆吮、年份等對訂單進行切片。日歷和事實表之間的關(guān)系基于訂單日期队贱。為了解決這種情況色冀,需要停止將Calendar表視為維度(只用于篩選數(shù)據(jù)表),并認為Calendar表只是一個表柱嫌。使用它以不同的方式計數(shù)锋恬,而不是僅僅是維度篩選等。
? ? ? ? 例如编丘,可以在OrderDate和ShipDate之間計算Calendar表中的行數(shù)与学,這些行同時也是工作日。如果以這種方式表示該算法嘉抓,它將非乘魇兀快地通過DAX定義出計算列:
Sales[WorkinDaysHandling] = COUNTROWS(
FILTER(Date,
AND( AND(Date[Date] >= Sales[OrderDate],Date[Date] <= Sales[ShipDate]),
Date[Working Day] ="WorkDay")))? --并接第一個AND
? ? 相同算法的一種更優(yōu)雅的度量方式如下:
Sales[WorkinDaysHandling] =
CALCULATE(COUNTROWS ( Date ),
ALL(Date),
DATESBETWEEN( Date[Date], Sales[OrderDate],Sales[ShipDate]),
Date[WorkingDay] ="WorkDay")?
? ? ? 后一個公式使用了DATESBETWEEN函數(shù)構(gòu)建時期篩選:即訂貨時期與發(fā)貨時期之間的所有日期,結(jié)果是一個表抑片,然后使用它作為篩選器參數(shù)來篩選計算卵佛,以更改現(xiàn)有的WorkDay選擇。
? ? 計算靜態(tài)移動平均值
? ? ? 移動平均線是另一個常見的模式敞斋。本例提供一個如何以靜態(tài)方式計算移動平均值的示例截汪。例如,使用包含某些股票價格的股票數(shù)據(jù)模型植捎,想要計算過去50至200個期間的平均價格衙解。因為在股票市場上,一種常見的交易技巧是:觀察移動速度較快的平均線(超過50個周期)何時穿過移動到較慢的平均線(200個時段)鸥跟,以確定買入和賣出點丢郊。
? ? ? 此示例還要求盔沫,由于數(shù)據(jù)庫中的節(jié)假日和遺漏日(如未確定股票價格的天數(shù)等)医咨,50個時間段與固定天數(shù)不相對應(yīng)。這就需要考慮數(shù)據(jù)庫中的遺漏日架诞,并始終確保平均50個點的股票覆蓋有一個定義的價格拟淮。
? ? ? ? 本示例使用的數(shù)據(jù)模型非常簡單,僅包含一個日歷表和另一個包含股票收盤價的表谴忧,如圖6 -12所示很泊,其中沒有1月6 - 7日兩天的價格:
? ? ? ? 圖6-12對于假期和其他非工作日角虫,股票價格有幾個遺漏。
? ? ? 為了計算平均值委造,可以添加計算列戳鹅。
? ? ? 主要原因是想要在圖表上顯示這個平均值,這意味著DAX需要計算數(shù)百個值來繪制線條昏兆。由于計算這么多平均值的點可能是一個緩慢的操作枫虏,所以,可以將這些計算合并到計算列中爬虱,以便生成更快的圖表隶债。
? ? ? 這需要定義好移動平均值起始日期,這些日期可能因股票而異跑筝。我們來解決這個問題:
? ? 第一步:給表中的每一行分配一個數(shù)字死讹。
? ? ? 這個數(shù)字隨每個股票單而增加。因此曲梗,為第一個Microsoft的價格指定一個數(shù)字:1赞警,數(shù)字2代表Microsoft的第二個價格,以此類推稀并。對Apple等每個唯一值的行都執(zhí)行同樣的操作仅颇。結(jié)果可見于圖6-19。
? ? ? ? ? 圖6-13 DayNumber列是股票價格的頻次數(shù)字碘举。
? ? 該計數(shù)只需:對與當(dāng)前行相同的每一行時期計數(shù)即可忘瓦,使用EARLIER:
Prices[DayNumber] =
COUNTROWS(FILTER(Prices,? ? ? // FILTER()表AND(Prices[Date] <=EARLIER( Prices[Date]),
Prices[Stock] =EARLIER( Prices[Stock] )))) // 同時滿足的兩個條件篩選
? ? ? 第二步:計算移動平均值。
? ? ? 既然每一行都有一個索引符引颈,就很容易確定50或200個周期的邊界:只需取當(dāng)前DayNumber--天數(shù)耕皮,減去要考慮的期間數(shù),然后取確定行的日期就足夠了蝙场。
? ? ? 可以使用兩種不同的技術(shù)來構(gòu)建這個計算列凌停。第一個方案更容易理解,即使代碼不是最優(yōu)的售滤;第二個解決方案比較優(yōu)雅一些罚拟,最大的優(yōu)點是遵循了前面概述的非常簡單的DAX計值算法規(guī)律:
Prices[MovingAverage200] =
CALCULATE ( AVERAGE(Prices[Close]),
FILTER(ALL(Prices[Date]),
AND(Prices[Date]>=LOOKUPVALUE(Prices[Date],Prices[Stock],EARLIER(Prices[Stock]),Prices[DayNumber],
EARLIER(Prices[DayNumber]) –200),
Prices[Date]<=EARLIER(Prices[Date]))),
ALLEXCEPT( Prices,Prices[Stock]))
? ? ? 該公式的核心是最內(nèi)部的條件,它篩選當(dāng)前的日期與DayNumber-200之間的日期完箩。外部ALLEXCEPT需要將計算限制為同一股票的行赐俗。
? ? ? 該公式的第二個版本是一個更好的解決方案:
Prices[MovingAverage200]=
CALCULATE (
CALCULATE ( AVERAGE(Prices[Close]),
Prices[DayNumber]>=VALUES(Prices[DayNumber])- 200,Prices[DayNumber]<=VALUES(Prices[DayNumber])),
ALLEXCEPT( Prices,Prices[Stock],Prices[DayNumber]))
? ? ? 同樣,定義出:Prices[MovingAverage50] 度量(200改為50即可)
? ? ? 這個移動平均值使用了兩個嵌套CALCULATE弊知。這里略過原內(nèi)容阻逮,其實使用DAX的萬能公式模式:關(guān)系+篩選+條件很好理解,如圖:
? ? ? 事實上秩彤,第一個紅框內(nèi):第二個CALCULATE定義的是一個度量值列表叔扼,兩個條件篩選下的求平均值的度量事哭,該度量做為第一個CALCULATE的第一個參數(shù)载矿,然后再放置了一個關(guān)系列表篩選條件(它針對ALLEXCEPT()定義的其中兩列與第二個CALCULATE()度量列表構(gòu)成新關(guān)系的計算列集峭咒,參與計算)。
? ? ? ? 三個度量很容易定義出來:
? ? ? [AVG 200] := AVERAGE( Prices[MovingAverage200] )
? ? ? [AVG 50]? := AVERAGE ( Prices[MovingAverage50] )
? ? ? [AVG Close]:= AVERAGE ( Prices[Close] )
? ? ? 最后措嵌,使用度量來顯示某個圖表与柑,該圖表為每只股票指示移動平均線交點處的買賣點流炕,如圖6-20所示:
? ? ? ? ? ? ? 圖6-20移動平均線是有用的,以顯示一個股票的隨著時間的推移方向仅胞。