import pandas as pd
import matplotlib.pyplot as plt
# 設置隨機種子以確保可重復性
np.random.seed(42)
# 生成一個1000個數據點的正態分布數據集
data = np.random.normal(loc=50, scale=5, size=1000)
# 人為地添加一些異常值
data[100] = 80
data[200] = 85
data[300] = 90
# 創建DataFrame
df = pd.DataFrame(data, columns=['Value'])
# 僅進行數據的可視化,不標注異常值索引
plt.figure(figsize=(10, 6))
plt.plot(df['Value'], 'b.', label='Data Points')
plt.title('Data Points Visualization')
plt.xlabel('Index')
plt.ylabel('Value')
plt.grid(linestyle='--', alpha=0.7)
plt.show()
生成一個包含1000個數據點的正態分布數據集,加入了幾個異常值,然后將數據點可視化為一個散點圖,這個數據將為接下來的實驗數據。
# 繪制箱線圖
plt.figure(figsize=(10, 6))
plt.boxplot(df['Value'], vert=False)
plt.title('Boxplot of Data Values')
plt.xlabel('Value')
plt.grid(linestyle='--', alpha=0.7)
plt.show()
# 計算異常值
Q1 = df['Value'].quantile(0.25)
Q3 = df['Value'].quantile(0.75)
IQR = Q3 - Q1
# 異常值定義為低于Q1 - 1.5 * IQR或高于Q3 + 1.5 * IQR的數據點
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
# 找出異常值及其索引
outliers = df[(df['Value'] < lower_bound) | (df['Value'] > upper_bound)]
# 輸出異常值的索引及其對應的值
outliers_indices = outliers.index.tolist()
outliers_values = outliers['Value'].tolist()
print("異常值索引: ", outliers_indices)
print("異常值對應的值: ", outliers_values)
箱線圖繪制:使用plt.boxplot()函數繪制數據的箱線圖,可以直觀地看到異常值的位置,計算異常值:通過計算四分位數(Q1和Q3)及四分位距(IQR),設定異常值的上下限,然后通過這些上下限找出異常值,執行代碼后,你會看到一個箱線圖以及異常值的索引和值,箱線圖展示了數據的分布情況,異常值位于圖中的“胡須”之外,然后你就可以根據索引對異常值進行相應處理,如果刪除后就按照缺失值的方法去填補數據。
# 定義函數 detect_outliers,接受兩個參數:DataFrame 和需要檢查的列名
def detect_outliers(df, column_names):
# 初始化一個空的 Index,用于存儲異常值的索引
outlier_indices = pd.Index([])
# 遍歷指定的列名列表
for column_name in column_names:
# 計算當前列的均值和標準差
mean = df[column_name].mean()
std = df[column_name].std()
# 計算Z-score
z_scores = (df[column_name] - mean) / std
# 設置 Z-score 閾值,例如,選擇閾值為 3,即超過3個標準差的值被認為是異常值
threshold = 3
# 找出Z-score絕對值大于閾值的行
outliers = df[abs(z_scores) > threshold]
# 將新找到的異常值索引與已有異常值索引合并
outlier_indices = outlier_indices.union(outliers.index)
# 返回所有異常值的索引列表
return outlier_indices
# 使用函數檢測異常值
outlier_indices = detect_outliers(df, ['Value'])
# 獲取異常值及其對應的索引和值
outliers = df.loc[outlier_indices]
# 輸出異常值的索引及其對應的值
print("異常值索引: ", outlier_indices.tolist())
print("異常值對應的值: ", outliers['Value'].tolist())
# 繪制數據的散點圖,并在圖中標注異常值
plt.figure(figsize=(10, 6))
plt.plot(df.index, df['Value'], 'b.', label='Data Points')
plt.plot(outliers.index, outliers['Value'], 'ro', label='Outliers')
# 在異常值點上標注其索引和值
for index, value in outliers.iterrows():
plt.text(index, value['Value'] + 0.5, f'{index}',
horizontalalignment='center', color='red', fontsize=10)
# 設置圖表標題和軸標簽
plt.title('Scatter Plot with Outliers Highlighted', fontsize=16)
plt.xlabel('Index', fontsize=14)
plt.ylabel('Value', fontsize=14)
plt.grid(linestyle='--', alpha=0.7)
plt.legend()
plt.show()
檢測異常值函數 detect_outliers:使用Z-score方法來檢測異常值,Z-score超過3的值被認為是異常值,輸出異常值:打印出檢測到的異常值的索引和對應的值,可視化:使用散點圖(plt.plot)繪制所有數據點,將異常值用紅色圓點標出,并在其上方標注對應的索引。
from scipy.stats import t
# 定義 Grubbs 檢驗的函數 detect_outliers_grubbs
def detect_outliers_grubbs(df, column_names, alpha=0.05):
# 初始化一個空的 Index,用于存儲異常值的索引
outlier_indices = pd.Index([])
# 遍歷指定的列名列表
for column_name in column_names:
# 獲取當前列的數據
data = df[column_name]
# 計算均值和標準差
mean = np.mean(data)
std = np.std(data, ddof=1) # 使用樣本標準差,自由度為1
# 計算 Grubbs 檢驗的統計量
n = len(data)
t_critical = t.ppf(1 - alpha / (2 * n), n - 2)
g = (np.max(data) - mean) / std
# 設置 Grubbs 檢驗的閾值
threshold = ((n - 1) / np.sqrt(n)) * np.sqrt(t_critical**2 / (n - 2 + t_critical**2))
# 找出 Grubbs 檢驗的異常值
outliers = df[np.abs((data - mean) / std) > threshold]
# 將新找到的異常值索引與已有異常值索引合并
outlier_indices = outlier_indices.union(outliers.index)
# 返回所有異常值的索引列表
return outlier_indices
# 使用 Grubbs 檢驗檢測異常值
outlier_indices = detect_outliers_grubbs(df, ['Value'])
# 獲取異常值及其對應的索引和值
outliers = df.loc[outlier_indices]
# 輸出異常值的索引及其對應的值
print("異常值索引: ", outlier_indices.tolist())
print("異常值對應的值: ", outliers['Value'].tolist())
# 繪制數據的散點圖,并在圖中標注異常值
plt.figure(figsize=(10, 6))
plt.plot(df.index, df['Value'], 'b.', label='Data Points')
plt.plot(outliers.index, outliers['Value'], 'ro', label='Outliers')
# 在異常值點上標注其索引和值
for index, value in outliers.iterrows():
plt.text(index, value['Value'] + 0.5, f'{index}',
horizontalalignment='center', color='red', fontsize=10)
# 設置圖表標題和軸標簽
plt.title('Scatter Plot with Grubbs Outliers Highlighted', fontsize=16)
plt.xlabel('Index', fontsize=14)
plt.ylabel('Value', fontsize=14)
plt.grid(linestyle='--', alpha=0.7)
plt.legend()
plt.show()
通過計算Grubbs統計量G和臨界值,將超過臨界值的點視為異常值,t_critical 使用Student t分布的臨界值來計算Grubbs檢驗的閾值,這個方法提供了一種基于假設檢驗的統計方法來識別異常值,適用于更嚴格的異常值檢測場景,在實際應用中,盡管不同異常值檢測方法的輸出結果可能有所不同(可以和前面的方法對比),但Grubbs檢驗在實驗數據中成功地識別出了所有人為添加的異常值,顯示了其在異常值檢測方面的有效性和精準性。
from sklearn.ensemble import IsolationForest
# 使用孤立森林進行異常值檢測
iso_forest = IsolationForest(contamination=0.01, random_state=42)
df['Anomaly'] = iso_forest.fit_predict(df[['Value']])
# 獲取異常值的索引及其對應的值
anomalies = df[df['Anomaly'] == -1]
anomaly_indices = anomalies.index
anomaly_values = anomalies['Value']
# 打印異常值的索引和對應的值
print("異常值索引: ", list(anomaly_indices))
print("異常值對應的值: ", list(anomaly_values))
# 數據可視化
plt.figure(figsize=(10, 6))
plt.plot(df['Value'], 'b.', label='Data Points')
plt.plot(anomaly_indices, anomaly_values, 'ro', label='Outliers')
plt.title('Data Points with Anomalies Visualization')
plt.xlabel('Index')
plt.ylabel('Value')
plt.grid(linestyle='--', alpha=0.7)
# 標注異常值索引
for i, index in enumerate(anomaly_indices):
plt.text(index, anomaly_values.iloc[i] + 1, f'{index}', color='red', fontsize=9)
plt.legend()
plt.show()
孤立森林檢測: 使用 IsolationForest 進行異常值檢測,標記異常值為 -1,可視化: 使用 matplotlib 可視化數據,并在圖中標注異常值的索引,異常值以紅色散點顯示,并在旁邊標注其索引。
對于如何使用 DBSCAN 進行異常值檢測,我之前已經撰寫了一篇詳細的文章,了解更多關于 DBSCAN 聚類和異常值檢測的內容:密度聚類DBSCAN詳解。
在這里可以發現每種方法的檢測結果都不完全一致,但是每種異常值檢測方法的檢測結果不一致是正常的,這是因為它們的原理和適用場景不同,箱線圖和標準差方法依賴于數據的分布特征,適合于簡單和對稱分布的數據;Grubbs 假設檢驗適合檢測單個異常值;而孤立森林則擅長處理高維和復雜的數據,選擇哪種方法取決于具體的數據特征和分析需求,建議在實際應用中結合多種方法進行對比,以確保檢測到的異常值更加準確和全面。