一、背景
0.0 神說,要有正態分布,於是就有了正態分布。
0.1 神看正態分布是好的,就讓隨機誤差都隨了正態分布。
0.2 正態分布的奇妙之處,就是許多看似隨機事件竟然服從一個表達式就能表達的分布,如同上帝之手特意為之。
神覺得拋硬幣是好的,於是定義每個拋出硬幣正面記+1分,反面記-1分。創世紀從0分開始,神只拋1次硬幣,有2種可能:一半的概率+1分,一半的概率-1分。此時概率分布大概是這樣的:

神決定扔10個硬幣,此時概率分布如下:

如果畫圖來感受,數據分布大概如下:

如果是100個,甚至是無窮多個呢?平均分數分布情況大概是什么樣呢?畫個圖感受一下:

——《創世紀·數理統計·正態分布的前世今生》
開頭摘自統計學中非常經典的一本書籍,由此可見正態分布是非常經典和隨處可見的,為什么正態分布這么常見呢?因為通常情況下,一個事物的影響因素都是多個,好比每個人的學習成績,受到多個因素的影響,比如:
- 本人的智商情況。
- 上課聽講的認真程度,課前的預習程度,與老師的互動程度。
- 課后是否及時復習,有沒有及時溫習知識點呢,有沒有做好作業鞏固。
每一天的因素,每天的行為,對於學生的成績不是產生正面因素就是負面因素,這些因素對於成績的影響不是正面就是負面的,反復累計加持就像上圖的拋硬幣一樣,讓成績最后呈現出正態分布。數據呈現正態分布其實背后是有中心極限定理原理支持,根據中心極限定理,如果事物受到多種因素的影響,不管每個因素單獨本身是什么分布,他們加總后結果的平均值就是正態分布。
二、引用
正是因為日常分析工作中數據呈現是正態分布的,處於兩個極端的值往往是異常的,與我們挑選異常值天然契合。在業務方尋求一種自動監控方案的過程中,我們選擇了該方案。根據數據分析工作中,結合統計學的數據閾值分布原理,通過自動划分數據級別范圍,確定異常值,如下圖箱線圖,箱線圖是一個能夠通過5個數字來描述數據的分布的標准方式,這5個數字包括:最小值,第一分位,中位數,第三分位數,最大值,箱線圖能夠明確的展示離群點的信息,同時能夠讓我們了解數據是否對稱,數據如何分組、數據的峰度。

箱線圖是一個能夠通過5個數字來描述數據的分布的標准方式,這5個數字包括:最小值,第一分位,中位數,第三分位數,最大值,箱線圖能夠明確的展示離群點的信息,同時能夠讓我們了解數據是否對稱,數據如何分組、數據的峰度,對於某些分布/數據集,會發現除了集中趨勢(中位數,均值和眾數)的度量之外,還需要更多信息。

(圖片來源於網絡)
需要有關數據變異性或分散性的信息。箱形圖是一張圖表,它很好地指示數據中的值如何分布,盡管與直方圖或密度圖相比,箱線圖似乎是原始的,但它們具有占用較少空間的優勢,這在比較許多組或數據集之間的分布時非常有用。——適用於大批量的數據波動監控。

(圖片來源於網絡)
箱線圖是一種基於五位數摘要(“最小”,第一四分位數(Q1),中位數,第三四分位數(Q3)和“最大”)顯示數據分布的標准化方法。
- 中位數(Q2 / 50th百分位數):數據集的中間值;
- 第一個四分位數(Q1 / 25百分位數):最小數(不是“最小值”)和數據集的中位數之間的中間數;
- 第三四分位數(Q3 / 75th Percentile):數據集的中位數和最大值之間的中間值(不是“最大值”);
- 四分位間距(IQR):第25至第75個百分點的距離;
- 晶須(藍色顯示);
- 離群值(顯示為綠色圓圈);
- “最大”:Q3 + 1.5 * IQR;
- “最低”:Q1 -1.5 * IQR。

(圖片來源於網絡)
上圖是近似正態分布的箱線圖與正態分布的概率密度函數(pdf)的比較, 兩側0.35%的數據就能夠被視為異常數據。
回到這次的監控方案,由中位數向兩邊擴散,划分一級二級三級四級五級數據,傳入連續時間段內指標的同環比,根據同環比分布的區間確定四個異常類型:異常上漲(同環比分布同時大於等於正三級)、異常(同環比分布在一正一負大於等於三級的范圍)、異常下降(同環比分布低於等於負三級)、無異常(同環比分布低於三級的范圍)。
三、落地
實現三部曲
1. 代碼實現
/* *數據分析API服務 */ public class DataAnalysis{ /* *波動分析 *input:json,分析源數據(樣例) { "org_data": [ { "date":"2020-02-01", "data":"10123230" }, 日期類型、long類型 { "date":"2020-02-02", "data":"9752755" }, { "date":"2020-02-03", "data":"12123230" }, ....... ] } *output:json,分析結果 { "type": 1, --調用正常返回1,異常返回0 "message":"", --異常原因 "date": 2020-02-14,--輸入數據中按日期升序排列的最后一組的日期 "data": 6346231,--輸入數據中按日期升序排列的最后一組的數據值 "rate1": -0.3,--同比值 "rate2": -0.6,--環比值 "level1": 4,--同比等級,5個類型:1、2、3、4、5 "level2": 3,--環比等級,5個類型:1、2、3、4、5 "result":"異常下降",--四個類型:異常上漲、異常、異常下降、無異常 } */ public String fluctuationAnalysis (String org_data){ //第一步,校驗輸入數據 if(checkOrgdata(org_data)) return {"result": 0, "message":""} //第二步,計算同環比 computeOrgdata(org_data) //第三步,數據升序排序,獲取數組大小、最后數據中按日期升序排列的最后一組 //以上面樣例為了,數組大小14,最后一組數據{ "date":"2020-02-14", "data":"6346231" ,同比:-0.3,環比:-0.6}
對同比、環比、(data暫不做)分別做如下處理
------- //第四步,按照數據升序排序及數據大小,將數據(不算上面找出的最后一組數據)平均分為4等份(這樣會在數組中插入三個樁),並計算出第一個樁和第三個樁的值,以上面為例子,原來數組大小14,去掉最后一個,13個 //判斷,給結論
(1) 如果同比等級> 2 and 環比等級 > 2 (表示一定有異常)
/* *數據分析API服務 */ public class DataAnalysis{ /* *波動分析 *input:json,分析源數據(樣例) { "org_data": [ { "date":"2020-02-01", "data":"10123230" }, 日期類型、long類型 { "date":"2020-02-02", "data":"9752755" }, { "date":"2020-02-03", "data":"12123230" }, ....... ] } *output:json,分析結果 { "type": 1, --調用正常返回1,異常返回0 "message":"", --異常原因 "date": 2020-02-14,--輸入數據中按日期升序排列的最后一組的日期 "data": 6346231,--輸入數據中按日期升序排列的最后一組的數據值 "rate1": -0.3,--同比值 "rate2": -0.6,--環比值 "level1": 4,--同比等級,5個類型:1、2、3、4、5 "level2": 3,--環比等級,5個類型:1、2、3、4、5 "result":"異常下降",--四個類型:異常上漲、異常、異常下降、無異常 } */ public String fluctuationAnalysis (String org_data){ //第一步,校驗輸入數據 if(checkOrgdata(org_data)) return {"result": 0, "message":""} //第二步,計算同環比 computeOrgdata(org_data) //第三步,數據升序排序,獲取數組大小、最后數據中按日期升序排列的最后一組 //以上面樣例為了,數組大小14,最后一組數據{ "date":"2020-02-14", "data":"6346231" ,同比:-0.3,環比:-0.6}
同比=(今日data/上周同日data-1)*100%
return { "org_data": [ { "date":"2020-02-01", "data":"10123230" ,同比:null,環比:null }, { "date":"2020-02-02", "data":"9752755" ,同比:null,環比:0.9}, { "date":"2020-02-03", "data":"12123230" ,同比:null,環比:0.9}, ....... ] } }
對於輸入數據的幾個關鍵點:
(1)要求是連續的日期,並且至少14天的數據,建議100天的數據
(2)api中當同環比計算為null時,統一處理為0
(3)當傳入的數量大於90天,取最近90天作為樣本,當數量小於90天,拿所有上傳作為樣本
2. API封裝
(1)提供已封裝好的API服務為大家使用:
API使用:
傳入數據示例(json)
{ "org_data": [ { "date":"2020-02-01", "data":"10123230" ,同比:null,環比:null }, { "date":"2020-02-02", "data":"9752755" ,同比:null,環比:0.9}, { "date":"2020-02-03", "data":"12123230" ,同比:null,環比:0.9}, ..... ] }
返回結果解釋:
{ "type": 1, --調用正常返回1,異常返回0 "message":"", --異常原因 "date": 2020-02-14,--輸入數據中按日期升序排列的最后一組的日期 "data": 6346231,--輸入數據中按日期升序排列的最后一組的數據值 "rate1": -0.3,--同比值 "rate2": -0.6,--環比值 "level1": 4,--同比等級,5個類型:1、2、3、4、5 "level2": 3,--環比等級,5個類型:1、2、3、4、5 "result":"異常下降",--四個類型:異常上漲、異常、異常下降、無異常 }
注意問題點:
對於輸入數據的幾個關鍵點:
(1)要求是連續的日期,並且至少14天的數據,建議100天的數據
(2)api中當同環比計算為null時,統一處理為0
(3)當傳入的數量大於90天,取最近90天作為樣本,當數量小於90天,拿所有上傳作為樣本
3. JAR 包提供
大數據中心日常數據開發工作以HQL為主,我們將API服務封裝成JAR包,可直接適用於數倉開發使用。
四、運用場景
目前成功運用於大數據中心多個重點業務和平台,對其日常指標進行監控,以應用商店為例。
1、獲取da表數據到 da_appstore_core_data_di

2、監控數據統一處理 da_appstore_core_data_result_di

3、每條記錄調用上述UDF函數,輸出判定結果,異常值可對業務發送提醒,幫助排除業務風險
參考文獻
- 《創世紀·數理統計·正態分布的前世今生》
- 知乎朋友-小堯、jinzhao 關於正態分布閾值原理的部分闡述
作者:vivo 互聯網大數據團隊