Python正态分布生成、可视化与统计检验实战

方予
方予
发布时间:2026-10-12| 3人浏览过
原创

本文详解如何使用Python生成正态分布数据,并通过Matplotlib绘制概率分布图与离散图。内容涵盖基于均值和标准差的随机数生成、正态性假设检验(如Scipy的normaltest)、置信区间计算以及基于四分位距(IQR)的异常值识别方法,附带完整代码与图表解析。

生成正态分布数据并绘制概率分布图

在数据分析中,生成符合特定统计特征的正态分布数据是基础操作。Python的 numpy 库提供了 np.random.normal 函数,允许用户指定均值(loc)和标准差(scale)来生成随机数。同时,为了直观展示数据的分布形态,可以使用 matplotlib 将理论概率密度函数(PDF)与实际生成的数据直方图进行叠加对比。

核心代码实现

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# 根据均值、标准差,求指定范围的正态分布概率值
def normfun(x, mu, sigma):
  pdf = np.exp(-((x - mu)**2)/(2*sigma**2)) / (sigma * np.sqrt(2*np.pi))
  return pdf

# 生成正态分布数据:均值为15,标准差为44,共100个样本
result = np.random.normal(15, 44, 100)
print(result)

# 设定 x 轴范围,基于生成数据的最小值和最大值
x = np.arange(min(result), max(result), 0.1)
# 计算实际数据的均值和标准差
print(result.mean(), result.std())
# 计算理论正态分布的 y 轴值
y = normfun(x, result.mean(), result.std())

# 绘制理论正态分布概率曲线
plt.plot(x, y)
# 绘制实际数据的直方图(归一化)
plt.hist(result, bins=10, rwidth=0.8, density=True)
plt.title('distribution')
plt.xlabel('temperature')
plt.ylabel('probability')
plt.show()

上述代码中,np.random.normal(15, 44, 100) 生成了均值为 15、标准差为 44 的 100 个随机数。绘图时,plt.hist 的 density=True 参数确保直方图的总面积为 1,从而可以与理论概率密度曲线进行有效对比。需要注意的是,由于正态分布理论上覆盖从负无穷到正无穷的区间,而绘图仅截取了数据的最小值到最大值,因此图中概率总和可能不完全等于 1。

判断序列是否符合正态分布

在实际应用中,验证一组数据是否服从正态分布至关重要。Scipy 库中的 stats.normaltest 函数基于 D'Agostino-Pearson 检验,能够综合偏度和峰度来评估数据是否符合正态分布。该检验的原假设(Null Hypothesis)是数据来自正态分布。

假设检验逻辑

import numpy as np
from scipy import stats

pts = 1000
np.random.seed(28041990)
# 生成两个不同的正态分布序列
a = np.random.normal(0, 1, size=pts)
b = np.random.normal(2, 1, size=pts)
# 将两个分布拼接,理论上形成非正态分布序列
x = np.concatenate((a, b))

# 执行正态性检验
k2, p = stats.normaltest(x)
alpha = 1e-3  # 显著性水平
print("p = {:g}".format(p))

# 根据 p 值判断原假设
if p < alpha:
  print("The null hypothesis can be rejected") # 拒绝原假设,即不是正态分布
else:
  print("The null hypothesis cannot be rejected") # 无法拒绝原假设,可能是正态分布

在示例中,我们将两个均值不同(0 和 2)但标准差相同(1)的正态分布序列拼接,理论上这会导致数据呈现双峰分布,从而偏离正态分布。通过 stats.normaltest 得到的 p 值若小于设定的显著性水平(如 1e-3),则拒绝原假设,判定该序列不符合正态分布。

求置信区间与异常值检测

除了分布形态,统计推断中常需计算置信区间以评估数据的波动范围,并识别异常值。置信区间反映了在给定置信水平下,总体参数可能存在的范围。而异常值检测通常基于四分位距(IQR)法则,即超出 [Q1 - 1.5*IQR, Q3 + 1.5*IQR] 范围的数据点被视为异常值。

置信区间与异常值计算

import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
import pandas as pd

# 求列表数据的异常点
def get_outer_data(data_list):
  df = pd.DataFrame(data_list, columns=['value'])
  df = df.iloc[:, 0]
  # 计算下四分位数和上四分位
  Q1 = df.quantile(q=0.25)
  Q3 = df.quantile(q=0.75)
  # 基于1.5倍的四分位差计算上下须对应的值
  low_whisker = Q1 - 1.5 * (Q3 - Q1)
  up_whisker = Q3 + 1.5 * (Q3 - Q1)
  # 寻找异常点
  kk = df[(df > up_whisker) | (df < low_whisker)]
  data1 = pd.DataFrame({'id': kk.index, '异常值': kk})
  return data1

N = 100
result = np.random.normal(0, 1, N)
mean, std = result.mean(), result.std(ddof=1) # 求均值和标准差

# 计算90%置信区间
conf_intveral = stats.norm.interval(0.9, loc=mean, scale=std)
print('置信区间:', conf_intveral)

x = np.arange(0, len(result), 1)
# 求异常值
outer = get_outer_data(result)
print(outer, type(outer))
x1 = outer.iloc[:, 0]
y1 = outer.iloc[:, 1]

# 绘图:离散点、异常点及置信区间线
plt.scatter(x1, y1, marker='x', color='r') # 所有离散点
plt.scatter(x, result, marker='.', color='g') # 异常点
plt.plot([0, len(result)], [conf_intveral[0], conf_intveral[0]])
plt.plot([0, len(result)], [conf_intveral[1], conf_intveral[1]])
plt.show()

在 get_outer_data 函数中,通过计算 Q1 和 Q3 得到四分位距 IQR,进而确定上下界。置信区间则通过 stats.norm.interval 计算,这里设定置信水平为 0.9(即 90%)。绘图时,红色 'x' 标记异常值,绿色 '.' 标记正常离散点,水平线表示置信区间边界。

采样点离散图和概率图综合绘制

为了更全面地展示数据特征,可以将离散图(显示具体数据点及异常值)与概率图(显示分布形态)组合在同一图表中。此外,代码还处理了标准差为 0 的极端情况(即所有数据点相同),此时无法计算置信区间和概率密度,需进行特殊处理。

综合可视化代码

import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
import pandas as pd
import time

print(time.strftime('%Y-%m-%D %H:%M:%S'))

# 根据均值、标准差,求指定范围的正态分布概率值
def _normfun(x, mu, sigma):
  pdf = np.exp(-((x - mu)**2)/(2*sigma**2)) / (sigma * np.sqrt(2*np.pi))
  return pdf

# 求列表数据的异常点
def get_outer_data(data_list):
  df = pd.DataFrame(data_list, columns=['value'])
  df = df.iloc[:, 0]
  Q1 = df.quantile(q=0.25)
  Q3 = df.quantile(q=0.75)
  low_whisker = Q1 - 1.5 * (Q3 - Q1)
  up_whisker = Q3 + 1.5 * (Q3 - Q1)
  kk = df[(df > up_whisker) | (df < low_whisker)]
  data1 = pd.DataFrame({'id': kk.index, '异常值': kk})
  return data1

N = 100
result = np.random.normal(0, 1, N)
mean, std = result.mean(), result.std(ddof=1)

# 处理标准差为0的情况
if std == 0:
  conf_intveral = [min(result)-1, max(result)+1]
else:
  conf_intveral = stats.norm.interval(0.9, loc=mean, scale=std)

outer = get_outer_data(result)
fig = plt.figure()
fig.add_subplot(2, 1, 1)
plt.subplots_adjust(hspace=0.3)
x = np.arange(0, len(result), 1)
plt.scatter(x, result, marker='.', color='g') # 画所有离散点
plt.scatter(outer.iloc[:, 0], outer.iloc[:, 1], marker='x', color='r') # 画异常离散点
plt.plot([0, len(result)], [conf_intveral[0], conf_intveral[0]]) # 置信区间线条
plt.plot([0, len(result)], [conf_intveral[1], conf_intveral[1]]) # 置信区间线条
plt.text(0, conf_intveral[0], '{:.2f}'.format(conf_intveral[0])) # 置信区间数字显示
plt.text(0, conf_intveral[1], '{:.2f}'.format(conf_intveral[1])) # 置信区间数字显示
info = 'outer count:{}'.format(len(outer.iloc[:, 0]))
plt.text(min(x), max(result)-((max(result)-min(result)) / 2), info) # 异常点数显示
plt.xlabel('sample count')
plt.ylabel('value')

# 绘制概率图
if std != 0:
  fig.add_subplot(2, 1, 2)
  x = np.arange(min(result), max(result), 0.1)
  y = _normfun(x, result.mean(), result.std())
  plt.plot(x, y) # 这里画出理论的正态分布概率曲线
  plt.hist(result, bins=10, rwidth=0.8, density=True) # bins个柱状图,宽度是rwidth(0~1),=1没有缝隙
  info = 'mean:{:.2f}\nstd:{:.2f}\nmode num:{:.2f}'.format(mean, std, np.median(result))
  plt.text(min(x), max(y) / 2, info)
  plt.xlabel('value')
  plt.ylabel('Probability')
else:
  fig.add_subplot(2, 1, 2)
  info = 'non-normal distribution!!\nmean:{:.2f}\nstd:{:.2f}\nmode num:{:.2f}'.format(mean, std, np.median(result))
  plt.text(0.5, 0.5, info)
  plt.xlabel('value')
  plt.ylabel('Probability')
plt.savefig('./distribution.jpg')
plt.show()

print(time.strftime('%Y-%m-%D %H:%M:%S'))

该综合示例首先检查标准差是否为 0。若不为 0,则正常计算置信区间并绘制概率密度曲线与直方图;若为 0(所有数据相同),则提示“非正态分布”并显示均值和标准差信息。图表分为上下两部分:上部展示采样点的离散分布及异常值标记,下部展示数据的概率分布形态。最终结果保存为 distribution.jpg。

总结

通过上述步骤,我们完成了正态分布数据的生成、可视化、正态性检验以及异常值与置信区间的计算。这些工具在数据预处理、统计分析及质量控制中具有广泛应用。掌握 numpy 的随机数生成、scipy 的统计检验以及 matplotlib 的绘图技巧,能够有效提升数据分析的效率与准确性。

以上就是Python生成正态分布数据并绘图与统计检验的详细内容,更多关于Python正态分布、数据可视化、假设检验的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。