1. 程式人生 > 實用技巧 >談談統計學正態分佈閾值原理在資料分析工作中的運用

談談統計學正態分佈閾值原理在資料分析工作中的運用

一、背景

0.0 神說,要有正態分佈,於是就有了正態分佈。

0.1 神看正態分佈是好的,就讓隨機誤差都隨了正態分佈。

0.2 正態分佈的奇妙之處,就是許多看似隨機事件竟然服從一個表示式就能表達的分佈,如同上帝之手特意為之。

神覺得拋硬幣是好的,於是定義每個丟擲硬幣正面記+1分,反面記-1分。創世紀從0分開始,神只拋1次硬幣,有2種可能:一半的概率+1分,一半的概率-1分。此時概率分佈大概是這樣的:

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

如果畫圖來感受,資料分佈大概如下:

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

——《創世紀·數理統計·正態分佈的前世今生》

開頭摘自統計學中非常經典的一本書籍,由此可見正態分佈是非常經典和隨處可見的,為什麼正態分佈這麼常見呢?因為通常情況下,一個事物的影響因素都是多個,好比每個人的學習成績,受到多個因素的影響,比如:

  • 本人的智商情況。
  • 上課聽講的認真程度,課前的預習程度,與老師的互動程度。
  • 課後是否及時複習,有沒有及時溫習知識點呢,有沒有做好作業鞏固。

每一天的因素,每天的行為,對於學生的成績不是產生正面因素就是負面因素,這些因素對於成績的影響不是正面就是負面的,反覆累計加持就像上圖的拋硬幣一樣,讓成績最後呈現出正態分佈。資料呈現正態分佈其實背後是有中心極限定理原理支援,根據中心極限定理,如果事物受到多種因素的影響,不管每個因素單獨本身是什麼分佈,他們加總後結果的平均值就是正態分佈。

二、引用

正是因為日常分析工作中資料呈現是正態分佈的,處於兩個極端的值往往是異常的,與我們挑選異常值天然契合。在業務方尋求一種自動監控方案的過程中,我們選擇了該方案。根據資料分析工作中,結合統計學的資料閾值分佈原理,通過自動劃分資料級別範圍,確定異常值,如下圖箱線圖,箱線圖是一個能夠通過5個數字來描述資料的分佈的標準方式,這5個數字包括:最小值,第一分位,中位數,第三分位數,最大值,箱線圖能夠明確的展示離群點的資訊,同時能夠讓我們瞭解資料是否對稱,資料如何分組、資料的峰度。

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

(圖片來源於網路)

需要有關資料變異性或分散性的資訊。箱形圖是一張圖表,它很好地指示資料中的值如何分佈,儘管與直方圖或密度圖相比,箱線圖似乎是原始的,但它們具有佔用較少空間的優勢,這在比較許多組或資料集之間的分佈時非常有用。——適用於大批量的資料波動監控。

(圖片來源於網路)

箱線圖是一種基於五位數摘要(“最小”,第一四分位數(Q1),中位數,第三四分位數(Q3)和“最大”)顯示資料分佈的標準化方法。

  1. 中位數(Q2 / 50th百分位數):資料集的中間值;
  2. 第一個四分位數(Q1 / 25百分位數):最小數(不是“最小值”)和資料集的中位數之間的中間數;
  3. 第三四分位數(Q3 / 75th Percentile):資料集的中位數和最大值之間的中間值(不是“最大值”);
  4. 四分位間距(IQR):第25至第75個百分點的距離;
  5. 晶須(藍色顯示);
  6. 離群值(顯示為綠色圓圈);
  7. “最大”:Q3 + 1.5 * IQR;
  8. “最低”: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暫不做)分別做如下處理

-------
        //第四步,按照資料升序排序及資料大小,將資料(不算上面找出的最後一組資料)平均分為4等份(這樣會在陣列中插入三個樁),並計算出第一個樁和第三個樁的值,以上面為例子,原來陣列大小14,去掉最後一個,13個

//判斷,給結論

(1)   如果同比等級> 2 and 環比等級 > 2  (表示一定有異常)

And

case when 同比值<0 and 環比值<0 then '異常下降'

       when 同比值>0 and 環比值>0 then '異常上漲'

       else '異常'

end as res



return 
(2)其他,無異常波動}
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函式,輸出判定結果,異常值可對業務傳送提醒,幫助排除業務風險

參考文獻

  1. 《創世紀·數理統計·正態分佈的前世今生》
  2. 知乎朋友-小堯、jinzhao 關於正態分佈閾值原理的部分闡述

作者:vivo 網際網路大資料團隊