用 Python 计算联合概率和条件概率
前言
概率论听起来像是数学课本里枯燥的公式,但实际上它无处不在——你早上出门前纠结“要不要带伞”,本质上就是在做一次概率判断。
今天我们要聊的联合概率和条件概率,是概率论中最基础也最实用的两个概念。
联合概率和条件概率到底是什么?
联合概率
联合概率,简单来说,就是两件事同时发生的概率。
比如:今天既下雨、你又选择了打车上班的概率。这两件事需要同时满足,缺一不可。
数学上,事件 A 和事件 B 同时发生的概率记作 P(A ∩ B) 或 P(A, B)。
🌰 生活类比:联合概率就像在问——“一个随机选出来的日子里,既下雨又是周一”的概率有多大?两个条件都要满足。
条件概率
条件概率则不太一样。它问的是:在已知某件事已经发生的前提下,另一件事发生的概率。
比如:已知今天下雨了,在这种情况下你选择打车上班的概率是多少?这里“下雨”是已知条件,我们只关注下雨天里你打车的比例。
数学上,在事件 B 已经发生的条件下,事件 A 发生的概率记作 P(A | B),读作“在 B 条件下 A 的概率”。
🌰 生活类比:条件概率就像在问——“在所有周一的样本里,有多少比例同时在下雨?”你先把范围缩小到“周一”,再看其中下雨的比例。
两者的核心区别
一句话总结:联合概率看的是“交集占总体”的比例,条件概率看的是“交集占条件”的比例。
如果你有一个数据集,联合概率的分母是全部数据,而条件概率的分母是符合条件的那一部分数据。这个区别是理解后续代码的关键。
场景示例——天气与通勤方式
理论说完了,我们来构建一个具体的生活场景。
假设你记录了自己过去 200 个工作日的通勤数据,包括两个信息:
- 天气状况:晴天(Sunny)、雨天(Rainy)、阴天(Cloudy)
- 通勤方式:步行(Walk)、骑车(Bike)、打车(Taxi)
数据如下(200 天的统计结果):
| 天气 \ 通勤方式 | 步行 | 骑车 | 打车 | 合计 |
|---|---|---|---|---|
| 晴天 | 40 | 35 | 15 | 90 |
| 雨天 | 5 | 10 | 45 | 60 |
| 阴天 | 25 | 15 | 10 | 50 |
| 合计 | 70 | 60 | 70 | 200 |
这个表格能帮我们回答很多有趣的问题:
- “某天既下雨又打车”的概率是多少?——联合概率
- “已知下雨了,当天打车”的概率是多少?——条件概率
- “已知打车了,当天是下雨天”的概率是多少?——另一个方向的条件概率
代码实现
准备数据
首先,我们需要把上面的表格转换成 Python 可以处理的形式。
最直接的方式是用 pandas 创建一个 DataFrame:
import pandas as pd
import numpy as np
# 创建原始数据:展开成 200 行
data = {
'Weather': ['Sunny']*90 + ['Rainy']*60 + ['Cloudy']*50,
'Commute': (
['Walk']*40 + ['Bike']*35 + ['Taxi']*15 +
['Walk']*5 + ['Bike']*10 + ['Taxi']*45 +
['Walk']*25 + ['Bike']*15 + ['Taxi']*10
)
}
df = pd.DataFrame(data)
print(df.head())
print(f"\n总记录数: {len(df)}")
输出:
Weather Commute
0 Sunny Walk
1 Sunny Walk
2 Sunny Walk
3 Sunny Walk
4 Sunny Walk
总记录数: 200
计算联合概率
联合概率 = 同时满足两个条件的记录数 / 总记录数。
比如,计算“雨天且打车”的联合概率:
total = len(df)
# 计算联合概率:雨天 AND 打车
joint_count = len(df[(df['Weather'] == 'Rainy') & (df['Commute'] == 'Taxi')])
joint_prob = joint_count / total
print(f"P(雨天, 打车) = {joint_count} / {total} = {joint_prob:.3f}")
# 输出: P(雨天, 打车) = 45 / 200 = 0.225
你可以用同样的方式计算任意组合:
# 晴天且步行
joint_sunny_walk = len(df[(df['Weather'] == 'Sunny') & (df['Commute'] == 'Walk')]) / total
print(f"P(晴天, 步行) = {joint_sunny_walk:.3f}") # 0.200
# 阴天且骑车
joint_cloudy_bike = len(df[(df['Weather'] == 'Cloudy') & (df['Commute'] == 'Bike')]) / total
print(f"P(阴天, 骑车) = {joint_cloudy_bike:.3f}") # 0.075
计算条件概率
条件概率 = 同时满足两个条件的记录数 / 满足条件的记录数。
比如,计算“已知下雨的情况下,打车”的概率:
# 先筛选出下雨天的所有记录
rainy_data = df[df['Weather'] == 'Rainy']
rainy_total = len(rainy_data)
# 在下雨天的记录中,统计打车的数量
taxi_in_rainy = len(rainy_data[rainy_data['Commute'] == 'Taxi'])
conditional_prob = taxi_in_rainy / rainy_total
print(f"P(打车 | 雨天) = {taxi_in_rainy} / {rainy_total} = {conditional_prob:.3f}")
# 输出: P(打车 | 雨天) = 45 / 60 = 0.750
注意这里的分母是 60(下雨天的总数),而不是总记录数 200。这就是联合概率和条件概率最本质的区别。
再看几个例子:
# 已知晴天的情况下,步行的概率
sunny_data = df[df['Weather'] == 'Sunny']
prob_walk_given_sunny = len(sunny_data[sunny_data['Commute'] == 'Walk']) / len(sunny_data)
print(f"P(步行 | 晴天) = {prob_walk_given_sunny:.3f}") # 40/90 = 0.444
# 已知打车的情况下,下雨的概率
taxi_data = df[df['Commute'] == 'Taxi']
prob_rainy_given_taxi = len(taxi_data[taxi_data['Weather'] == 'Rainy']) / len(taxi_data)
print(f"P(雨天 | 打车) = {prob_rainy_given_taxi:.3f}") # 45/70 = 0.643
封装成通用函数
为了方便复用,可以把上面的逻辑封装成函数:
def joint_probability(df, col1, val1, col2, val2):
"""
计算两个事件同时发生的联合概率
"""
total = len(df)
count = len(df[(df[col1] == val1) & (df[col2] == val2)])
return count / total
def conditional_probability(df, given_col, given_val, target_col, target_val):
"""
计算在给定条件下,目标事件发生的概率
P(target_val | given_val)
"""
subset = df[df[given_col] == given_val]
if len(subset) == 0:
return 0.0
count = len(subset[subset[target_col] == target_val])
return count / len(subset)
# 使用示例
print(joint_probability(df, 'Weather', 'Rainy', 'Commute', 'Taxi')) # 0.225
print(conditional_probability(df, 'Weather', 'Rainy', 'Commute', 'Taxi')) # 0.750
用透视表一次看全
如果想一次性看到所有组合的联合概率和条件概率,可以用 pandas 的 crosstab 和归一化功能:
# 联合概率表(所有组合的概率之和 = 1)
joint_table = pd.crosstab(df['Weather'], df['Commute'], normalize='all')
print("联合概率表:")
print(joint_table.round(3))
输出:
Commute Bike Taxi Walk
Weather
Cloudy 0.075 0.050 0.125
Rainy 0.050 0.225 0.025
Sunny 0.175 0.075 0.200
# 条件概率表(按行归一化:每行的和 = 1)
# 即 P(通勤方式 | 天气)
conditional_table = pd.crosstab(df['Weather'], df['Commute'], normalize='index')
print("\n条件概率表 P(通勤方式 | 天气):")
print(conditional_table.round(3))
输出:
Commute Bike Taxi Walk
Weather
Cloudy 0.300 0.200 0.500
Rainy 0.167 0.750 0.083
Sunny 0.389 0.167 0.444
从条件概率表中可以直观地看到:下雨天打车概率高达 75%,而晴天步行概率也有 44%——这些数字背后藏着你的通勤习惯。
这两个概率能用在哪里?
理解这两个概念之后,你会发现它们几乎可以用在任何涉及“两个因素同时考虑”的场景中:
| 场景 | 联合概率问的是 | 条件概率问的是 |
|---|---|---|
| 电商推荐 | 用户是女性 且 购买了某商品 | 已知用户是女性,购买某商品的概率 |
| 健康管理 | 年龄大于 50 且 有高血压 | 已知年龄大于 50,有高血压的概率 |
| 金融风控 | 信用分低 且 逾期还款 | 已知信用分低,逾期还款的概率 |
| 市场营销 | 居住在一线城市 且 订阅了会员 | 已知居住在一线城市,订阅会员的概率 |
| 天气预报 | 早上阴天 且 下午下雨 | 已知早上阴天,下午下雨的概率 |
本质上,只要你有一个二维(或多维)的数据集,就可以用这两个工具来挖掘其中的规律。
总结
回顾一下文章的内容:
- 联合概率 P(A, B) = 两件事同时发生的概率,分母是全部数据
- 条件概率 P(A | B) = 在 B 已发生的前提下 A 发生的概率,分母是符合条件 B 的数据
- 两者通过公式 P(A, B) = P(A | B) × P(B) 相互联系
- Python 实现的核心就是筛选数据 + 计数 + 做除法,代码非常简洁
概率不是玄学,它就是用数据来量化不确定性。
下次当你面临一个“两件事同时考虑”的决策时,不妨想想:我是在问联合概率,还是条件概率?答案本身,可能就是解决问题的关键一步。

浙公网安备 33010602011771号