談談統計學正態分佈閾值原理在資料分析工作中的運用
一、背景
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}-------
//第四步,按照資料升序排序及資料大小,將資料(不算上面找出的最後一組資料)平均分為4等份(這樣會在陣列中插入三個樁),並計算出第一個樁和第三個樁的值,以上面為例子,原來陣列大小14,去掉最後一個,13個 //判斷,給結論 (1) 如果同比等級> 2 and 環比等級 > 2 (表示一定有異常) And case when 同比值<0 and 環比值<0 then '異常下降' when 同比值>0 and 環比值>0 then '異常上漲' else '異常' end as res return}
public bool checkOrgdata (String org_data){ //檢驗陣列中日期是否全部連續,數量至少要14天資料 … if(日期不連續 || 數量小於14) return false; return ture; } public String computeOrgdata (String org_data){ // 計算輸入資料中,每一行的同環比環比=(今日data/昨日data-1)*100%,
同比=(今日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 網際網路大資料團隊