用Python的NumPy和SciPy玩转均匀分布:从骰子模拟到销售预测实战
用Python的NumPy和SciPy玩转均匀分布:从骰子模拟到销售预测实战
概率论中的均匀分布就像一位公平的裁判——它给每个可能的结果分配完全相同的获胜机会。想象一下掷骰子:1点到6点每个数字出现的概率都是1/6,这就是均匀分布在现实中最直观的体现。但对于数据科学家和Python开发者来说,均匀分布的价值远不止于理解概率概念,它更是生成随机数、模拟真实场景的强大工具。
在Python生态中,NumPy和SciPy这对黄金组合为我们提供了操作均匀分布的全套装备。无论是生成服从均匀分布的随机数,还是计算概率密度、累积分布,亦或是可视化分析,这些库都能让我们用几行代码就实现专业级的概率计算。本文将带你从零开始,通过三个逐渐深入的实战案例,掌握均匀分布在数据分析中的实际应用。
1. 环境准备与基础概念
1.1 必备Python库安装
在开始之前,确保你的Python环境已经安装了以下核心库:
pip install numpy scipy matplotlib ipython这些库各司其职:
- NumPy:提供高效的数组操作和随机数生成
- SciPy:包含完整的统计分布函数
- Matplotlib:实现专业的数据可视化
- IPython:增强交互式体验(特别是Jupyter Notebook用户)
1.2 均匀分布的数学本质
均匀分布分为两种类型:
- 离散均匀分布:适用于有限个等概率结果(如骰子)
- 连续均匀分布:适用于某一区间内的连续值
其概率密度函数(PDF)数学表示为:
f(x) = 1/(b-a) 当a ≤ x ≤ b f(x) = 0 其他情况其中关键参数:
a(loc):分布的下界b(scale):分布的上界
在SciPy中,uniform函数使用loc和scale参数,其中scale实际上是区间宽度(b-a)。
2. 骰子模拟:理解离散均匀分布
2.1 模拟单次掷骰子
让我们从最经典的例子开始——模拟骰子游戏。传统骰子有6个面,每个面出现的概率完全相同,是离散均匀分布的完美示例。
import numpy as np # 设置随机种子保证结果可复现 np.random.seed(42) # 模拟掷骰子 def roll_dice(): return np.random.randint(1, 7) print(f"骰子结果: {roll_dice()}")运行多次你会发现,每次结果都在1到6之间随机出现,这正是离散均匀分布的特性。
2.2 大数定律验证
概率论告诉我们,当试验次数足够多时,每个结果出现的频率会趋近于理论概率1/6。让我们用代码验证:
import matplotlib.pyplot as plt # 模拟10000次掷骰子 results = np.random.randint(1, 7, size=10000) # 统计各面出现次数 unique, counts = np.unique(results, return_counts=True) frequencies = counts / len(results) # 可视化 plt.bar(unique, frequencies, color='skyblue', alpha=0.7) plt.axhline(y=1/6, color='red', linestyle='--', label='理论概率') plt.xlabel('骰子点数') plt.ylabel('出现频率') plt.title('骰子点数分布验证') plt.legend() plt.show()你会看到随着试验次数增加,各柱状图高度逐渐接近红色虚线表示的理论概率。
2.3 进阶:自定义面数的骰子
均匀分布的强大之处在于它的通用性。我们可以轻松扩展代码来模拟任意面数的骰子:
def roll_dice_advanced(n_sides=6, n_rolls=1): return np.random.randint(1, n_sides+1, size=n_rolls) # 模拟20面骰子掷10次 print(f"20面骰子结果: {roll_dice_advanced(20, 10)}")3. 连续均匀分布实战:销售预测模型
3.1 花店日销售量建模
假设一家花店的历史数据显示,每日销售量均匀分布在10到40束之间。我们可以用连续均匀分布来建模:
from scipy.stats import uniform # 定义分布参数 a = 10 # 最小销量 b = 40 # 最大销量 scale = b - a # 创建均匀分布对象 sales_dist = uniform(loc=a, scale=scale)3.2 计算特定销售区间的概率
现在回答一些实际的业务问题:
问题1:日销售量在15到30束之间的概率是多少?
prob_15_to_30 = sales_dist.cdf(30) - sales_dist.cdf(15) print(f"销售量在15-30束的概率: {prob_15_to_30:.2%}")问题2:日销售量超过20束的概率是多少?
prob_over_20 = 1 - sales_dist.cdf(20) print(f"销售量超过20束的概率: {prob_over_20:.2%}")3.3 可视化概率密度与累积分布
理解分布最直观的方式就是可视化。让我们绘制PDF和CDF:
# 生成x值 x = np.linspace(a-5, b+5, 500) # 创建画布 plt.figure(figsize=(12, 6)) # PDF plt.subplot(1, 2, 1) plt.plot(x, sales_dist.pdf(x), 'b-', lw=2, label='PDF') plt.fill_between(x, sales_dist.pdf(x), where=(x>=15)&(x<=30), color='red', alpha=0.2, label='P(15≤X≤30)') plt.title('概率密度函数(PDF)') plt.xlabel('销售量') plt.ylabel('概率密度') plt.legend() # CDF plt.subplot(1, 2, 2) plt.plot(x, sales_dist.cdf(x), 'g-', lw=2, label='CDF') plt.axvline(x=20, color='orange', linestyle='--', label='X=20') plt.title('累积分布函数(CDF)') plt.xlabel('销售量') plt.ylabel('累积概率') plt.legend() plt.tight_layout() plt.show()这张图清晰地展示了:
- 左图:红色区域对应15-30束的销售概率
- 右图:橙色虚线显示20束对应的累积概率
4. 参数影响分析与蒙特卡洛模拟
4.1 loc和scale参数的影响
均匀分布的形状完全由loc(a)和scale(b-a)决定。让我们比较不同参数下的分布:
params = [ {'loc': 0, 'scale': 1, 'label': '标准均匀[0,1]', 'color': '#008fd5'}, {'loc': 2, 'scale': 3, 'label': '均匀[2,5]', 'color': '#fc4f30'}, {'loc': -1, 'scale': 2, 'label': '均匀[-1,1]', 'color': '#e5ae38'} ] plt.figure(figsize=(10, 6)) x = np.linspace(-2, 6, 500) for param in params: dist = uniform(loc=param['loc'], scale=param['scale']) plt.plot(x, dist.pdf(x), label=param['label'], color=param['color'], lw=2) plt.fill_between(x, dist.pdf(x), color=param['color'], alpha=0.1) plt.title('不同参数的均匀分布比较') plt.xlabel('值') plt.ylabel('概率密度') plt.legend() plt.grid(True, alpha=0.3) plt.show()从图中可以直观看到:
loc决定分布的位置(左右移动)scale决定分布的宽度(区间长度)
4.2 蒙特卡洛模拟应用
均匀分布是蒙特卡洛模拟的基础。让我们用它来估算圆周率π:
def estimate_pi(n_samples=100000): # 在单位正方形内随机撒点 points = np.random.uniform(-1, 1, size=(n_samples, 2)) # 计算落在单位圆内的比例 inside_circle = np.sum(np.linalg.norm(points, axis=1) <= 1) # 估算π值 return 4 * inside_circle / n_samples pi_estimate = estimate_pi() print(f"π的估算值: {pi_estimate:.6f} (误差: {abs(pi_estimate-np.pi)/np.pi:.2%})")这个模拟展示了如何利用均匀分布的随机性来解决确定性问题。增加样本量n_samples可以提高估算精度。
5. 实际业务场景扩展应用
5.1 库存优化决策
回到花店案例,假设:
- 每束花利润:50元
- 未售出损失:20元(存储成本)
- 每日进货量需要决策
我们可以用均匀分布模拟不同进货量下的预期利润:
def expected_profit(order_quantity, a=10, b=40): # 生成可能的需求场景 demands = np.linspace(a, b, 1000) prob_density = uniform(loc=a, scale=b-a).pdf(demands) # 计算每种需求下的利润 profits = np.where(demands >= order_quantity, order_quantity * 50, # 需求≥进货量 demands * 50 - (order_quantity - demands) * 20) # 需求<进货量 # 计算期望利润 return np.trapz(profits * prob_density, demands) # 评估不同进货量 order_quantities = np.arange(15, 41, 5) profits = [expected_profit(q) for q in order_quantities] # 找出最优进货量 optimal_idx = np.argmax(profits) print(f"最优进货量: {order_quantities[optimal_idx]}束, 预期利润: {profits[optimal_idx]:.2f}元") # 可视化 plt.plot(order_quantities, profits, 'o-') plt.axvline(x=order_quantities[optimal_idx], color='r', linestyle='--') plt.xlabel('进货量') plt.ylabel('预期利润(元)') plt.title('不同进货量下的预期利润') plt.grid(True, alpha=0.3) plt.show()这个分析帮助花店在不确定需求的情况下做出最优库存决策。
5.2 A/B测试中的随机分组
均匀分布在实验设计中也大有用武之地。例如在A/B测试中,我们需要将用户随机分配到不同实验组:
def ab_test_assignment(user_ids, n_groups=2): # 为每个用户生成均匀随机数 random_values = np.random.uniform(0, 1, size=len(user_ids)) # 根据随机数分配组别 groups = np.floor(random_values * n_groups).astype(int) return dict(zip(user_ids, groups)) # 模拟用户ID user_ids = [f"user_{i}" for i in range(1000)] group_assignment = ab_test_assignment(user_ids) # 检查分组平衡性 from collections import Counter print("分组统计:", Counter(group_assignment.values()))这种方法确保每个用户被分配到任何组的概率完全相同,避免了选择偏差。
