用Python可视化秒懂概率分布:从理论到实战的趣味探索

概率论常被视为数据科学和机器学习的数学基石,但传统教材中晦涩的公式推导往往让初学者望而生畏。本文将带你用Python的可视化工具,重新发现概率密度函数(PDF)和累积分布函数(CDF)的直观本质。通过动手绘制经典概率分布的图形,你将获得比死记硬背公式更深刻的理解。

1. 环境准备与基础概念

在开始绘制之前,我们需要配置Python环境和理解几个核心概念。首先确保安装了以下库:

!pip install numpy matplotlib seaborn scipy

概率密度函数(PDF)描述连续随机变量在某个取值点的概率"密度",而累积分布函数(CDF)则表示随机变量小于等于某值的概率。对于离散变量,我们使用概率质量函数(PMF)代替PDF。

关键区别:PDF在某点的值不是概率,只有对PDF在区间内积分才能得到概率;而CDF在任何点的值都是确切的概率。

让我们用生活中的例子理解这两个函数:

  • PDF就像温度曲线——某时刻的温度值本身没有意义,只有一段时间内的温度变化才有意义
  • CDF则像累计降雨量——直接告诉我们从开始到某时刻总共下了多少雨

2. 离散分布可视化实战

我们从简单的离散分布开始,用Python可视化二项分布和泊松分布。

2.1 二项分布的可视化

二项分布描述n次独立试验中成功次数的概率分布。假设我们抛10次硬币,看看正面朝上的概率分布:

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import binom

n, p = 10, 0.5
x = np.arange(0, n+1)
pmf = binom.pmf(x, n, p)
cdf = binom.cdf(x, n, p)

plt.figure(figsize=(12,5))
plt.subplot(121)
plt.bar(x, pmf, color='skyblue')
plt.title('PMF of Binomial Distribution')
plt.xlabel('Number of successes')
plt.ylabel('Probability')

plt.subplot(122)
plt.step(x, cdf, where='post', color='salmon')
plt.title('CDF of Binomial Distribution')
plt.xlabel('Number of successes')
plt.ylabel('Cumulative Probability')
plt.tight_layout()
plt.show()

这段代码会生成并排的两个图表:

  • 左侧PMF图显示每种可能结果(0到10次正面)的概率
  • 右侧CDF图呈阶梯状上升,显示累积概率

2.2 泊松分布的可视化

泊松分布常用于描述单位时间内随机事件发生的次数。假设某商店平均每小时有5位顾客光临:

from scipy.stats import poisson

mu = 5
x = np.arange(0, 15)
pmf = poisson.pmf(x, mu)
cdf = poisson.cdf(x, mu)

plt.figure(figsize=(12,5))
plt.subplot(121)
plt.bar(x, pmf, color='lightgreen')
plt.title('PMF of Poisson Distribution (μ=5)')
plt.xlabel('Number of events')
plt.ylabel('Probability')

plt.subplot(122)
plt.step(x, cdf, where='post', color='orange')
plt.title('CDF of Poisson Distribution (μ=5)')
plt.xlabel('Number of events')
plt.ylabel('Cumulative Probability')
plt.tight_layout()
plt.show()

观察图表可以发现:

  • PMF在均值附近(5)达到峰值
  • CDF逐渐趋近于1,表示所有可能性的总和为100%

3. 连续分布可视化实战

连续分布的可视化能更直观展示PDF和CDF的关系。我们以最常用的正态分布为例。

3.1 正态分布的可视化

from scipy.stats import norm

mu, sigma = 0, 1
x = np.linspace(-4, 4, 1000)
pdf = norm.pdf(x, mu, sigma)
cdf = norm.cdf(x, mu, sigma)

plt.figure(figsize=(12,5))
plt.subplot(121)
plt.plot(x, pdf, 'b-', lw=2)
plt.title('PDF of Standard Normal Distribution')
plt.xlabel('Value')
plt.ylabel('Density')

plt.subplot(122)
plt.plot(x, cdf, 'r-', lw=2)
plt.title('CDF of Standard Normal Distribution')
plt.xlabel('Value')
plt.ylabel('Cumulative Probability')
plt.tight_layout()
plt.show()

这个可视化揭示了几个关键点:

  1. PDF曲线下的总面积等于1
  2. CDF是PDF的积分,单调递增
  3. 在μ=0处,CDF值为0.5,表示有50%的概率取值小于均值

3.2 PDF与CDF的面积关系

为了更直观理解PDF和CDF的关系,我们可以添加阴影区域:

plt.figure(figsize=(12,5))

# PDF with shaded area
plt.subplot(121)
plt.plot(x, pdf, 'b-', lw=2)
plt.fill_between(x[x<=1], pdf[x<=1], color='skyblue', alpha=0.5)
plt.title('PDF: P(X ≤ 1) = Area under curve')
plt.xlabel('Value')
plt.ylabel('Density')

# Corresponding CDF point
plt.subplot(122)
plt.plot(x, cdf, 'r-', lw=2)
plt.scatter(1, norm.cdf(1), color='darkred', s=100)
plt.axhline(norm.cdf(1), linestyle='--', color='gray')
plt.axvline(1, linestyle='--', color='gray')
plt.title('CDF: P(X ≤ 1) = '+str(round(norm.cdf(1),3)))
plt.xlabel('Value')
plt.ylabel('Cumulative Probability')
plt.tight_layout()
plt.show()

这个可视化清晰地展示了:

  • PDF中x≤1曲线下的面积(左侧图)
  • 等于CDF在x=1处的值(右侧图)
  • 两者通过积分/微分相关联

4. 高级可视化技巧

掌握了基础可视化后,我们可以创建更丰富的图形来深入理解概率分布。

4.1 比较不同参数的分布

比较不同参数的正态分布有助于理解μ和σ的影响:

params = [(0,1), (0,2), (1,1), (-1,0.5)]
x = np.linspace(-5,5,1000)

plt.figure(figsize=(10,6))
for mu, sigma in params:
    pdf = norm.pdf(x, mu, sigma)
    plt.plot(x, pdf, label=f'μ={mu}, σ={sigma}')
plt.title('Normal Distributions with Different Parameters')
plt.xlabel('Value')
plt.ylabel('Density')
plt.legend()
plt.grid(True)
plt.show()

从图中可以直观看到:

  • μ决定分布的中心位置
  • σ决定分布的"胖瘦"程度
  • σ越小,曲线越"瘦高"

4.2 交互式可视化

使用Plotly创建交互式图表能进一步提升探索体验:

import plotly.graph_objects as go
from ipywidgets import interact

def interactive_normal(mu=0, sigma=1):
    x = np.linspace(mu-4*sigma, mu+4*sigma, 200)
    pdf = norm.pdf(x, mu, sigma)
    cdf = norm.cdf(x, mu, sigma)
    
    fig = go.Figure()
    fig.add_trace(go.Scatter(x=x, y=pdf, name='PDF', line=dict(color='blue')))
    fig.add_trace(go.Scatter(x=x, y=cdf, name='CDF', line=dict(color='red')))
    
    fig.update_layout(
        title=f'Normal Distribution (μ={mu}, σ={sigma})',
        xaxis_title='Value',
        yaxis_title='Density/Probability',
        hovermode='x unified'
    )
    fig.show()

interact(interactive_normal, mu=(-3,3,0.1), sigma=(0.1,2,0.1))

这种交互式可视化允许你:

  • 滑动调整μ和σ参数
  • 实时观察PDF和CDF的变化
  • 悬停查看精确数值

4.3 3D可视化多元正态分布

对于更高级的探索,我们可以可视化二元正态分布:

from scipy.stats import multivariate_normal
from mpl_toolkits.mplot3d import Axes3D

mu = [0, 0]
cov = [[1, 0.5], [0.5, 1]]
x, y = np.mgrid[-3:3:.1, -3:3:.1]
pos = np.dstack((x, y))
rv = multivariate_normal(mu, cov)

fig = plt.figure(figsize=(12,6))
ax1 = fig.add_subplot(121, projection='3d')
ax1.plot_surface(x, y, rv.pdf(pos), cmap='viridis')
ax1.set_title('3D View of Bivariate Normal PDF')

ax2 = fig.add_subplot(122)
contour = ax2.contourf(x, y, rv.pdf(pos))
plt.colorbar(contour)
ax2.set_title('Contour Plot of Bivariate Normal PDF')
plt.tight_layout()
plt.show()

这个可视化展示了:

  • 两个随机变量的联合概率密度
  • 3D曲面图和等高线图两种表示方式
  • 变量间的相关性(通过协方差矩阵控制)

5. 实际应用案例

理论结合实践才能融会贯通。让我们看几个概率分布在实际问题中的应用。

5.1 假设检验可视化

假设检验是统计推断的核心方法,可视化可以帮我们理解p值和临界区域的概念:

# 生成两组模拟数据
group1 = np.random.normal(5, 1, 100)
group2 = np.random.normal(6, 1, 100)

# 计算t检验
from scipy.stats import ttest_ind
t_stat, p_val = ttest_ind(group1, group2)

# 可视化
plt.figure(figsize=(10,6))
sns.kdeplot(group1, label='Group 1', shade=True)
sns.kdeplot(group2, label='Group 2', shade=True)
plt.title(f'Distribution Comparison\np-value: {p_val:.4f}')
plt.xlabel('Value')
plt.ylabel('Density')
plt.legend()
plt.show()

这个案例展示了:

  • 如何用核密度估计(KDE)可视化数据分布
  • 如何直观理解两组数据的差异
  • p值在图形中的含义(分布重叠程度)

5.2 蒙特卡洛模拟可视化

蒙特卡洛方法依靠随机采样解决问题,可视化能清晰展示其工作原理。例如估算π值:

n_samples = 10000
points = np.random.uniform(-1, 1, (n_samples, 2))
inside = (points[:,0]**2 + points[:,1]**2) <= 1
pi_estimate = 4 * np.sum(inside) / n_samples

plt.figure(figsize=(8,8))
plt.scatter(points[inside,0], points[inside,1], c='blue', s=1, alpha=0.5)
plt.scatter(points[~inside,0], points[~inside,1], c='red', s=1, alpha=0.5)
plt.title(f'Monte Carlo Estimation of π: {pi_estimate:.4f}')
plt.axis('equal')
plt.show()

这个可视化生动展示了:

  • 随机点在单位圆内外的分布
  • 如何通过面积比例估算π值
  • 随着样本量增加,估计值会越来越精确

5.3 贝叶斯更新可视化

贝叶斯统计中的先验、似然和后验分布通过可视化更容易理解:

from scipy.stats import beta

# 先验分布 (假设我们对硬币公平性没有强烈先验)
alpha_prior, beta_prior = 1, 1
# 观察数据 (40次正面,60次反面)
alpha_likelihood, beta_likelihood = 40, 60
# 后验分布
alpha_post = alpha_prior + alpha_likelihood
beta_post = beta_prior + beta_likelihood

x = np.linspace(0, 1, 100)
plt.figure(figsize=(10,6))
plt.plot(x, beta.pdf(x, alpha_prior, beta_prior), label='Prior')
plt.plot(x, beta.pdf(x, alpha_likelihood+1, beta_likelihood+1), label='Likelihood')
plt.plot(x, beta.pdf(x, alpha_post, beta_post), label='Posterior')
plt.title('Bayesian Update of Coin Flip Probability')
plt.xlabel('Probability of Heads')
plt.ylabel('Density')
plt.legend()
plt.show()

这个案例清晰地展示了:

  • 先验分布如何表示初始信念
  • 似然函数如何表示数据证据
  • 后验分布如何结合前两者
  • 随着数据量增加,后验分布会更集中于真实参数附近
Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐