PowerBI & Excel CEO 終極駕駛艙 – 第二彈 – 綜合近期與歷史分析
- 2019 年 10 月 6 日
- 筆記

長期關注PowerBI戰友聯盟的戰友會發現,我們現在的很多文章出現了連載的跡象。我們在此前的文章以及系統化的影片教程中已經講解了PowerBI及DAX基礎部分,我們的文章將不斷基於這些基礎給出非常現實的設計。每篇文章可能會以及此前的文章,並重點解決某類痛點,最後給出一個綜合的標準實現。
當您下載到標準實現後,也許您直接看到這個設計是不容易理解的,因為它不是點一點出來了,是基於大量基礎和優化過的思想進行的,需要您結合對應文章提到的前序文章和實現來理解。本文繼續。
最終效果
CEO需要實時和歷史地追蹤和分析不同元素(可能是:人,產品,BU,市場等)的變化以進行實時決策。

我們簡單解讀一下:對於銷售額,這裡考察經理楚傑在相對於報表刷新日期前一天(設為:基準日)的業績表現;左邊是相對於基準日的MTD的表現;右邊是歷史不同月份的表現;非常清晰地看到了楚傑對銷售額完成的全景。
這張圖表的資訊量之大,資訊密集度之高,用到PowerBI以及DAX技巧之妙,先讓我們有一個感覺。我們得到這樣的結果是一步步進行的,下面仔細地來描述。
建議您在看下文時稍作停留,您可以觀察和思考下,上圖中可能涉及到的PowerBI或DAX的難點,如果您的思考都在後文里,說明您可以解構一個設計了,當然,能做到不看後文而完全解構的可能性太小了。
前情回顧
對於CEO查看各種指標,我們此前兩篇文章已經給出了不錯的開始,可以參考:
《PowerBI 零基礎支援上百指標多日期切換分析模板》以及《Excel CEO 終極駕駛艙 – 第一彈》。
在其中:

以及:

這很好。存在一個問題:
大部分 CEO 並不是在一個月(季度,周)結束時才關注目標的進展,他們需要隨時可以看到項目當前的進展。
舉個例子,周1開會,對於用戶增長這一指標,往往知道本月的指標,還需要知道到了周3,本周的周1到周3相對上周的周1到周3是否有所提升,如果沒有提升,說明策略很可能是有問題的,需要及時調整,不可以等到下周1再開會,更不可以等到下個月再開會。這可以定義為部分數據驅動驅動的目標管理。
再比如,預定的一個季度的銷售指標,當前的MTD需要與上月的MTD做及時的對比,而不是等到這個月結束再對比。
那麼,在如上的歷史分析圖表中就無法應對了。
因此,我們需要一種相對日期區間的分析,相對的參考點就是今天。從今天看:今天,昨天,本周至今,本月至今,本季至今,本年至今。
PowerBI 的不足
在給出我們正確的解決方案前,我們來看看在PowerBI中可以做什麼,是否可以解決這個問題。在PowerBI中,如果拖拽日期維度進入畫布,確實可以設置相對日期,如下:

我們打開日曆,來對照下:

我們會發現:PowerBI給出的日曆周是從周日開始的。這還不是最大的問題,最大的問題是這裡的相對日期並沒有TD(To Date)的概念,例如:WTD指的是本周一至今;MTD指的是本月一日至今。
因此,PowerBI的相對日期區間比較受限。為此,我們需要構建我們自己的相對日期架構。
超級靈活的相對日期架構XTD
除了不再重複 PowerBI 的相對日期外,我們給出一套更有意義的相對日期XTD架構設計。所謂XTD就是DTD,WTD,MTD,QTD,YTD。含義為:當日,當周1日至當日,當月1日至當日,當季1日至當日,當年1日至當日。
值得強調的是:當【X區間】的1日至當日 中的 「當」 字是非常精確的說法。不是本年至今,更不是本月至今。本年至今,從語義上看,是現實世界的時間;而我們所說的 當期的1日至當日 並不一定是現實世界的時間。如果您根本對此沒有感覺,那本文您將收穫頗豐;如果您完全明白我們的意思,說明您對日期智慧(準確講不是時間智慧,而是日期智慧)有了深入的思考和理解。
首先,我們看看效果,再做解釋:

可以看到,我們需要同時考慮的事情包括:
- 相對於刷新日期還是現實日期。本例中,刷新日期:2019.07.14而現實日期是今日。
- 截止到最後一日還是最後一日的上一日。在實際中,最後一日可能是數據不全的,因此可能需要上一日。
- 區間類型是XTD。需要實現6種。
- 同比上期。需要實現5種。
我們來看下DAX實現:
DatesPeriod.XTD = VAR vToday = TODAY() VAR vLastUpdate = [System.LastUpdate] VAR Period_Today = VAR X = TREATAS( { vToday } , 'DatesTemplate'[Date] ) VAR PX = TREATAS( { vToday - 1 } , 'DatesTemplate'[Date] ) VAR TodayX = ADDCOLUMNS( LASTDATE( X ) , "區間類型" , "當日" , "截止到" , "最後一日" , "區間類型排序" , 1 ) VAR TodayPX = ADDCOLUMNS( LASTDATE( PX ) , "區間類型" , "當日" , "截止到" , "上一日" , "區間類型排序" , 1 ) VAR YesterdayX = ADDCOLUMNS( PREVIOUSDAY( X ) , "區間類型" , "上一日" , "截止到" , "最後一日" , "區間類型排序" , 2 ) VAR YesterdayPX = ADDCOLUMNS( PREVIOUSDAY( PX ) , "區間類型" , "上一日" , "截止到" , "上一日" , "區間類型排序" , 2 ) VAR WTDX = ADDCOLUMNS( DATESINPERIOD( 'DatesTemplate'[Date] , X , - WEEKDAY( X , 2 ) + 1 , DAY ) , "區間類型" , "WTD" , "截止到" , "最後一日" , "區間類型排序" , 3 ) VAR WTDPX = ADDCOLUMNS( DATESINPERIOD( 'DatesTemplate'[Date] , PX , - WEEKDAY( PX , 2 ) + 1 , DAY ) , "區間類型" , "WTD" , "截止到" , "上一日" , "區間類型排序" , 3 ) VAR MTDX = ADDCOLUMNS( DATESMTD( X ) , "區間類型" , "MTD" , "截止到" , "最後一日" , "區間類型排序" , 4 ) VAR MTDPX = ADDCOLUMNS( DATESMTD( PX ) , "區間類型" , "MTD" , "截止到" , "上一日" , "區間類型排序" , 4 ) VAR QTDX = ADDCOLUMNS( DATESQTD( X ) , "區間類型" , "QTD" , "截止到" , "最後一日" , "區間類型排序" , 5 ) VAR QTDPX = ADDCOLUMNS( DATESQTD( PX ) , "區間類型" , "QTD" , "截止到" , "上一日" , "區間類型排序" , 5 ) VAR YTDX = ADDCOLUMNS( DATESYTD( X ) , "區間類型" , "YTD" , "截止到" , "最後一日" , "區間類型排序" , 6 ) VAR YTDPX = ADDCOLUMNS( DATESYTD( PX ) , "區間類型" , "YTD" , "截止到" , "上一日" , "區間類型排序" , 6 ) RETURN UNION( TodayX , TodayPX , YesterdayX , YesterdayPX , WTDX , WTDPX , MTDX , MTDPX , QTDX , QTDPX , YTDX , YTDPX ) VAR Period_LastUpdate = VAR X = TREATAS( { vLastUpdate } , 'DatesTemplate'[Date] ) VAR PX = TREATAS( { vLastUpdate - 1 } , 'DatesTemplate'[Date] ) VAR TodayX = ADDCOLUMNS( LASTDATE( X ) , "區間類型" , "當日" , "截止到" , "最後一日" , "區間類型排序" , 1 ) VAR TodayPX = ADDCOLUMNS( LASTDATE( PX ) , "區間類型" , "當日" , "截止到" , "上一日" , "區間類型排序" , 1 ) VAR YesterdayX = ADDCOLUMNS( PREVIOUSDAY( X ) , "區間類型" , "上一日" , "截止到" , "最後一日" , "區間類型排序" , 2 ) VAR YesterdayPX = ADDCOLUMNS( PREVIOUSDAY( PX ) , "區間類型" , "上一日" , "截止到" , "上一日" , "區間類型排序" , 2 ) VAR WTDX = ADDCOLUMNS( DATESINPERIOD( 'DatesTemplate'[Date] , X , - WEEKDAY( X , 2 ) + 1 , DAY ) , "區間類型" , "WTD" , "截止到" , "最後一日" , "區間類型排序" , 3 ) VAR WTDPX = ADDCOLUMNS( DATESINPERIOD( 'DatesTemplate'[Date] , PX , - WEEKDAY( PX , 2 ) + 1 , DAY ) , "區間類型" , "WTD" , "截止到" , "上一日" , "區間類型排序" , 3 ) VAR MTDX = ADDCOLUMNS( DATESMTD( X ) , "區間類型" , "MTD" , "截止到" , "最後一日" , "區間類型排序" , 4 ) VAR MTDPX = ADDCOLUMNS( DATESMTD( PX ) , "區間類型" , "MTD" , "截止到" , "上一日" , "區間類型排序" , 4 ) VAR QTDX = ADDCOLUMNS( DATESQTD( X ) , "區間類型" , "QTD" , "截止到" , "最後一日" , "區間類型排序" , 5 ) VAR QTDPX = ADDCOLUMNS( DATESQTD( PX ) , "區間類型" , "QTD" , "截止到" , "上一日" , "區間類型排序" , 5 ) VAR YTDX = ADDCOLUMNS( DATESYTD( X ) , "區間類型" , "YTD" , "截止到" , "最後一日" , "區間類型排序" , 6 ) VAR YTDPX = ADDCOLUMNS( DATESYTD( PX ) , "區間類型" , "YTD" , "截止到" , "上一日" , "區間類型排序" , 6 ) RETURN UNION( TodayX , TodayPX , YesterdayX , YesterdayPX , WTDX , WTDPX , MTDX , MTDPX , QTDX , QTDPX , YTDX , YTDPX ) RETURN UNION( ADDCOLUMNS( Period_Today , "相對日期" , "現實日期" ), ADDCOLUMNS( Period_LastUpdate , "相對日期" , "刷新日期" ) )
這裡不做過多解釋,因為這裡的DAX並不複雜,僅僅用來拼裝這個結構。得到:

再來看看數據模型。
數據模型
對於日期的使用,您將看到史上近乎終極的日期模型:

對關鍵進行說明:
- 【必】不直接創建日期表,而是創建一個日期表模板。(原因不展開,記住就行)
- 日期表 = 日期表模板。
- 日期區間表,並與日期表構建雙向篩選。
- 日期區間表(XTD版),並與日期表構建雙向篩選。
值得強調的是(以下針對高手):
- 日期表模板是必須的,避免對日期表的循環依賴。
如果您不明白上面的內容,不要緊,照著做就可以了。
其實本案例的數據模型和日期模型並不複雜,只是要穿透這種複雜性,就需要超越這種複雜性去理解透徹日期表和篩選背後的本質,這塊內容太過技術,我們不再展開,最終形成的結構卻是簡單的。
處理複雜的日期篩選
當您使用度量值的時候,要非常清楚這個度量值受到三套日期結構的篩選(如上圖)。為此,在構建一個重要的度量值PX(上期)時,我們的精妙處理是這樣的:
KPI.PX = VAR vPeriodType = SELECTEDVALUE( DatesPeriod[區間類型] ) RETURN SWITCH( TRUE() , vPeriodType = "年" , CALCULATE( [KPI] , SAMEPERIODLASTYEAR( Dates[Date] ) , ALL( DatesPeriod ) , ALL( 'DatesPeriod.XTD' ) ) , vPeriodType = "季" , CALCULATE( [KPI] , DATEADD( Dates[Date] , -1 , QUARTER ) , ALL( DatesPeriod ) , ALL( 'DatesPeriod.XTD' ) ) , vPeriodType = "月" , CALCULATE( [KPI] , DATEADD( Dates[Date] , -1 , MONTH ) , ALL( DatesPeriod ) , ALL( 'DatesPeriod.XTD' ) ) , vPeriodType = "周" , CALCULATE( [KPI] , DATEADD( Dates[Date] , -7 , DAY ) , ALL( DatesPeriod ) , ALL( 'DatesPeriod.XTD' ) ) , vPeriodType = "日" , CALCULATE( [KPI] , DATEADD( Dates[Date] , -1 , DAY ) , ALL( DatesPeriod ) , ALL( 'DatesPeriod.XTD' ) ) , BLANK() )
即:需要用ALL取消對兩個日期區間表的篩選,這裡涉及到對CALCULATE參數的精確理解,不再展開。但我們可以給出一個結論:使用CALCULATE可以構建任意複雜的篩選結構。這樣,我們就有了正確計算PX的度量值。
度量值
我們簡單解釋一下度量值的安排:

其實非常簡單:
KPI = VAR vKPIName = SELECTEDVALUE( 'KPI參數'[KPI名稱] ) RETURN SWITCH( TRUE() , vKPIName = "銷售額" , [Core.Sales] , vKPIName = "利潤" , [Core.Profit] , vKPIName = "利潤率%" , [Core.Profit%] , vKPIName = "數量" , [Core.Volume] , [Core.Sales] )
對於 KPI.PX 已經給出,不再贅述。
PowerBI 的篩選控制
如果您親自實踐,會遇到奇怪的問題,我們簡單解釋一下您可能遇到的問題以及如何處理:

很明顯,上圖中紅色的篩選是一個整體;黃色的篩選是一個整體。而在實際中,他們會交叉影響的,這是PowerBI作為BI產品的交叉篩選的默認特性。我們需要簡單進行設置以實現定向的精確控制,如下:

對於每個篩選器,我們都要精確設置其影響的範圍。
什麼鬼?沒見過?請系統化學習我們出品的《PowerBI商業智慧分析基礎系列》。
能做好的設計的前提是非常堅實的基礎。
至此,整個設計就OK了。
用IBCS進行標準化
這裡直接使用了滿足IBCS商業圖表標準的ZebraBI for PowerBI做標準化展現,如下:

即使沒有這些插件,也可以使用PowerBI的內置圖表來完成,就不再贅述。
關於商業圖表標準化,請參考我們的三篇雄文以及系統化課程《PowerBI & Excel商業圖表標準》。 參考: 真正的商業圖表可視化之道-佈道篇 真正的商業圖表可視化之道-實踐篇 真正的商業數據可視化之道-工具篇
用局部切換來增強資訊密度
細心的夥伴會發現:

沒錯,這確實是一個按鈕,點擊後:

在圖表與數據之間切換,便於CEO在左腦和右腦之間切換。CEO誇張到:太貼心了,就像開著寶馬在運動檔和手動檔的切換,真沒想到PowerBI可以這樣。
體驗CEO駕駛的快感
綜合上述所有內容,我們就可以得到最終的結果(動畫或影片):
CEO可以暢快地考察不同的人以及隨時跟蹤各種表現。
總結
從技術上看,本文涉及PowerBI及DAX的知識點包括:
- 日期表
- 日期區間
- 日期區間(XTD)
- CALCULATE靈活的取消篩選
- 動態指標KPI
- 雙向篩選
- 篩選器的作用範圍
- IBCS & ZebraBI
基於這些技術知識點可以構造以下的業務價值:
- 靈活動態的歷史區間
- 靈活動態的XTD區間
- 動態的KPI
- 任意的篩選以鎖定最近以及歷史表現以決策
總之,真正的簡潔是要基於堅實的基礎穿透複雜再形成的。世界上只有青藏高原而沒有電線杆。
本文案例文件已經盡享至訂閱會員專區,祝您玩得愉快。
——
擴展閱讀:
真正的商業圖表可視化之道-佈道篇 真正的商業圖表可視化之道-實踐篇 真正的商業數據可視化之道-工具篇 PowerBI 零基礎支援上百指標多日期切換分析模板 Excel CEO 終極駕駛艙 – 第一彈 相關基礎課程:
《PowerBI自助商業智慧分析基礎系列》 《PowerBI DAX基礎系列》 《PowerBI DAX實戰系列》 《PowerBI & Excel 商業圖表可視化標準》
歡迎交流