PowerBI 全網(wǎng)首發(fā)原生平滑曲線 - 通用模板及應(yīng)用

書接上回,我們研究了折線如何變成曲線,如下:

并得到了最終的終極方案:

它滿足了:
足夠簡單
連續(xù)光滑
性能夠好
那么問題來了:
要單獨構(gòu)建兩個輔助表
是否可以讓 X 軸不顯示序號而是真實信息
也就是通用化問題。
答案是非??隙ǖ?。
效果
我們都知道,在給出年月計算新老客戶以及活躍用戶數(shù)是相對比較復(fù)雜的計算,我們來看看最終效果:

這個效果完全滿足了需求。
請觀察幾個點:
2017年01月,活躍用戶由純新用戶供應(yīng)49人,老用戶0人。
2017年11月,活躍用戶50人,分別對應(yīng)新用戶21人與老用戶29人。
2019年05月,活躍用戶100人,分別對應(yīng)新用戶13人與老用戶87人。
2019年07月,活躍用戶35人,新用戶0人,完全由老用戶供應(yīng)。
整套曲線的顯示很完美。
再觀賞坐標(biāo)軸,圖例,曲線顏色,標(biāo)簽,標(biāo)簽背景顏色都完美配合。
這的確實現(xiàn)了想要的一切,這套曲線是連續(xù)光滑的,避免了折線圖的生硬。
如果是折線圖,會是這樣:

不對比不知道,一對比,就看出平滑曲線的優(yōu)雅了。
上文有伙伴留言:
Excel 里點一下就好了
Tableau 里點一下就好了
沒有錯。
這兩句話是用來懟BI佐羅好還是用來懟PowerBI產(chǎn)品組好呢?
明明是 PowerBI 產(chǎn)品組應(yīng)該做的事,偏偏不做,把時間用來研究 PowerBI 的logo不叫l(wèi)ogo叫Icon這種問題,而 BI佐羅 只能通過這么間接的方法來彌補(bǔ)了 PowerBI 的硬傷,反而撈不到什么好。一定要注意:這是 PowerBI 的問題。
那么我們這么做有意義嗎?有的。
處于學(xué)習(xí) PowerBI 尤其是 DAX 的階段,這樣的問題的解決可以極度提升個人的 DAX 以及 PowerBI 能力,而不是曲線問題本身。
很多人學(xué)習(xí) PowerBI 以及學(xué)習(xí) DAX,可以在這些問題中得以思考并實現(xiàn)是很有價值的。
最重要的是,本文要從根本上給出通用方法。
通用實現(xiàn)
一個問題的解決,并不是最難的,最難的在于:
通用化和可擴(kuò)展化,適用于所有場景
以更高的性能運(yùn)行與折線圖不應(yīng)該有任何性能差異
這兩點居然被完美地解決了。
首先,來看方法的通用化。
如果我們的 X 軸是 0,1,2,...,N,那么,我們只需要這么做:
將 X 軸演化為:0.01,0.02,....1.00,1.01,1.02,...,2.00,2.01,2.02,...,3.00,4.00,...,N.00 即可。這樣 X 軸就以10倍或者20倍拉伸除了更多的點。我們只要在這些點計算出值,并用純折線圖連接,由于點很多,看著就是平滑的曲線了。
但是,如果我們的 X 軸是年,月,甚至是年,月的層級怎么辦?
如果您學(xué)習(xí)過《BI真經(jīng)》就可以知道我們可以創(chuàng)建一個定制 X 軸,并保留年月層級同時做到擴(kuò)大20倍,如下所示:

可以看出:
2017-01 的 X 是 0,而有 0.00 到 0.95 間隔 0.05 步長的 20 個點
X 和 X' 與該維度融合,無需另外制作
將這兩個特定整合到一起真的太強(qiáng)大了,也就意味著:
當(dāng)且僅當(dāng)需要為某個折線圖定制它的光滑曲線版本時,才進(jìn)行僅一次維度定制即可。
接著,維度定制的方法存在通用性嗎?
答案是肯定的。其通用算法如下:
Smooth.X.CRM.Date =
VAR _base =
VAR _last = EOMONTH( [Start:Date.LastDate.All] , -1 )
RETURN
CALCULATETABLE(
SUMMARIZE( 'Calendar' , [YearNumber] , [MonthNumber] ),
'Calendar'[Date] <= _last
)
VAR _base_indexKey =
ADDCOLUMNS(
_base,
"@IndexKey" , [YearNumber] * 100 + [MonthNumber]
)
// no need to change the DAX below:
VAR _index_temp = SELECTCOLUMNS( _base_indexKey , "@IndexKey" , [@IndexKey] )
VAR _base_index = SUBSTITUTEWITHINDEX( _base_indexKey , "X" , _index_temp , [@IndexKey] , ASC )
VAR _table_ex =
FILTER(
GENERATEALL( _base_index , SELECTCOLUMNS( GENERATESERIES( 0 , 19 ) , "X'" , [x] + [Value] / 20 ) ) ,
[X'] <= MAXX( _base_index , [X] )
)
RETURN _table_ex框架思路如下:
準(zhǔn)備基本維度
將基本維度擴(kuò)展出通用索引 X 以及 X'
其中,第一步需要編寫,而第二步已經(jīng)被模板化,無需做任何修改。
需要修改的 DAX 僅僅為:
VAR _base =
VAR _last = EOMONTH( [Start:Date.LastDate.All] , -1 )
RETURN
CALCULATETABLE(
SUMMARIZE( 'Calendar' , [YearNumber] , [MonthNumber] ),
'Calendar'[Date] <= _last
)
VAR _base_indexKey =
ADDCOLUMNS(
_base,
"@IndexKey" , [YearNumber] * 100 + [MonthNumber]
)也就是構(gòu)建了一個在合理時間范圍內(nèi)的 年 月 表而已,這也是本來就必須要做的。
而將基本維度擴(kuò)展出通用索引 X 以及 X' 的 DAX 無需做任何修改,已經(jīng)神奇地做了通用實現(xiàn)。
沒有《BI真經(jīng)》作為基礎(chǔ),看不懂是很正常的,請學(xué)習(xí)《BI真經(jīng)》。
那么,現(xiàn)在維度已經(jīng)好了。
再來看度量值如何做才能:
通用化
高性能
這里我們借助《BI真經(jīng)》給出的通用視圖性能提升模式,給出通用化實現(xiàn),如下:
CRM.User.New.Smooth =
VAR _x_curr = SELECTEDVALUE( 'Smooth.X.CRM.Date'[X] ) // 指定表
VAR _dx = SELECTEDVALUE( 'Smooth.X.CRM.Date'[X'] ) - SELECTEDVALUE( 'Smooth.X.CRM.Date'[X] ) // 指定表
VAR _view_table =
CALCULATETABLE(
ADDCOLUMNS(
SUMMARIZE( 'Smooth.X.CRM.Date' , [X] , [YearNumber] , [MonthNumber] ) , // 新維度
"Y" ,
CALCULATE(
[CRM.User.New] , // 原始度量值
TREATAS(
{ ( [YearNumber] , [MonthNumber] ) } , // 新維度
'Calendar'[YearNumber] , 'Calendar'[MonthNumber] // 原始維度
)
)
)
,
ALLSELECTED( )
)
// ------以下模板無需修改----------
VAR _y_min = MAXX( FILTER( _view_table , [X] = MINX( _view_table , [X] ) ) , [Y] )
VAR _y_max = MAXX( FILTER( _view_table , [X] = MAXX( _view_table , [X] ) ) , [Y] )
VAR _y0 = COALESCE( MAXX( FILTER( _view_table , [X] = _x_curr - 1 ) , [Y] ) , _y_min )
VAR _y1 = MAXX( FILTER( _view_table , [X] = _x_curr + 0 ) , [Y] )
VAR _y2 = MAXX( FILTER( _view_table , [X] = _x_curr + 1 ) , [Y] )
VAR _y3 = COALESCE( MAXX( FILTER( _view_table , [X] = _x_curr + 2 ) , [Y] ) , _y_max )
RETURN
VAR _u1 = _dx
VAR _u2 = _dx * _dx
VAR _a0 = -0.5 * _y0 + 1.5 * _y1 - 1.5 * _y2 + 0.5 * _y3
VAR _a1 = _y0 - 2.5 * _y1 + 2 * _y2 - 0.5 * _y3
VAR _a2 = -0.5 * _y0 + 0.5 * _y2
VAR _a3 = _y1
RETURN _a0 * _u1 * _u2 + _a1 * _u2 + _a2 * _u1 + _a3本來是復(fù)雜的度量值邏輯計算,在這里也做到了:
高度通用化,僅僅格式化的修改幾個位置
用了視圖層模式,以及對該問題的定制優(yōu)化,性能非???/p>
這樣,就完美地解決了度量值計算的問題。
理解視圖層通用模式需要學(xué)習(xí)《BI真經(jīng)》。
總結(jié)
本文給出了折線圖的平滑曲線版本的完美通用實現(xiàn)以及所有的 DAX 細(xì)節(jié)。需要《BI真經(jīng)》作為基礎(chǔ)方能領(lǐng)悟其中的各種妙處。
從本文中不難看出在解決問題到通用化的過程中的通用模式,也就是模式的模式:
問題到模板
維度的通用化構(gòu)建方法
維度的可變部分,用套路改有限的參數(shù)
維度的不變部分,模板化實現(xiàn),永不修改
度量值的通用化頭肩方法
度量值的可變部分,用套路改有限的參數(shù)
度量值的不變部分,模板化實現(xiàn),永不修改
如果你學(xué)習(xí)過《BI真經(jīng)》的《PBI高級》,你就可以看出,這里嚴(yán)格遵守了 OCP 原則(開放閉合原則)給出了穩(wěn)定需求和可變需求的分離,讓實現(xiàn)具備了通用性。
在選擇10個點,20個點還是更多點作為插值元素方面以及索引從 0 開始而不是從 1 開始等很多細(xì)節(jié)都經(jīng)過了極為巧妙地推演,讀者可以自行研究,此處就不再贅述。
再來回顧最終的結(jié)果:

致敬:大學(xué)本科二年級《數(shù)學(xué)分析》第四章 - 函數(shù)的連續(xù),曲線的光滑。
既然可以用于這個新老用戶訪問趨勢圖,本方案便可以用于任何曲線場景,完美通用實現(xiàn)。
缺乏 DAX 基礎(chǔ)和數(shù)學(xué)基礎(chǔ)的伙伴認(rèn)為本文比較復(fù)雜也很正常,但在實際應(yīng)用層面,僅僅是改幾個參數(shù)的問題,已經(jīng)可以無腦復(fù)制,但問題是如果您沒有《BI真經(jīng)》的學(xué)習(xí),無腦復(fù)制也是有難度的。
最后,強(qiáng)烈建議有緣的你,如果正在學(xué)習(xí) PowerBI 請學(xué)習(xí)《BI真經(jīng)》,它可以推演出所有在 PowerBI 中需要的內(nèi)容。
在訂閱了BI佐羅講授的《BI真經(jīng)》之《BI進(jìn)行時》課程區(qū),除了可以下載本文案例,還可以觀看視頻講解。
在視頻講解中,會更加詳盡地介紹一些細(xì)節(jié),連前幾個視頻將于 2 月初統(tǒng)一更新。
PowerBI 全網(wǎng)首發(fā)原生平滑曲線 - 原理及實現(xiàn)

讓數(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í),驗證碼:data2021
PowerBI MVP 帶你正確而高效地學(xué)習(xí) PowerBI
點擊“閱讀原文”,即刻開始
↙
