【算法】基于中位数和MAD鲁棒平均值计算算法
问题
在项目中,需要对异常值进行剔除,需要一种鲁棒性比较好的方法,总结了一个实践方法。
方法
基于中位数和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)