在数据分析中,协方差矩阵的估计对于理解数据特征之间的关系至关重要。然而,传统的最大似然估计方法对数据集中的异常值非常敏感,这可能导致估计结果的偏差。为了解决这个问题,可以使用鲁棒的协方差估计方法,以确保估计结果对数据集中的“错误”观测值具有抵抗力。
最小协方差行列式(MCD)估计器是一种鲁棒的、高断裂点的协方差估计方法。这意味着它可以用于估计高度污染的数据集的协方差矩阵,直到数据集中的异常值数量达到 \(\frac{n_\text{samples} - n_\text{features}-1}{2}\)。MCD估计器的核心思想是找到 \(\frac{n_\text{samples} + n_\text{features}+1}{2}\) 个观测值,其经验协方差具有最小的行列式,从而得到一个“纯净”的观测值子集,用于计算位置和协方差的标凊估计。在进行校正步骤以补偿仅从初始数据的一部分学习估计值的事实后,最终得到了数据集位置和协方差的鲁棒估计。
MCD估计器由P.J.Rousseuw提出,其主要优点是在数据集样本数量大于5倍特征数量时,能够提供低误差的估计。这种方法特别适合于那些对异常值敏感的应用场景,例如金融风险管理、生物统计学和环境科学等领域。
在本例中,比较了在受污染的高斯分布数据集上使用不同类型的位置和协方差估计时所犯的估计误差。具体来说,比较了以下几种估计方法:
通过这些比较,可以清楚地看到,在数据集中存在异常值时,鲁棒估计方法(如MCD)相比于传统的经验估计方法具有显著的优势。
以下是一个使用Python和scikit-learn库进行鲁棒协方差估计的示例代码。
import matplotlib.pyplot as plt
import numpy as np
from sklearn.covariance import EmpiricalCovariance, MinCovDet
# 示例设置
n_samples = 80
n_features = 5
repeat = 10
range_n_outliers = np.concatenate((np.linspace(0, n_samples/8, 5), np.linspace(n_samples/8, n_samples/2, 5)[1:-1])).astype(int)
# 定义用于存储结果的数组
err_loc_mcd = np.zeros((range_n_outliers.size, repeat))
err_cov_mcd = np.zeros((range_n_outliers.size, repeat))
err_loc_emp_full = np.zeros((range_n_outliers.size, repeat))
err_cov_emp_full = np.zeros((range_n_outliers.size, repeat))
err_loc_emp_pure = np.zeros((range_n_outliers.size, repeat))
err_cov_emp_pure = np.zeros((range_n_outliers.size, repeat))
# 计算
for i, n_outliers in enumerate(range_n_outliers):
for j in range(repeat):
rng = np.random.RandomState(i * j)
# 生成数据
X = rng.randn(n_samples, n_features)
# 添加一些异常值
outliers_index = rng.permutation(n_samples)[:n_outliers]
outliers_offset = 10.0 * (np.random.randint(2, size=(n_outliers, n_features)) - 0.5)
X[outliers_index] += outliers_offset
inliers_mask = np.ones(n_samples).astype(bool)
inliers_mask[outliers_index] = False
# 拟合最小协方差行列式(MCD)鲁棒估计器到数据
mcd = MinCovDet().fit(X)
# 比较原始鲁棒估计与真实位置和协方差
err_loc_mcd[i, j] = np.sum(mcd.location_ ** 2)
err_cov_mcd[i, j] = mcd.error_norm(np.eye(n_features))
# 比较从完整数据集学习的估计器与真实参数
err_loc_emp_full[i, j] = np.sum(X.mean(0) ** 2)
err_cov_emp_full[i, j] = (EmpiricalCovariance().fit(X).error_norm(np.eye(n_features)))
# 与从纯净数据集学习的经验协方差进行比较(即“完美”的mcd)
pure_X = X[inliers_mask]
pure_location = pure_X.mean(0)
pure_emp_cov = EmpiricalCovariance().fit(pure_X)
err_loc_emp_pure[i, j] = np.sum(pure_location ** 2)
err_cov_emp_pure[i, j] = pure_emp_cov.error_norm(np.eye(n_features))
# 显示结果
font_prop = matplotlib.font_manager.FontProperties(size=11)
plt.subplot(2, 1, 1)
lw = 2
plt.errorbar(range_n_outliers, err_loc_mcd.mean(1), yerr=err_loc_mcd.std(1) / np.sqrt(repeat), label="鲁棒位置", lw=lw, color="m")
plt.errorbar(range_n_outliers, err_loc_emp_full.mean(1), yerr=err_loc_emp_full.std(1) / np.sqrt(repeat), label="全数据集均值", lw=lw, color="green")
plt.errorbar(range_n_outliers, err_loc_emp_pure.mean(1), yerr=err_loc_emp_pure.std(1) / np.sqrt(repeat), label="纯净数据集均值", lw=lw, color="black")
plt.title("异常值对位置估计的影响")
plt.ylabel(r"误差 ($||\mu - \hat{\mu}||_2^2$)")
plt.legend(loc="upper left", prop=font_prop)
plt.subplot(2, 1, 2)
x_size = range_n_outliers.size
plt.errorbar(range_n_outliers, err_cov_mcd.mean(1), yerr=err_cov_mcd.std(1), label="鲁棒协方差 (mcd)", color="m")
plt.errorbar(range_n_outliers[:(x_size//5+1)], err_cov_emp_full.mean(1)[:(x_size//5+1)], yerr=err_cov_emp_full.std(1)[:(x_size//5+1)], label="全数据集经验协方差", color="green")
plt.plot(range_n_outliers[(x_size//5):(x_size//2-1)], err_cov_emp_full.mean(1)[(x_size//5):(x_size//2-1)], color="green", ls="--")
plt.errorbar(range_n_outliers, err_cov_emp_pure.mean(1), yerr=err_cov_emp_pure.std(1), label="纯净数据集经验协方差", color="black")
plt.title("异常值对协方差估计的影响")
plt.xlabel("污染量 (%)")
plt.ylabel("RMSE")
plt.legend(loc="upper center", prop=font_prop)
plt.show()