离散概率分布全指南:从理论到Python实战

2025-11-07 16:29:21
文章摘要
想知道发送100封邮件有多少人会点击,或抽查10件产品出现次品的概率吗? 本文将带你掌握7种最常用的离散概率分布(如伯努利、二项、泊松分布)。通过完整Python代码和实际案例(如A/B测试、智能质检),帮你把可数结果转化为精准的概率预测,用数据指导你的决策!

你在做用户转化分析时,是否遇到过这样的问题。

发送100封邮件,预计有多少人会点击?

或者在质检时想知道,抽查10件产品,出现次品的概率是多少?

这些看似简单的业务问题,背后都离不开离散概率分布。

本文会带你掌握数据科学中最常用的7种离散分布,并且以完整代码实现。

 

一、  什么是离散概率分布?为什么你需要它

一句话理解核心概念

离散概率分布就是给"可数结果"分配概率的工具。比如:

● 掷骰子(1~6点)

● 统计网站访客数量(0人、1人、2人...)

● 判断邮件是否被打开(打开/未打开)

它和连续分布(如身高、体重)的本质区别是:离散分布处理整数或类别,连续分布处理小数测量值。


为什么它对你的工作有用?

Netflix用它做推荐算法、银行用它评估信用风险、制造业用它做质量抽检。具体到开发场景:

 

二、  数学基础扫盲

在进入具体分布前,先理解三个核心工具:

1.  概率质量函数(PMF)

告诉你每个结果的精确概率。

两条铁律:

1.  所有概率 ≥ 0(不能出现负概率)

2.  所有概率加起来 = 1(总得有个结果发生)

实战例子

# 掷骰子的PMF
outcomes = [1, 2, 3, 4, 5, 6]
probabilities = [1/6] * 6  # 每面概率1/6
print(sum(probabilities))  # 输出: 1.0

 

2. 累积分布函数(CDF)

告诉你"结果≤某个值"的累积概率。

举例:掷骰子"点数≤3"的概率 = P(1) + P(2) + P(3) = 0.5


3.  期望值与方差

期望值(均值):长期平均结果

方差:结果的波动程度

# 快速计算示例
import numpy as np
X = [0, 1, 2, 3]
P = [0.1, 0.2, 0.3, 0.4]

mean = sum(x * p for x, p in zip(X, P))  # 期望值
variance = sum((x - mean)**2 * p for x, p in zip(X, P))  # 方差
print(f"均值: {mean}, 方差: {variance}")

 

三、  核心分布实战指南

1.  伯努利分布:最简单的"成功/失败"模型

适用场景:单次事件,只有两种结果

● 用户是否点击广告

● 邮件是否被打开

● 产品是否合格

公式:P(X=1) = p,P(X=0) = 1-p

Python实现

from scipy.stats import bernoulli

# 场景:邮件打开率60%
p = 0.6
rv = bernoulli(p)

print(f"打开概率: {rv.pmf(1)}")  # 0.6
print(f"未打开概率: {rv.pmf(0)}")  # 0.4
print(f"期望值: {rv.mean()}")  # 0.6

 

AI应用示例(本地):

import random

def classify_sentiment_safe(text, use_api=False):
    """安全的情绪分类函数,带本地备选"""
    if not use_api:
        # 本地简单实现
        positive_words = ['好', '优秀', '棒', '不错', '满意', '喜欢', '赞']
        negative_words = ['差', '糟糕', '烂', '不好', '失望', '讨厌']
        
        text_lower = text.lower()
        positive_count = sum(1 for word in positive_words if word in text_lower)
        negative_count = sum(1 for word in negative_words if word in text_lower)
        
        if positive_count > negative_count:
            return 1
        elif negative_count > positive_count:
            return 0
        else:
            return random.randint(0, 1)  # 随机
    
    try:
        # API调用(需要配置有效密钥)
        import requests
        API_KEY = "你的硅基流动API_KEY"
        if API_KEY == "你的硅基流动API_KEY":
            print("请配置有效API_KEY,使用本地模拟")
            return classify_sentiment_safe(text, use_api=False)
            
        MODEL = "THUDM/GLM-4-9B-0414"
        response = requests.post(
            "https://api.siliconflow.cn/v1/chat/completions",
            headers={"Authorization": f"Bearer {API_KEY}"},
            json={
                "model": MODEL,
                "messages": [{"role": "user", "content": f"判断情感(正面=1/负面=0):{text}"}]
            },
            timeout=10
        )
        response.raise_for_status()
        result = response.json()['choices'][0]['message']['content']
        return 1 if "1" in result or "正面" in result else 0
    except Exception as e:
        print(f"API调用失败: {e},使用本地模拟")
        return classify_sentiment_safe(text, use_api=False)

# 模拟100次分类,统计成功率
results = [classify_sentiment_safe("这个产品很好用", use_api=False) for _ in range(100)]
success_rate = sum(results) / len(results)
print(f"模型正面判断率: {success_rate:.2f}")

 

 

2.  二项分布:重复N次独立试验

适用场景:多次重复伯努利试验

● 100封邮件有多少人会点击

● 生产100件产品有多少件不合格

核心假设:

1.  每次试验成功概率相同

2.  试验次数固定

3.  各次试验独立

Python实现:

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

def setup_chinese_font():
    """设置中文字体,避免乱码"""
    try:
        plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans']
        plt.rcParams['axes.unicode_minus'] = False
        return True
    except:
        print("中文显示设置失败,使用默认字体")
        return False

# 场景:发送100封邮件,点击率30%
n, p = 100, 0.3
rv = binom(n, p)

# 计算恰好40人点击的概率
prob_40 = rv.pmf(40)
print(f"恰好40人点击: {prob_40:.4f}")

# 计算至少20人点击的概率
prob_at_least_20 = 1 - rv.cdf(19)
print(f"至少20人点击: {prob_at_least_20:.4f}")

# 可视化
setup_chinese_font()
x = range(0, 61)  # 只显示0-60范围
plt.figure(figsize=(10, 6))
plt.bar(x, [rv.pmf(i) for i in x], alpha=0.7)
plt.xlabel('点击人数')
plt.ylabel('概率')
plt.title('二项分布PMF (n=100, p=0.3)')
plt.tight_layout()
plt.show()

 

实战应用:A/B测试显著性检验

def ab_test_significance(clicks_a, trials_a, clicks_b, trials_b):
    """判断两个版本转化率是否有显著差异"""
    try:
        from scipy.stats import binomtest
        
        rate_a = clicks_a / trials_a
        rate_b = clicks_b / trials_b
        
        # 使用精确二项检验
        result = binomtest(clicks_b, trials_b, rate_a, alternative='two-sided')
        p_value = result.pvalue
        
        if p_value < 0.05 and abs(rate_b - rate_a) > 0.01:  # 添加最小效果阈值
            return f"版本B显著不同 (p={p_value:.4f}, 提升: {(rate_b-rate_a):.2%})"
        else:
            return f"无显著差异 (p={p_value:.4f})"
    
    except ImportError:
        from scipy.stats import chi2_contingency
        import numpy as np
        obs = np.array([[clicks_a, trials_a-clicks_a], 
                       [clicks_b, trials_b-clicks_b]])
        _, p_value, _, _ = chi2_contingency(obs)
        return f"卡方检验: p={p_value:.4f}"

# 测试数据
result = ab_test_significance(clicks_a=300, trials_a=1000, 
                               clicks_b=350, trials_b=1000)
print(result)

 

3.  泊松分布:预测"稀有但稳定"的事件

适用场景:

● 客服中心每小时来电数

● 网站每分钟访问量

● 系统每天故障次数

核心特征:均值 = 方差(这是识别泊松分布的关键)

Python实现:

from scipy.stats import poisson

# 场景:客服平均每小时接3通电话
lambda_rate = 3
rv = poisson(mu=lambda_rate)

# 计算1小时内无来电的概率
prob_0 = rv.pmf(0)
print(f"无来电概率: {prob_0:.4f}")  # 约5%

# 计算接到5通以上电话的概率
prob_above_5 = 1 - rv.cdf(5)
print(f"超过5通电话: {prob_above_5:.4f}")

# 实际应用:人力规划
print(f"95%置信区间内最多接听: {rv.ppf(0.95):.0f}通")

 

结合AI的异常检测应用

import numpy as np
from scipy.stats import poisson

def detect_anomaly(observed_count, lambda_rate, threshold=0.05):
    """检测观测值是否异常(基于泊松分布)"""
    rv = poisson(mu=lambda_rate)
    p_value = 1 - rv.cdf(observed_count - 1)  # P(X >= observed_count)
    
    if p_value < threshold:
        return f"⚠️ 异常!观测值{observed_count}超出正常范围(p={p_value:.4f})"
    return f"✓ 正常范围(p={p_value:.4f})"

# 示例:正常情况下每小时3次API调用,今天突然来了10次
print(detect_anomaly(observed_count=10, lambda_rate=3))

 

4.  几何分布:第一次成功需要多少次

适用场景:

● 抽奖系统:平均多少次能中奖

● 获客成本:联系多少个客户能成交第一单

关键特性:无记忆性(前面失败不影响后续概率)

Python实现:

from scipy.stats import geom

# 场景:转化率20%,第一次成功平均需要多少次
p = 0.2
rv = geom(p)

print(f"期望尝试次数: {rv.mean()}")  # 5次
print(f"第3次成功的概率: {rv.pmf(3):.4f}")

# 实战:计算获客成本
cost_per_contact = 50  # 每次联系成本50元
expected_cost = rv.mean() * cost_per_contact
print(f"预期获客成本: {expected_cost:.0f}元")

 

5.  超几何分布:不放回抽样的精确计算

适用场景:

● 质检抽样(样本量小,不能放回)

● 抽卡游戏概率(牌堆固定)

与二项分布的区别:超几何是不放回,二项是放回

Python实现:

from scipy.stats import hypergeom

# 场景:100件产品中有8件次品,抽查10件
N, K, n = 100, 8, 10  # 总数、次品数、抽样数
rv = hypergeom(M=N, n=K, N=n)

# 计算至少抽到1件次品的概率
prob_at_least_1 = 1 - rv.pmf(0)
print(f"至少1件次品: {prob_at_least_1:.4f}")  # 58.34%

# 对比:如果用二项分布(放回抽样)会怎样
from scipy.stats import binom
rv_binom = binom(n=10, p=8/100)
prob_binom = 1 - rv_binom.pmf(0)
print(f"二项分布结果: {prob_binom:.4f}")  # 56.51%(略有偏差)

 

四、  进阶分布速览

1.  分类分布:多类别场景

import numpy as np
from scipy.stats import rv_discrete

# 场景:用户可能访问首页/产品页/关于页
pages = ["home", "product", "about"]
probs = [0.5, 0.3, 0.2]

rv = rv_discrete(values=(np.arange(len(probs)), probs))
print(f"访问产品页概率: {rv.pmf(1)}")  # 0.3

 

2.  离散均匀分布:完全随机

from scipy.stats import randint

# 场景:随机抽取1~100的整数
rv = randint(low=1, high=101)
print(f"抽到50的概率: {rv.pmf(50)}")  # 0.01

 

五、  完整环境配置与避坑指南

1.  环境安装

# 创建虚拟环境
conda create -n prob_dist python=3.9
conda activate prob_dist

# 安装核心库(使用国内镜像加速)
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple numpy scipy matplotlib requests

# 或使用阿里云镜像
pip install -i https://mirrors.aliyun.com/pypi/simple/ numpy scipy matplotlib requests

 

2.  常见报错解决

报错1:AttributeError: module 'scipy.stats' has no attribute 'binom'

# 解决:升级scipy版本
pip install --upgrade scipy

 

报错2:泊松分布参数错误

# 错误写法
rv = poisson(3)  # ❌ 缺少参数名

# 正确写法
rv = poisson(mu=3)  # ✓ 必须指定mu

 

报错3:超几何分布参数顺序混淆

# scipy的参数顺序:M(总数), n(成功数), N(抽样数)
rv = hypergeom(M=100, n=8, N=10)  # 正确
# 不要和数学公式的N、K、n混淆!

 

六、  实战项目:构建智能质检系统

综合运用多个分布,构建一个完整的质检决策系统:

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

class QualityControl:
    def __init__(self, batch_size, defect_rate, sample_size):
        self.batch_size = batch_size
        self.defect_rate = defect_rate
        self.sample_size = sample_size
    
    def sampling_plan(self, accept_threshold):
        """计算抽样方案的接收概率"""
        expected_defects = int(self.batch_size * self.defect_rate)
        rv = hypergeom(M=self.batch_size, n=expected_defects, N=self.sample_size)
        
        prob_accept = rv.cdf(accept_threshold)
        return prob_accept
    
    def predict_daily_defects(self, production_rate):
        """预测每日次品数(泊松分布)"""
        lambda_rate = production_rate * self.defect_rate
        rv = poisson(mu=lambda_rate)
        
        return {
            'expected': rv.mean(),
            '95%_upper': rv.ppf(0.95),
            'prob_zero': rv.pmf(0)
        }
    
    def compare_distributions(self):
        """比较超几何分布和二项分布的差异"""
        expected_defects = int(self.batch_size * self.defect_rate)
        
        # 超几何分布(不放回)
        hyper_rv = hypergeom(M=self.batch_size, n=expected_defects, N=self.sample_size)
        
        # 二项分布(放回)
        binom_rv = binom(n=self.sample_size, p=self.defect_rate)
        
        x_values = range(0, min(self.sample_size, expected_defects) + 1)
        
        plt.figure(figsize=(10, 6))
        plt.bar([x - 0.2 for x in x_values], 
                [hyper_rv.pmf(x) for x in x_values], 
                width=0.4, label='超几何分布', alpha=0.7)
        plt.bar([x + 0.2 for x in x_values], 
                [binom_rv.pmf(x) for x in x_values], 
                width=0.4, label='二项分布', alpha=0.7)
        
        plt.xlabel('次品数量')
        plt.ylabel('概率')
        plt.title('抽样分布比较')
        plt.legend()
        plt.tight_layout()
        plt.show()

# 使用示例
if __name__ == "__main__":
    # 设置中文字体
    plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei']
    plt.rcParams['axes.unicode_minus'] = False
    
    qc = QualityControl(batch_size=1000, defect_rate=0.02, sample_size=50)
    
    # 场景1:抽样方案评估
    accept_prob = qc.sampling_plan(accept_threshold=2)
    print(f"抽到≤2件次品时接收批次的概率: {accept_prob:.2%}")
    
    # 场景2:日产量预测
    daily_stats = qc.predict_daily_defects(production_rate=500)
    print(f"预期每日次品数: {daily_stats['expected']:.1f}件")
    print(f"95%置信上限: {daily_stats['95%_upper']:.0f}件")
    print(f"无次品概率: {daily_stats['prob_zero']:.2%}")
    
    # 场景3:分布比较
    qc.compare_distributions()


是单次事件?

├─ 是 → 伯努利分布

└─ 否 → 是固定次数重复试验?

├─ 是 → 二项分布

└─ 否 → 是计数事件频率?

├─ 是 → 泊松分布

└─ 否 → 是等待第一次成功?

├─ 是 → 几何分布

└─ 否 → 是不放回抽样?

├─ 是 → 超几何分布

└─ 否 → 检查其他分布


进阶学习路径

理论深化:学习矩母函数(MGF)理解分布性质

连续分布:掌握正态分布、指数分布

统计检验:卡方检验、t检验

贝叶斯推断:将概率分布与先验知识结合

声明:该内容由作者自行发布,观点内容仅供参考,不代表平台立场;如有侵权,请联系平台删除。