在统计学中,马氏距离是一种衡量单个观测值与数据集分布模式之间距离的方法。对于高斯(正态)分布的数据,可以通过计算观测值的马氏距离来确定其与数据集中心的距离。具体来说,观测值x_i
到分布均值\(\mu\)
的距离可以通过下面的公式计算:
d_{(\mu,\Sigma)}(x_i)^2 = (x_i - \mu)^T\Sigma^{-1}(x_i - \mu)
其中,\(\mu\)
代表数据集的均值向量,\(\Sigma\)
是数据集的协方差矩阵。在实际应用中,这些参数通常通过样本数据来估计。然而,传统的最大似然估计(MLE)对于数据集中的异常值非常敏感,这会影响到马氏距离的准确性。因此,使用稳健的协方差估计方法,如最小协方差行列式(MCD)估计器,可以更好地抵抗异常值的影响,从而更准确地反映数据的真实结构。
最小协方差行列式估计器是一种高断裂点的稳健协方差估计方法,它可以用于估计高度污染的数据集的协方差矩阵,即使数据集中存在大量异常值。MCD的核心思想是找到一组观测值,其经验协方差矩阵的行列式最小,从而得到一个“纯净”的观测值子集,进而计算出均值和协方差的稳健估计。MCD估计器由P.J.Rousseuw提出。
通过比较使用MCD和MLE估计器计算的马氏距离,可以发现,当数据集中存在异常值时,基于MLE的马氏距离会受到这些异常值的影响,而基于MCD的马氏距离则能够更好地区分出异常值和正常观测值。这种区分能力在异常值检测、观测值排序和聚类等应用中非常有用。
首先,生成一个包含125个样本和2个特征的数据集。这两个特征都是以0为均值的高斯分布,其中第一个特征的标准差为2,第二个特征的标准差为1。然后,将其中的25个样本替换为具有异常值的高斯样本,其中第一个特征的标准差为1,第二个特征的标准差为7。
import numpy as np
# 为了结果的一致性
np.random.seed(7)
n_samples = 125
n_outliers = 25
n_features = 2
# 生成形状为(125, 2)的高斯数据
gen_cov = np.eye(n_features)
gen_cov[0, 0] = 2.0
X = np.dot(np.random.randn(n_samples, n_features), gen_cov)
# 添加一些异常值
outliers_cov = np.eye(n_features)
outliers_cov[np.arange(1, n_features), np.arange(1, n_features)] = 7.0
X[-n_outliers:] = np.dot(np.random.randn(n_outliers, n_features), outliers_cov)
接下来,使用MCD和MLE估计器来拟合数据,并打印出估计的协方差矩阵。注意到,基于MLE的估计器估计的特征2的方差(7.5)比基于MCD的稳健估计器(1.2)高得多。这表明,基于MCD的稳健估计器对异常值样本的抵抗能力更强。
import matplotlib.pyplot as plt
from sklearn.covariance import EmpiricalCovariance, MinCovDet
# 使用MCD稳健估计器拟合数据
robust_cov = MinCovDet().fit(X)
# 使用MLE估计器拟合数据
emp_cov = EmpiricalCovariance().fit(X)
print("估计的协方差矩阵:\nMCD (稳健):\n{}\nMLE:\n{}".format(robust_cov.covariance_, emp_cov.covariance_))
为了更直观地展示差异,绘制了由两种方法计算的马氏距离的等高线图。可以注意到,基于MCD的稳健马氏距离更好地拟合了内部黑点,而基于MLE的距离则更多地受到异常值红点的影响。
import matplotlib.lines as mlines
fig, ax = plt.subplots(figsize=(10, 5))
# 绘制数据集
inlier_plot = ax.scatter(X[:, 0], X[:, 1], color="black", label="内点")
outlier_plot = ax.scatter(X[-n_outliers:, 0], X[-n_outliers:, 1], color="red", label="异常值")
ax.set_xlim(ax.get_xlim()[0], 10.0)
ax.set_title("污染数据集的马氏距离")
# 创建特征1和特征2值的网格
xx, yy = np.meshgrid(np.linspace(plt.xlim()[0], plt.xlim()[1], 100), np.linspace(plt.ylim()[0], plt.ylim()[1], 100))
zz = np.c_[xx.ravel(), yy.ravel()]
# 计算基于MLE的马氏距离
mahal_emp_cov = emp_cov.mahalanobis(zz)
mahal_emp_cov = mahal_emp_cov.reshape(xx.shape)
emp_cov_contour = plt.contour(xx, yy, np.sqrt(mahal_emp_cov), cmap=plt.cm.PuBu_r, linestyles="dashed")
# 计算基于MCD的马氏距离
mahal_robust_cov = robust_cov.mahalanobis(zz)
mahal_robust_cov = mahal_robust_cov.reshape(xx.shape)
robust_contour = ax.contour(xx, yy, np.sqrt(mahal_robust_cov), cmap=plt.cm.YlOrBr_r, linestyles="dotted")
# 添加图例
ax.legend([mlines.Line2D([], [], color="tab:blue", linestyle="dashed"),
mlines.Line2D([], [], color="tab:orange", linestyle="dotted"),
inlier_plot, outlier_plot],
["MLE距离", "MCD距离", "内点", "异常值"],
loc="upper right", borderaxespad=0,)
plt.show()
最后,强调了基于MCD的马氏距离在区分异常值方面的能力。取马氏距离的立方根,得到近似正态分布(如Wilson和Hilferty所建议),然后使用箱线图绘制内点和异常值样本的值。对于基于MCD的稳健马氏距离,异常值样本的分布与内点样本的分布更加分离。
fig, (ax1, ax2) = plt.subplots(1, 2)
plt.subplots_adjust(wspace=0.6)
# 计算基于MLE的马氏距离的立方根
emp_mahal = emp_cov.mahalanobis(X - np.mean(X, 0))**(0.33)
# 绘制箱线图
ax1.boxplot([emp_mahal[:-n_outliers], emp_mahal[-n_outliers:]], widths=0.25)
# 绘制单个样本
ax1.plot(np.full(n_samples-n_outliers, 1.26), emp_mahal[:-n_outliers], "+k", markeredgewidth=1,)
ax1.plot(np.full(n_outliers, 2.26), emp_mahal[-n_outliers:], "+k", markeredgewidth=1,)
ax1.axes.set_xticklabels(("内点", "异常值"), size=15)
ax1.set_ylabel(r"$\sqrt[3]{\rm{(马氏距离)}}$", size=16)
ax1.set_title("使用非稳健估计\n(最大似然)")
# 计算基于MCD的马氏距离的立方根
robust_mahal = robust_cov.mahalanobis(X - robust_cov.location_)**(0.33)
# 绘制箱线图
ax2.boxplot([robust_mahal[:-n_outliers], robust_mahal[-n_outliers:]], widths=0.25)
# 绘制单个样本
ax2.plot(np.full(n_samples-n_outliers, 1.26), robust_mahal[:-n_outliers], "+k", markeredgewidth=1,)
ax2.plot(np.full(n_outliers, 2.26), robust_mahal[-n_outliers:], "+k", markeredgewidth=1,)
ax2.axes.set_xticklabels(("内点", "异常值"), size=15)
ax2.set_ylabel(r"$\sqrt[3]{\rm{(马氏距离)}}$", size=16)
ax2.set_title("使用稳健估计\n(最小协方差行列式)")
plt.show()