問題
在項目中,需要對異常值進行剔除,需要一種魯棒性比較好的方法,總結了一個實踐方法。
方法
基于中位數和MAD(中位數絕對偏差)的魯棒平均值計算算法的詳細過程,按照您要求的步驟分解:
算法過程
過程:
-
- 先使用中位數作為初始估計
-
- 計算MAD作為離散度度量
-
- 排除偏離中位數超過3倍MAD的數據點
-
- 對剩余數據計算平均值
輸入:
- 數據集
data = [x?, x?, ..., x?]
(可能包含異常值) - 異常值閾值
k
(默認k=3
)
輸出:
- 魯棒平均值
robust_mean
- 被排除的異常值索引列表
outliers
步驟 1:計算中位數(初始估計)
中位數對異常值不敏感,是數據中心的魯棒估計。
median = np.median(data) # 中位數
例子:
data = [10, 12, 11, 15, 10, 9, 11, 10, 100, 8, 9, 10, 12, -50]
排序后:[-50, 8, 9, 9, 10, 10, 10, 10, 11, 11, 12, 12, 15, 100]
中位數 median = 10
(第7和第8個值的平均)
步驟 2:計算MAD(離散度度量)
MAD(Median Absolute Deviation)是數據與中位數絕對偏差的中位數,對異常值魯棒。
deviations = np.abs(data - median) # 各點與中位數的絕對偏差
mad = np.median(deviations) # MAD
mad = mad * 1.4826 # 調整因子(使MAD≈標準差)
調整因子解釋:
- 對于正態分布,標準差
σ ≈ 1.4826 × MAD
。 - 調整后,
k=3
對應正態分布的3σ準則(覆蓋99.7%數據)。
例子:
絕對偏差 deviations = [60, 2, 1, 5, 0, 1, 1, 0, 90, 2, 1, 0, 2, 40]
排序后:[0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 5, 40, 60, 90]
MAD = 1(中位數)
調整后 mad = 1.4826
步驟 3:排除異常值(3×MAD準則)
標記所有滿足 |x? - median| > k × mad
的點為異常值。
outlier_mask = deviations > (k * mad) # 異常值掩碼
clean_data = data[~outlier_mask] # 清洗后的數據
例子(k=3
):
閾值 3 × 1.4826 ≈ 4.45
異常值條件:|x? - 10| > 4.45
100
:|100 - 10| = 90 > 4.45
→ 異常-50
:|-50 - 10| = 60 > 4.45
→ 異常
其他點均保留。
步驟 4:計算剩余數據的平均值
對清洗后的數據求算術平均。
robust_mean = np.mean(clean_data)
例子:
清洗后數據:[10, 12, 11, 15, 10, 9, 11, 10, 8, 9, 10, 12]
魯棒平均值 robust_mean = 10.5
完整代碼實現
import numpy as npdef robust_mean(data, k=3):data = np.asarray(data)median = np.median(data)# 計算MAD并調整deviations = np.abs(data - median)mad = np.median(deviations) * 1.4826# 處理MAD為0的情況(所有數據相同)if mad == 0:return median, np.array([])# 標記并排除異常值outlier_mask = deviations > (k * mad)clean_data = data[~outlier_mask]return np.mean(clean_data), np.where(outlier_mask)[0]# 示例
data = [10, 12, 11, 15, 10, 9, 11, 10, 100, 8, 9, 10, 12, -50]
mean, outliers = robust_mean(data)
print(f"魯棒平均值: {mean}, 異常值索引: {outliers}")
算法優點
- 魯棒性:中位數和MAD均不受極端值影響。
- 自動閾值:
k=3
對應正態分布的3σ準則,可調整(如嚴格檢測用k=2.5
)。 - 適用性:適合傳感器數據(如雞秤)、金融數據等含離群點的場景。
可視化
數據分布: [-50, 8, 9, 9, 10, 10, 10, 10, 11, 11, 12, 12, 15, 100]↑______中位數=10______↑ ↑異常值(-50) 異常值(100)