如何使用Scipy的Mahalanobis距离实现检测DataFrame中的异常值?
用Scipy实现DataFrame全局马氏距离异常值检测
核心逻辑
Scipy的mahalanobis函数仅支持两个一维向量的距离计算,要处理整个DataFrame,只需将每行数据作为单个向量,与数据集的全局均值向量计算距离,再结合卡方分布推导p值来判定异常。
完整实现代码
1. 导入依赖
import pandas as pd import numpy as np from scipy.spatial.distance import mahalanobis from scipy.stats import chi2
2. 准备测试数据
# 生成模拟多维数据集 np.random.seed(42) df = pd.DataFrame({ 'feat1': np.random.normal(50, 10, 100), 'feat2': np.random.normal(20, 5, 100), 'feat3': np.random.normal(10, 2, 100) }) # 手动插入2个异常值 df.loc[100] = [100, 50, 20] df.loc[101] = [0, 0, 0]
3. 计算全局统计量
# 提取均值向量 mean_vec = df.mean().values # 计算协方差矩阵,加微小正则项避免奇异矩阵 cov_mat = df.cov().values + 1e-6 * np.eye(df.shape[1]) # 求协方差矩阵的逆矩阵 inv_cov = np.linalg.inv(cov_mat)
4. 批量计算马氏距离与p值
def get_mahalanobis_stats(row): row_vec = row.values # 调用Scipy的mahalanobis函数计算距离 dist = mahalanobis(row_vec, mean_vec, inv_cov) # 马氏距离平方服从卡方分布,自由度为特征数-1 chi_sq = dist ** 2 # 计算右侧检验的p值 p_val = 1 - chi2.cdf(chi_sq, df=df.shape[1]-1) return pd.Series([dist, chi_sq, p_val], index=['mahalanobis_dist', 'chi_sq', 'p_value']) # 应用到DataFrame每一行 stats_df = df.apply(get_mahalanobis_stats, axis=1) # 合并原数据与统计结果 final_df = pd.concat([df, stats_df], axis=1)
5. 标记异常值
# 以p<0.05为阈值标记异常值,可根据业务调整 final_df['is_outlier'] = final_df['p_value'] < 0.05
关键细节
- 协方差矩阵添加
1e-6 * np.eye()是为了避免数据线性相关时出现奇异矩阵,导致逆矩阵无法求解 - 卡方分布的自由度为特征数量-1,这是p值计算的核心前提
- 异常值阈值可灵活调整,比如业务对异常敏感时可设为0.01
内容的提问来源于stack exchange,提问作者kp42
相关产品推荐
相关产品推荐

