You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何判断使用Scipy拟合正态分布是否正确?含PCA降维后拟合场景

如何验证用Scipy拟合多元正态分布的操作是否正确?

先帮你确认下代码本身的正确性,再给你几个实用的验证方法,一步步来:

一、先检查你的拟合代码是否正确

你给出的代码片段方向是对的,补全后完整代码应该是这样:

import numpy as np
from scipy.stats import multivariate_normal

# z是PCA降维后的矩阵,shape为(样本数, 700)
mean = np.mean(z, axis=0)  # 按列计算每个主成分的均值,得到700维均值向量
cov = np.cov(z, rowvar=0)  # rowvar=0表示每一行是一个样本,计算700x700的协方差矩阵
g = multivariate_normal(mean=mean, cov=cov)  # 实例化多元正态分布对象

这里的关键细节:

  • np.mean(z, axis=0):完全正确,我们需要每个主成分(列)的均值,对应多元正态的均值向量。
  • np.cov(z, rowvar=0):也没问题,rowvar=0明确告诉函数“行是样本,列是特征”,和你的数据结构匹配。
  • 额外提一句:因为你之前把原始数据归一化到均值为0,PCA作为线性变换,理论上主成分的均值应该非常接近0(比如绝对值<1e-10),如果你的mean向量偏差很大,那得回头检查归一化或PCA的步骤有没有问题。

二、验证拟合是否合理的实用方法

1. 协方差矩阵的基础校验

PCA后的主成分应该是正交不相关的,所以协方差矩阵cov的非对角元素应该接近0,且矩阵本身是对称的。可以用两行代码快速验证:

# 检查协方差矩阵是否对称
print(np.allclose(cov, cov.T))  # 正常应该返回True
# 检查非对角元素是否接近0(允许微小浮点误差)
off_diag = cov[np.triu_indices_from(cov, k=1)]
print(np.allclose(off_diag, 0, atol=1e-8))  # 正常应该返回True

如果这两个检查不通过,说明PCA的执行可能有问题,得先排查这一步。

2. 单个主成分的可视化校验

多元正态分布的每个边缘分布都是正态分布,所以你可以随机选几个主成分,画直方图+拟合的正态曲线,直观对比:

import matplotlib.pyplot as plt

# 选第3个主成分为例(随便换索引就行)
comp_idx = 3
data = z[:, comp_idx]

# 绘制数据直方图(密度归一化)
plt.hist(data, bins=30, density=True, alpha=0.6, color='#1f77b4', label='Data Histogram')

# 生成拟合的正态分布PDF曲线
x_range = np.linspace(data.min(), data.max(), 1000)
pdf = g.pdf(x_range[:, np.newaxis])  # 注意要把一维数组转成列向量
plt.plot(x_range, pdf, 'r-', linewidth=2, label='Fitted Normal PDF')

plt.xlabel(f'Principal Component {comp_idx}')
plt.ylabel('Density')
plt.title('Data vs Fitted Normal Distribution')
plt.legend()
plt.show()

如果直方图的形状和红色曲线贴合得比较好,说明这个边缘分布符合正态性,间接支持多元正态的假设。

3. 统计检验:单个特征的正态性

用Shapiro-Wilk检验(小样本更灵敏)或Kolmogorov-Smirnov检验来量化每个主成分的正态性:

from scipy.stats import shapiro, kstest

# 遍历所有主成分做Shapiro-Wilk检验
for idx in range(z.shape[1]):
    stat, p_val = shapiro(z[:, idx])
    print(f"Component {idx}: Shapiro stat={stat:.4f}, p-value={p_val:.4f}")
  • p-value > 0.05,则不能拒绝“该主成分服从正态分布”的原假设;
  • 注意:如果你的样本量很大(比如超过5000),Shapiro-Wilk可能会因为微小的偏差就拒绝原假设,这时候要结合可视化结果一起判断,不要只看p值。

4. 统计检验:多元正态性

用Mardia检验直接验证整个数据集的多元正态性,这是专门针对多元分布的检验方法:

from scipy.stats import mardia

# 计算偏度和峰度的检验统计量与p值
skew_stat, skew_p = mardia(z, axis=0, skewness=True)
kurt_stat, kurt_p = mardia(z, axis=0, kurtosis=True)

print(f"Mardia Skewness Test: stat={skew_stat:.4f}, p-value={skew_p:.4f}")
print(f"Mardia Kurtosis Test: stat={kurt_stat:.4f}, p-value={kurt_p:.4f}")

如果两个p值都大于0.05,说明没有足够的证据拒绝“数据服从多元正态分布”的原假设,你的拟合是合理的。

5. 拟合优度检验:卡方检验

把数据分成若干区间,比较观测频数和拟合分布的期望频数,用卡方检验判断差异是否显著:

from scipy.stats import chisquare

# 以第一个主成分为例
data = z[:, 0]
n_bins = 10

# 计算观测频数和区间边界
obs_counts, bin_edges = np.histogram(data, bins=n_bins)
# 计算每个区间的期望频数
bin_width = bin_edges[1] - bin_edges[0]
bin_mids = (bin_edges[:-1] + bin_edges[1:]) / 2
exp_probs = multivariate_normal.pdf(bin_mids, mean=mean[0], cov=cov[0,0]) * bin_width
exp_counts = exp_probs * len(data)

# 执行卡方检验(注意:如果期望频数<5,需要合并区间)
stat, p_val = chisquare(obs_counts, exp_counts)
print(f"Chi-square Test: stat={stat:.4f}, p-value={p_val:.4f}")

p值大于0.05的话,说明观测数据和拟合的正态分布没有显著差异。


内容的提问来源于stack exchange,提问作者PyRsquared

火山引擎 最新活动