问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

正态性检验:从Q-Q图到Shapiro-Wilk的全面解析数据是否呈现正态分布

创作时间:
作者:
@小白创作中心

正态性检验:从Q-Q图到Shapiro-Wilk的全面解析数据是否呈现正态分布

引用
1
来源
1.
https://explinks.com/blog/wx-normality-test-comprehensive-analysis-from-q-q-plot-to-shapiro-wilk-for-assessing-data-normality/

背景

正态分布是统计学中的一种连续概率分布,也称为高斯分布,其概率密度函数呈钟形曲线。正态分布具有以下重要特点:

  • 以均值为中心,数据在均值左右对称分布
  • 标准差决定了分布的宽度,较小的标准差使分布更窄,较大的标准差使分布更宽
  • 分布的总面积为1,即所有可能取值的概率之和为1

正态分布的概率密度函数可以表示为:

其中,为均值,是方差

数据呈正态分布的作用

  • 统计假设前提:许多统计方法(如t检验、ANOVA)要求数据正态分布,否则结果可能失效
  • 模型性能:线性回归等模型在处理正态分布数据时表现更佳,解释性更强
  • 特征处理:正态分布的数据更适合标准化和正则化,提升模型的稳定性和泛化能力
  • 异常值检测:正态性检验有助于识别异常值,清理数据以减少干扰
  • 算法表现:K-Means等算法对正态分布数据效果更好,深度学习也通过正态化加速收敛
  • 树模型例外:决策树类算法对正态分布不敏感,但正态数据有助于更均衡的特征划分

正态性检验是数据分析和机器学习中的重要步骤,判断数据是否呈正态分布不仅可以帮助选择合适的算法和方法,还能提升模型的性能和可解释性。如果数据不符合正态分布,可以考虑通过数据变换(如对数变换、Box-Cox变换)来改善分布特征。

正态分布检验方法

正态分布检验用于判断给定的数据是否来自正态分布。常见的正态性检验方法包括:

图形法

  • Q-Q图:将样本的分位数与正态分布的理论分位数进行比较。如果样本点大致沿直线分布,说明数据接近正态分布
  • P-P图:将样本的累积分布函数(CDF)与理论分布的CDF进行比较
  • 直方图:将数据的直方图与正态分布的理论密度曲线进行对比,看是否呈现钟形

统计检验法

  • Shapiro-Wilk检验:一种广泛使用的正态性检验,假设数据来自正态分布,如果p值较小(通常小于0.05),则拒绝数据来自正态分布的假设
  • Kolmogorov-Smirnov检验:用于比较样本的经验分布与理论正态分布,适合较大样本,但对均值和方差的敏感性较弱
  • Anderson-Darling检验:对分布尾部有较高的灵敏度,是Shapiro-Wilk检验的加强版
  • Jarque-Bera检验:基于样本的偏度和峰度,检验数据是否符合正态分布。适用于大样本
  • D’Agostino’s K-squared检验:基于偏度和峰度的综合检验方法,适合较大的样本

不同检验方法对数据的敏感度不同,选择时可以根据样本量和对正态性的严格要求程度来确定。

代码实现

数据生成

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
plt.rcParams['font.family'] = 'Times New Roman'
plt.rcParams['axes.unicode_minus'] = False
np.random.seed(42)

# 生成一列正态分布数据,均值为0,标准差为1,数据量为1000
normal_data = np.random.normal(loc=0, scale=1, size=1000)

# 生成一列非正态分布数据(例如从均匀分布生成)
non_normal_data = np.random.uniform(low=-2, high=2, size=1000)

data = pd.DataFrame({
    'Normal_Distribution': normal_data,
    'Non_Normal_Distribution': non_normal_data
})

data

生成两列数据,一列为正态分布,另一列为非正态分布,接下来根据这些数据进行数据正态分布检验实现。

Q-Q图

import scipy.stats as stats

# 创建Q-Q图函数
def plot_qq(data, title):
    stats.probplot(data, dist="norm", plot=plt)
    plt.title(title)
    plt.grid(True)

# 绘制两列数据的Q-Q图
plt.figure(figsize=(12, 6), dpi=1200)
plt.subplot(1, 2, 1)
plot_qq(data['Normal_Distribution'], 'Q-Q Plot of Normal Distribution')
plt.subplot(1, 2, 2)
plot_qq(data['Non_Normal_Distribution'], 'Q-Q Plot of Non-Normal Distribution')
plt.tight_layout()
plt.savefig("Q-Q.pdf", format='pdf', bbox_inches='tight')
plt.show()

左侧图是正态分布数据的Q-Q图,点大致沿着一条直线分布,说明这列数据接近正态分布。右侧图是非正态分布数据的Q-Q图,点偏离直线较明显,说明这列数据不符合正态分布。

P-P图

# 创建P-P图函数
def plot_pp(data, title):
    sorted_data = np.sort(data)
    cdf = stats.norm.cdf(sorted_data, np.mean(sorted_data), np.std(sorted_data))
    plt.plot(cdf, np.linspace(0, 1, len(data)), marker='o', linestyle='', markersize=3)
    plt.plot([0, 1], [0, 1], 'r--')
    plt.title(title)
    plt.xlabel('Theoretical CDF')
    plt.ylabel('Empirical CDF')
    plt.grid(True)

# 绘制两列数据的P-P图
plt.figure(figsize=(12, 6), dpi=1200)
plt.subplot(1, 2, 1)
plot_pp(data['Normal_Distribution'], 'P-P Plot of Normal Distribution')
plt.subplot(1, 2, 2)
plot_pp(data['Non_Normal_Distribution'], 'P-P Plot of Non-Normal Distribution')
plt.tight_layout()
plt.savefig("P-P.pdf", format='pdf', bbox_inches='tight')
plt.show()

左侧是正态分布数据的P-P图,数据点与理论的CDF曲线大致符合,说明这列数据接近正态分布。右侧是非正态分布数据的P-P图,数据点明显偏离理论的CDF曲线,说明这列数据不符合正态分布。

直方图

# 绘制直方图函数
def plot_hist(data, title):
    plt.hist(data, bins=30, density=True, alpha=0.6, color='g')
    # 绘制正态分布曲线
    mu, std = np.mean(data), np.std(data)
    xmin, xmax = plt.xlim()
    x = np.linspace(xmin, xmax, 100)
    p = stats.norm.pdf(x, mu, std)
    plt.plot(x, p, 'k', linewidth=2)
    plt.title(title)
    plt.grid(True)

# 绘制两列数据的直方图
plt.figure(figsize=(12, 6), dpi=1200)
plt.subplot(1, 2, 1)
plot_hist(data['Normal_Distribution'], 'Histogram of Normal Distribution')
plt.subplot(1, 2, 2)
plot_hist(data['Non_Normal_Distribution'], 'Histogram of Non-Normal Distribution')
plt.tight_layout()
plt.savefig("Histogram.pdf", format='pdf', bbox_inches='tight')
plt.show()

左侧是正态分布数据的直方图,其形状接近对称的钟形,并且与叠加的正态分布曲线高度吻合,说明数据符合正态分布。右侧是非正态分布数据的直方图,其形状较为平坦,未呈现正态分布的钟形曲线,说明数据不符合正态分布。

Shapiro-Wilk检验

# 进行Shapiro-Wilk检验
shapiro_normal = stats.shapiro(data['Normal_Distribution'])
shapiro_non_normal = stats.shapiro(data['Non_Normal_Distribution'])
shapiro_results = {
    'Normal_Distribution': {
        'Statistic': shapiro_normal.statistic,
        'p-value': shapiro_normal.pvalue
    },
    'Non_Normal_Distribution': {
        'Statistic': shapiro_non_normal.statistic,
        'p-value': shapiro_non_normal.pvalue
    }
}
shapiro_results

正态分布数据:统计量为 0.9986,p 值为 0.6265,p 值大于 0.05,因此不能拒绝数据符合正态分布的假设。非正态分布数据:统计量为 0.9543,p 值非常小,p 值远小于 0.05,因此可以拒绝数据符合正态分布的假设。

Kolmogorov-Smirnov检验

# 进行Kolmogorov-Smirnov检验
ks_normal = stats.kstest(data['Normal_Distribution'], 'norm', args=(np.mean(data['Normal_Distribution']), np.std(data['Normal_Distribution'])))
ks_non_normal = stats.kstest(data['Non_Normal_Distribution'], 'norm', args=(np.mean(data['Non_Normal_Distribution']), np.std(data['Non_Normal_Distribution'])))
ks_results = {
    'Normal_Distribution': {
        'Statistic': ks_normal.statistic,
        'p-value': ks_normal.pvalue
    },
    'Non_Normal_Distribution': {
        'Statistic': ks_non_normal.statistic,
        'p-value': ks_non_normal.pvalue
    }
}
ks_results

正态分布数据:统计量为 0.0215,p 值为 0.7370,p 值大于 0.05,无法拒绝数据符合正态分布的假设。非正态分布数据:统计量为 0.0678,p 值为 0.00019,p 值远小于 0.05,因此可以拒绝数据符合正态分布的假设。

Anderson-Darling检验

# 进行Anderson-Darling检验
ad_normal = stats.anderson(data['Normal_Distribution'], dist='norm')
ad_non_normal = stats.anderson(data['Non_Normal_Distribution'], dist='norm')
ad_results = {
    'Normal_Distribution': {
        'Statistic': ad_normal.statistic,
        'Critical_Values': ad_normal.critical_values,
        'Significance_Level': ad_normal.significance_level
    },
    'Non_Normal_Distribution': {
        'Statistic': ad_non_normal.statistic,
        'Critical_Values': ad_non_normal.critical_values,
        'Significance_Level': ad_non_normal.significance_level
    }
}
ad_results

正态分布数据:统计量为 0.347,低于所有的临界值(0.574, 0.653, 0.784, 0.914, 1.088),因此我们不能拒绝数据符合正态分布的假设。非正态分布数据:统计量为 11.326,远高于所有的临界值,因此可以拒绝数据符合正态分布的假设。

Jarque-Bera检验

# 进行Jarque-Bera检验
jb_normal = stats.jarque_bera(data['Normal_Distribution'])
![](https://wy-static.wenxiaobai.com/chat-rag-image/460041978901755952)
jb_non_normal = stats.jarque_bera(data['Non_Normal_Distribution'])
jb_results = {
    'Normal_Distribution': {
        'Statistic': jb_normal.statistic,
        'p-value': jb_normal.pvalue
    },
    'Non_Normal_Distribution': {
        'Statistic': jb_non_normal.statistic,
        'p-value': jb_non_normal.pvalue
    }
}
jb_results

正态分布数据:统计量为 2.456,p 值为 0.2928,p 值大于 0.05,无法拒绝数据符合正态分布的假设。非正态分布数据:统计量为 59.919,p 值非常小,可以拒绝数据符合正态分布的假设。

D’Agostino’s K-squared检验

# 进行D'Agostino's K-squared检验
dagostino_normal = stats.normaltest(data['Normal_Distribution'])
dagostino_non_normal = stats.normaltest(data['Non_Normal_Distribution'])
dagostino_results = {
    'Normal_Distribution': {
        'Statistic': dagostino_normal.statistic,
        'p-value': dagostino_normal.pvalue
    },
    'Non_Normal_Distribution': {
        'Statistic': dagostino_non_normal.statistic,
        'p-value': dagostino_non_normal.pvalue
    }
}
dagostino_results

正态分布数据:统计量为 2.576,p 值为 0.2759,p 值大于 0.05,无法拒绝数据符合正态分布的假设。非正态分布数据:统计量为 710.37,p 值极小,可以拒绝数据符合正态分布的假设。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号