異常值檢測!最佳統計方法實踐(程式碼實現)!⛵

ShowMeAI發表於2022-11-23
異常值檢測!最佳統計方法實踐(程式碼實現)!⛵

? 作者:韓信子@ShowMeAI
? Python3◉技能提升系列https://www.showmeai.tech/tutorials/56
? 資料分析實戰系列https://www.showmeai.tech/tutorials/40
? 本文地址https://www.showmeai.tech/article-detail/336
? 宣告:版權所有,轉載請聯絡平臺與作者並註明出處
? 收藏ShowMeAI檢視更多精彩內容

異常值檢測!最佳統計方法實踐(程式碼實現)!⛵

? 異常值 Q&A

異常值是距離其他資料值太遠的資料點,也被稱為離群點。它可能是自然發生的,也可能是由於測量不準確、拼寫錯誤或系統故障造成的。異常值也可能出現在傾斜資料中,這些型別的異常值被認為是自然異常值。

異常值檢測!最佳統計方法實踐(程式碼實現)!⛵

瞭解異常值檢測與分析的基礎知識,請檢視 ShowMeAI](https://www.showmeai.tech/) 這篇文章:

? 異常值對分佈有什麼影響?

異常值會影響資料的均值、標準差和四分位數值。如果我們在去除異常值之前和之後計算這些統計資料,可能會有比較大的差異。

異常值檢測!最佳統計方法實踐(程式碼實現)!⛵

? 異常值對機器學習模型有什麼影響?

  • 如果認為異常值是自然的,不是由於測量錯誤產生的 → 應該將其保留在資料集中,並用『標準化』等資料預處理方式處理。
  • 如果有一個包含少量異常值的大型資料集 → 應該將其保留,不會顯著影響結果。
  • 如果確定異常值是由測量誤差造成的 → 應該將它們從資料集中刪除。
異常值檢測!最佳統計方法實踐(程式碼實現)!⛵

去除異常值會帶來資料集規模的減小,而且模型的適用性也會限制在輸入值的度量範圍內,丟棄自然異常值也可能導致模型不準確。

? 基於視覺化的異常值檢測

異常值不容易被『肉眼』檢測到,但我們有一些視覺化工具可以幫助完成這項任務。最常見的是箱線圖和直方圖。我們這裡用 ?保險資料來做一個講解:

? 實戰資料集下載(百度網盤):公✦眾✦號『ShowMeAI研究中心』回覆『實戰』,或者點選 這裡 獲取本文 [29]基於統計方法的異常值檢測程式碼實戰insurance資料集

ShowMeAI官方GitHubhttps://github.com/ShowMeAI-Hub

我們首先匯入必要的庫並載入資料集。

import numpy as np
import pandas as pd
import seaborn as sns
import statistics#Load dataset:
df = pd.read_csv('insurance.csv')
df
異常值檢測!最佳統計方法實踐(程式碼實現)!⛵

我們對變數『年齡』、『體重指數』和『費用』進行異常值檢測分析。

第一種方法是使用箱線圖 / Box-Plots 來繪製資料分佈:

# age, bmi 和 expenses的箱線圖繪圖
sns.boxplot(y="age", data=df)
sns.boxplot(y="bmi", data=df)
sns.boxplot(y="expenses", data=df)
異常值檢測!最佳統計方法實踐(程式碼實現)!⛵

透過檢視箱線圖,我們可以看到變數 age 沒有異常值,變數 bmi 在上限中有一些異常值,而變數 expense 在上限中有一系列異常值(表明存在偏態分佈)。

為了檢查偏態分佈,我們再使用直方圖繪圖:

# age, bmi 和 expenses的直方圖
sns.histplot(df, x="age", kde=True)
sns.histplot(df, x="bmi", kde=True)
sns.histplot(df, x="expenses", kde=True)
異常值檢測!最佳統計方法實踐(程式碼實現)!⛵

透過直方圖,我們可以看到變數『age』是近似均勻分佈,『bmi』接近正態分佈,而『expense/費用』則呈偏態分佈。

對於年齡,我們無需做異常值剔除;對於 bmi,我們將剔除高於 47 的值;對於費用,我們將剔除高於 50000 的值。

#bmi 和 expenses 的異常值處理
df.drop(df[df['bmi'] >= 47].index, inplace = True)
df.drop(df[df['expenses'] >= 50000].index, inplace = True)

現在,如果我們再次檢查箱線圖和直方圖:

異常值檢測!最佳統計方法實踐(程式碼實現)!⛵

? 基於統計方法的異常值檢測

檢測異常值有兩種主要的統計方法:使用 z 分數和使用四分位距。

? 使用 z 分數檢測異常值

Z 分數是一種數學變換,它根據每個觀測值與平均值的距離對其進行分類。z-score 的計算公示為:

異常值檢測!最佳統計方法實踐(程式碼實現)!⛵

我們定義異常檢測標準:如果 z-score 小於 -3或 z-score 大於 3。

我們將重新載入資料集,因為我們在前面的示例中對其進行了更改,載入後的資料上我們會把變數轉換為 z 分數:

# 重新載入資料
df = pd.read_csv('insurance.csv')

# 為age計算均值和標準差
mean_age = statistics.mean(df['age'])
stdev_age = statistics.stdev(df['age'])

# 計算z值
age_z_score = (df['age']-mean_age)/stdev_age

# 新增z結果到原dataframe
df['age_z_score'] = age_z_score.tolist()

現在我們將檢查高於 3SD 或低於 -3SD 的值:

# 檢測小於-3SD的值:
df.sort_values(by=['age_z_score'], ascending=True)
異常值檢測!最佳統計方法實踐(程式碼實現)!⛵

我們可以看到 -3SD 以下沒有值。我們現在將檢查 3SD 以上的值:

# 檢測+3SD以上的值:
df.sort_values(by=['age_z_score'], ascending=False)
異常值檢測!最佳統計方法實踐(程式碼實現)!⛵

我們可以看到沒有高於 3SD 的值。變數年齡沒有異常值。

現在我們將對變數 bmi 執行相同的操作:

# 為bmi計算均值和標準差
mean_bmi = statistics.mean(df['bmi'])
stdev_bmi = statistics.stdev(df['bmi'])

# 為bmi計算z-score
bmi_z_score = (df['bmi']-mean_bmi)/stdev_bmi

# 新增到原始dataframe
df['bmi_z_score'] = bmi_z_score.tolist()

# 檢查低於-3SD的值
df.sort_values(by=['bmi_z_score'], ascending=True)

# 檢查大於3SD的值
df.sort_values(by=['bmi_z_score'], ascending=False)

這次我們會發現一些高於 3SD 的值:

異常值檢測!最佳統計方法實踐(程式碼實現)!⛵

我們對它進行剔除:

# 異常值處理
df.drop(df[df[‘bmi_z_score’] >= 3].index, inplace = True)

我們將對『expense/費用』應用相同的技術:

# 為expenses計算均值和標準差
mean_expenses = statistics.mean(df['expenses'])
stdev_expenses = statistics.stdev(df['expenses'])

# 計算z-score
expenses_z_score = (df['expenses']-mean_expenses)/stdev_expenses

# 新增到原始dataframe
df['expenses_z_score'] = expenses_z_score.tolist()

# 檢查低於-3SD的值
df.sort_values(by=['expenses_z_score'], ascending=True)

# 檢查高於3SD的值
df.sort_values(by=['expenses_z_score'], ascending=False)

# 異常值處理
df.drop(df[df[‘expenses_z_score’] >= 3].index, inplace = True)

如果我們再次檢查箱線圖和直方圖,我們將獲得:

異常值檢測!最佳統計方法實踐(程式碼實現)!⛵

? 使用四分位距檢測異常值

四分位間距將資料分為四個部分,從低到高排序,如下圖所示,每個部分包含相同數量的樣本。第一個四分位數(Q1)是邊界中資料點的值。這同樣適用於 Q2 和 Q3。 四分位距(IQR)是兩個中間部分的資料點(代表 50% 的資料)。四分位距包含高於 Q1 和低於 Q3 的所有資料點。如果該點高於 Q3 + (1.5 x IQR),則存在較高的異常值,如果 Q1 - (1.5 x IQR),則存在較低的異常值。

異常值檢測!最佳統計方法實踐(程式碼實現)!⛵

程式碼實現如下:

# 重新載入資料
df = pd.read_csv('insurance.csv')

# 計算上下四分位數位置
q75_age, q25_age = np.percentile(df['age'], [75 ,25])
iqr_age = q75_age - q25_age
iqr_age

# 計算上下邊界以用於異常檢測
age_h_bound = q75_age+(1.5*iqr_age)
age_l_bound = q25_age-(1.5*iqr_age)
print(age_h_bound)
print(age_l_bound)
異常值檢測!最佳統計方法實踐(程式碼實現)!⛵

我們計算得到上邊界 87 和下邊界 -9:

# 排序
df.sort_values(by=['age'], ascending=True)
異常值檢測!最佳統計方法實踐(程式碼實現)!⛵
# 排序
df.sort_values(by=['age'], ascending=False)

我們看到沒有異常值。

我們對變數 bmi 執行相同的操作:

# 計算上下四分位數位置
q75_bmi, q25_bmi = np.percentile(df['bmi'], [75 ,25])
iqr_bmi = q75_bmi - q25_bmi
iqr_bmi

# 計算上下邊界以用於異常檢測
bmi_h_bound = q75_bmi+(1.5*iqr_bmi)
bmi_l_bound = q25_bmi-(1.5*iqr_bmi)
print(bmi_h_bound)
print(bmi_l_bound)

# 排序
df.sort_values(by=['bmi'], ascending=True)
df.sort_values(by=['bmi'], ascending=False)

# 剔除異常值
df.drop(df[df['bmi'] >= 47.3].index, inplace = True)
df.drop(df[df['bmi'] <= 13.7].index, inplace = True)

我們只需要對可變費用做同樣的事情,我們將獲得以下箱線圖和直方圖:

異常值檢測!最佳統計方法實踐(程式碼實現)!⛵

參考資料

異常值檢測!最佳統計方法實踐(程式碼實現)!⛵

相關文章