Data Science for Beginners 第 04 课:用概率与统计方法分析真实数据,基于 MLB 数据集从置信区间到假设检验的完整实践
【免费下载链接】Data-Science-For-Beginners10 Weeks, 20 Lessons, Data Science for All!项目地址: https://gitcode.com/GitHub_Trending/da/Data-Science-For-Beginners
本篇是 Data-Science-For-Beginners 课程「1-Introduction」阶段的第 04 课,系统讲解概率与统计中数据科学家必须掌握的基础数学工具:随机变量与概率分布、均值/方差/标准差、中位数与四分位数(箱线图)、正态分布、置信区间、Student t 检验假设检验、中心极限定理以及协方差与相关系数。所有概念都结合仓库自带的 SOCR_MLB.tsv 棒球队员数据集与配套 notebook.ipynb 展开,学完本篇你将能够独立地:用 Python(numpy/pandas/scipy)计算描述性统计量、绘制箱线图与直方图、生成符合正态分布的模拟样本、计算置信区间、用 t 检验对「某一群体的均值大于另一群体」这类假设做出统计上可辩护的判断,并解释数据间的线性相关关系。
1. 概率与随机变量
概率(Probability)是一个 0 到 1 之间的数,用来表达某个**事件(event)**发生的可能性。经典定义是:在等可能的前提下,有利结果数除以全部可能结果数。例如掷一颗骰子,掷出偶数的概率是 3/6 = 0.5。
讨论事件时我们使用随机变量(random variable)。例如「掷骰子得到的点数」就是一个随机变量 X,取值范围为 1 到 6。取值 1 到 6 的集合称为样本空间(sample space)。我们可以谈随机变量取某个值的概率,例如 P(X=3) = 1/6。
随机变量分两大类:
- 离散(discrete)随机变量:样本空间可数,即取值可以逐一列举,如掷骰子的点数;
- 连续(continuous)随机变量:样本空间是一个实数区间甚至全体实数 R。典型例子是「公交车到达的时间」。
2. 概率分布:离散与连续
2.1 离散分布
对离散随机变量,可以直接用一个函数 P(X) 描述每个事件发生的概率:对样本空间 S 中的每个取值 s,P(X=s) 给出一个 0 到 1 之间的数,且所有取值上的概率之和为 1。
最著名的离散分布是均匀分布(uniform distribution):样本空间含 N 个元素,每个元素以相等概率 1/N 出现。
2.2 连续分布与概率密度函数
连续变量的分布更难描述。假设变量取值于区间 [a, b] 或全体实数 R,以公交车到达时间为例:对任何一个精确的到达时刻 t,公交车恰好在该时刻到达的概率其实是 0!
也就是说,概率为 0 的事件照样经常发生——至少公交车每次到达时都发生了一次。
因此我们只能谈论变量落在某个区间内的概率,例如 P(t₁ ≤ X < t₂)。此时分布由概率密度函数(probability density function)p(x) 描述:
均匀分布的连续类比是连续均匀分布,定义在有限区间上:X 落在长度为 l 的区间内的概率与 l 成正比,随区间长度增大而增大,直到取满整个定义域时达到 1。
另一种极其重要的分布是正态分布,见下文第 5 节。
3. 均值、方差与标准差
设我们抽取随机变量 X 的 n 个样本:x₁, x₂, ..., xₙ。序列的均值(mean,算术平均)按传统方式定义为 (x₁+x₂+...+xₙ)/n。当样本量 n → ∞ 时,这个样本均值收敛到分布的期望(expectation),记作E(x)。
对任意取值为 {x₁, ..., xₙ}、对应概率为 p₁, ..., p_N 的离散分布,可以证明期望等于 E(X) = x₁p₁ + x₂p₂ + ... + x_Np_N。
为了刻画数据离散程度,计算方差:
σ² = Σ(xᵢ − μ)² / n其中 μ 是序列均值。σ 称为标准差(standard deviation),σ² 称为方差(variance)。
3.1 在配套 Notebook 中的落地
notebook.ipynb 演示了这些量的标准 Python 写法。首先是加载本仓库的数据文件(制表符分隔,无表头,共 1033 条记录 + 表头行):
import numpy as np import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv("../../data/SOCR_MLB.tsv", sep='\t', header=None, names=['Name','Team','Role','Weight','Height','Age'])注意路径
../../data/SOCR_MLB.tsv是相对 notebook 所在目录 1-Introduction/04-stats-and-probability/ 的写法;从仓库根目录看,该数据文件位于 data/SOCR_MLB.tsv,每行是姓名 球队 位置 身高 体重 年龄的 TSV 记录。
然后计算全队的均值,以及单一变量的均值/方差/标准差:
df[['Age','Height','Weight']].mean() # 各列均值 mean = df['Height'].mean() var = df['Height'].var() std = df['Height'].std() print(f"Mean = {mean}\nVariance = {var}\nStandard Deviation = {std}")在本仓库数据上实测,全量体重的均值约为201.73、样本标准差(ddof=1)约为21.02,与第 6 节置信区间表格中的 201.73 完全吻合。
4. 众数、中位数与四分位数
有时均值并不能充分代表数据的「典型值」。例如数据中混入几个远超范围的极端值时,会被显著拉偏。更稳健的指标是中位数(median):一半数据点低于它,另一半高于它。
为了理解数据分布,还要引入四分位数(quartiles):
- 第一四分位数 Q1:25% 的数据落在它之下;
- 第三四分位数 Q3:75% 的数据落在它之下。
中位数与四分位数的关系用**箱线图(box plot)**表示最直观:
在箱线图的基础上还计算两个量:
- 四分位距 IQR = Q3 − Q1;
- 离群点(outliers):落在区间 [Q1 − 1.5×IQR, Q3 + 1.5×IQR] 之外的值。
对于取值种数有限的离散/分类数据,一个好的「典型值」是出现次数最多的值,即众数(mode)。它常用于分类数据。文档中举了一个生动的反例:假设人群分成两类,一类强烈偏好红色、另一类偏好蓝色,若把颜色编码成数字后求均值,会得到一个「橙绿之间」的颜色,两类人的真实偏好都没有表达出来;而众数会落在红色或蓝色上(若两类人数相等则两者皆是,此时称样本为多峰/multimodal)。
4.1 用 Box Plot 可视化 MLB 数据
配套 notebook 中对身高绘制水平箱线图(ffill()用于把缺失的下一行值前向填充):
plt.figure(figsize=(10,2)) plt.boxplot(df['Height'].ffill(), orientation='horizontal', showmeans=True) plt.grid(color='gray', linestyle='dotted') plt.tight_layout() plt.show()由于数据包含不同球员位置(Role),还可以按位置分组画箱线图,观察各位置的身高差异(df.boxplot(column='Height', by='Role', ...))。
这张图提示我们:第一垒手(First_Baseman)的平均身高高于第二垒手(Second_Baseman)。第 7 节将学习如何用统计方法正式检验这个假设。
5. 真实世界数据:直方图与正态分布
5.1 直方图(Histogram)
分析真实数据时,数据点往往不是严格意义上的随机变量——我们并不是在反复做未知结果的实验。例如棒球队员的身高、体重、年龄这些数值并不「随机」,但我们可以把同一个人的体重序列视为从某个随机变量中抽取的样本序列。这正是统计方法能应用于观测数据的前提:
处理真实世界数据时,我们假设所有数据点都是从某个概率分布中抽取的样本。正是这个假设让我们能够应用机器学习技术、构建可用的预测模型。
仓库文档给出的前 20 个体重样本为:
[180.0, 215.0, 210.0, 210.0, 188.0, 176.0, 209.0, 200.0, 231.0, 180.0, 188.0, 180.0, 185.0, 160.0, 180.0, 185.0, 197.0, 189.0, 185.0, 219.0]体重的箱线图(均值、中位数、四分位数):
要观察数据的分布形状,绘制直方图:X 轴是不同的体重区间(bins),纵轴是样本落入该区间的次数。notebook 中的对应代码:
df['Weight'].hist(bins=15, figsize=(10,6)) plt.suptitle('Weight distribution of MLB Players') plt.xlabel('Weight') plt.ylabel('Count') plt.tight_layout() plt.show()从直方图可见:所有值围绕某个平均体重聚集,离该体重越远,对应值出现的次数越少——棒球队员体重与均值相差极大的概率很小。体重的方差则刻画了体重偏离均值的程度。
如果换成非棒球队的普通人群体重,分布的具体参数(均值、方差)会不同,但形状通常是同一种。因此,若用棒球队员训练模型再应用到大学生群体,由于底层分布不同,结果很可能是错的。
5.2 正态分布(Normal Distribution)
上面观察到的体重分布非常典型:许多真实世界的测量值都服从这一类分布,只是均值和方差不同。这就是正态分布,它在统计学中扮演核心角色。
正态分布也是生成模拟数据的正确方式。已知均值mean和标准差std后,用一行代码即可生成 1000 个体重样本:
samples = np.random.normal(mean, std, 1000)对生成样本画直方图,会得到与真实体重分布非常相似的图片。增大样本量和 bins 数后,可以逼近理想正态曲线(mean=0、std=1 的情形):
常见错误用法:Python 的伪随机数发生器默认产出的是均匀分布。notebook 特意演示了用np.random.rand(均匀分布)去模拟体重的后果——直方图呈扁平矩形,与真实的钟形分布完全不符:
wrong_sample = np.random.rand(1000) * 2 * std + mean - std # 错误示范 plt.hist(wrong_sample)结论:模拟现实中近似正态的量时,必须使用np.random.normal而不是均匀随机数。
6. 置信区间(Confidence Intervals)
讨论棒球队员体重时,我们假设存在某个随机变量 W,对应「全体棒球队员」这一总体(population)体重的理想概率分布;而我们手中的体重序列只是总体中一个样本(sample)。一个关键问题是:能否知道 W 的分布参数,即总体均值和方差?
最直接的答法是用样本的均值和方差去估计。但随机样本未必能精确代表总体,因此需要置信区间:
置信区间是在给定置信水平(confidence level)下,对总体真实均值的区间估计——即该估计以某一确定概率是准确的。
设从分布中抽取样本 X₁, ..., Xₙ。每次抽取样本都会得到不同的均值 μ,因此 μ 本身可以视为一个随机变量。置信水平为 p 的置信区间是一对值 (L_p, R_p),使得 P(L_p ≤ μ ≤ R_p) = p,即测量均值落入该区间的概率等于 p。
具体计算涉及样本均值相对于总体真均值的分布,称为Student 分布(学生分布)。
有趣的事实:Student 分布得名于数学家 William Sealy Gosset,他以化名「Student」发表论文。他供职于吉尼斯啤酒厂,据传雇主不想让公众知道他们在用统计检验来判定原料质量。
估计总体均值 μ(置信水平 p)的步骤:取 Student 分布 A 的(1−p)/2 分位数(可查表,也可用 Python、R 等统计软件的内置函数计算),则 μ 的区间为
X ± A·D/√n其中 X 是样本均值,D 是标准差。(文档说明:自由度(degrees of freedom)概念对理解 Student 分布很重要,此处从略,可查阅统计学专著深入了解。)
6.1 仓库中的实现代码
notebook.ipynb 给出了可直接运行的实现(引用自 Stack Overflow 的经典配方):
import scipy.stats def mean_confidence_interval(data, confidence=0.95): a = 1.0 * np.array(data) n = len(a) m, se = np.mean(a), scipy.stats.sem(a) h = se * scipy.stats.t.ppf((1 + confidence) / 2., n-1) return m, h for p in [0.85, 0.9, 0.95]: m, h = mean_confidence_interval(df['Weight'].ffill(), p) print(f"p={p:.2f}, mean = {m:.2f} ± {h:.2f}")关键点逐行解释:
| 代码 | 含义 |
|---|---|
scipy.stats.sem(a) | 标准误差 SE = D/√n,即样本均值的标准差 |
scipy.stats.t.ppf((1 + confidence) / 2., n-1) | Student t 分布的分位数函数,取 (1+p)/2 处分位数,自由度为 n−1 |
h = se * t临界值 | 区间半宽,对应公式 X ± A·D/√n 中的增量部分 |
课程文档给出的体重均值置信区间结果:
| p(置信水平) | Weight mean |
|---|---|
| 0.85 | 201.73 ± 0.94 |
| 0.90 | 201.73 ± 1.08 |
| 0.95 | 201.73 ± 1.28 |
置信水平越高,置信区间越宽——这是置信区间的基本性质:要更有把握,就得付出区间变宽的代价。
7. 假设检验(Hypothesis Testing)
7.1 从分组统计到假设
MLB 数据集按位置分组后的统计量(notebook 中用df.groupby('Role').agg({'Weight':'mean','Height':'mean','Age':'count'})计算),如下表:
| Role | Height | Weight | Count |
|---|---|---|---|
| Catcher | 72.723684 | 204.328947 | 76 |
| Designated_Hitter | 74.222222 | 220.888889 | 18 |
| First_Baseman | 74.000000 | 213.109091 | 55 |
| Outfielder | 73.010309 | 199.113402 | 194 |
| Relief_Pitcher | 74.374603 | 203.517460 | 315 |
| Second_Baseman | 71.362069 | 184.344828 | 58 |
| Shortstop | 71.903846 | 182.923077 | 52 |
| Starting_Pitcher | 74.719457 | 205.163636 | 221 |
| Third_Baseman | 73.044444 | 200.955556 | 45 |
可以看到第一垒手的平均身高(74.00)高于第二垒手(71.36),于是我们可能想断言「第一垒手比第二垒手更高」。
这个陈述称为假设(hypothesis),因为我们尚不知道它实际上是否成立。
由于每个均值都有对应的置信区间,这种差异也可能只是统计误差。我们需要更正式的检验方法。分别计算两类球员身高的置信区间:
| Confidence | First Basemen | Second Basemen |
|---|---|---|
| 0.85 | 73.62..74.38 | 71.04..71.69 |
| 0.90 | 73.56..74.44 | 70.99..71.73 |
| 0.95 | 73.47..74.53 | 70.92..71.81 |
| 计算方式 |
|---|
对df.loc[df['Role']=='First_Baseman',['Height']]与df.loc[df['Role']=='Second_Baseman',['Height']]分别套用第 6 节的mean_confidence_interval函数,p 取 0.85/0.90/0.95 |
在任何一个置信水平下两组区间都不重叠,这支持了「第一垒手更高」的假设。
7.2 Student t 检验
更正式的表述是:判断两个概率分布是否相同(或至少参数是否相同)。分布不同适用的检验也不同;若已知两个分布都服从正态分布,可以应用Student t 检验。
其原理:计算所谓的t 值(t-value),它刻画了两个均值之差(已计入方差影响)。可以证明 t 值服从Student 分布,从而能求出给定置信水平 p 的阈值(计算或查数值表)。将 t 值与阈值比较,即可接受或拒绝假设。
在 Python 中,SciPy包的ttest_ind函数会替你算出 t 值,并反查出 p 值(p-value),你只需看置信度即可下结论。
示例一:First Baseman vs Second Baseman(notebook 中第 32 个代码单元格的写法)
from scipy.stats import ttest_ind tval, pval = ttest_ind(df.loc[df['Role']=='First_Baseman',['Height']], df.loc[df['Role']=='Second_Baseman',['Height']], equal_var=False) print(f"T-value = {tval[0]:.2f}\nP-value: {pval[0]}")equal_var=False表示使用 Welch 版本(不假设两组方差相等)。t 检验返回的两个值含义如下(摘自 notebook):
- p-value可理解为「两个分布均值相同」这一原假设的概率。本例中它极低,说明有强证据支持第一垒手更高;
- t-value是归一化均值之差的中间量,会与给定置信水平对应的阈值比较。
示例二:README 文档给出的对比(First Baseman vs Designated_Hitter)
from scipy.stats import ttest_ind tval, pval = ttest_ind(df.loc[df['Role']=='First_Baseman',['Height']], df.loc[df['Role']=='Designated_Hitter',['Height']], equal_var=False) print(f"T-value = {tval[0]:.2f}\nP-value: {pval[0]}")文档记录的运行结果:
T-value = 7.65 P-value: 9.137321189738925e-12p 值非常低(约 10⁻¹¹ 量级),意味着有极强的统计学证据支持所检验的差异真实存在。
除了均值比较,文档还列举了其他常见的假设类型:
- 证明给定样本服从某个分布(本例中我们假设身高服从正态分布,但这需要正式的统计验证,例如正态性检验);
- 证明样本均值等于某个预设值;
- 比较多个样本的均值(例如不同年龄段人群的幸福感差异)。
8. 大数定律与中心极限定理
正态分布如此重要,一个原因在于中心极限定理(Central Limit Theorem):设有一个大样本,N 个独立值 X₁, ..., X_N 来自任意一个均值为 μ、方差为 σ² 的分布。当 N 足够大(N → ∞)时,样本均值会服从正态分布,均值为 μ,方差为 σ²/N。
另一种理解方式:无论你从什么分布出发,只要计算任意随机变量取值的(大样本)均值,最终都会得到正态分布。
由中心极限定理还可推出:当 N → ∞ 时,样本均值等于 μ 的概率趋于 1,这就是大数定律(Law of Large Numbers)。
8.1 在 Notebook 中用 CLT 模拟正态分布
Python 内置伪随机数发生器产出的是均匀分布。如果想自己造一个正态分布生成器,可以直接利用中心极限定理——对一批均匀分布样本取均值即可得到近似正态的值:
import random def normal_random(sample_size=100): sample = [random.uniform(0, 1) for _ in range(sample_size)] return sum(sample) / sample_size sample = [normal_random() for _ in range(100)] plt.hist(sample) plt.show()对 100 个这样的「100 个均匀随机数的均值」画直方图,可以看到钟形轮廓——这是对中心极限定理最直观的一次演示。
9. 协方差与相关系数
数据科学的一大任务就是发现数据之间的关系。当两个序列同时表现出相似行为时,我们说它们相关(correlate):要么同涨同跌,要么一涨一跌。
相关不一定意味着因果。两个变量可能都受某个外部原因驱动,甚至只是碰巧相关。但强数学相关是两者之间存在某种联系的良好信号。
刻画两个随机变量关系的核心数学概念是协方差(covariance):
Cov(X, Y) = E[(X − E(X))·(Y − E(Y))]计算两个变量各自偏离其均值的量,再求这些偏差乘积的期望。若两变量同向偏离(同时偏高或同时偏低),乘积恒为正,累加得到正协方差;若反向偏离(一个偏高时另一个偏低),累加得到负协方差;若偏差相互独立,则正负相抵,协方差接近 0。
但协方差的绝对值受变量本身量级影响,难以直接比较大小。把协方差除以两个变量的标准差,就得到相关系数(correlation),它被归一化到 [−1, 1]:1 表示强正相关,−1 表示强负相关,0 表示完全不相关(两变量独立)。
9.1 实战:身高与体重的相关性
计算 MLB 球员身高与体重的相关系数(使用第 5 节提到的ffill()处理缺失值):
print(np.corrcoef(df['Height'].ffill(), df['Weight']))得到相关系数矩阵(correlation matrix):
array([[1. , 0.52959196], [0.52959196, 1. ]])相关系数矩阵 C 可以对任意多条输入序列 S₁...Sₙ 计算。C_ij 是 S_i 与 S_j 的相关系数,对角线元素恒为 1(即 S_i 与自身的自相关)。
本例中 0.53 说明身高与体重之间存在一定程度的正相关。把其中一个值对另一个画散点图可以更直观地看到关系:
9.2 「邪恶棒球公司」:协方差矩阵与人为线性关系
notebook 中还设计了一个教学示例:假设某「邪恶棒球公司」按身高付薪水——身高每高 1%,底薪 $1000 之外最多加 $100 奖金:
heights = df['Height'].ffill() salaries = 1000 + (heights - heights.min()) / (heights.max() - heights.mean()) * 100np.cov给出协方差矩阵,np.corrcoef给出相关系数矩阵:
print(f"Covariance matrix:\n{np.cov(heights, salaries)}") print(f"Covariance = {np.cov(heights, salaries)[0,1]}") print(f"Correlation = {np.corrcoef(heights, salaries)[0,1]}")相关系数等于 1,说明两变量间是强线性关系,散点图会落在一条直线上。随后 notebook 引入非线性和噪声来演示相关系数的退化过程:
# 加入 sin 非线性 salaries = 1000 + np.sin((heights - heights.min()) / (heights.max() - heights.mean())) * 100 print(f"Correlation = {np.corrcoef(heights, salaries)[0,1]}") # 再叠加 ±10 的均匀随机噪声 salaries = (1000 + np.sin((heights - heights.min()) / (heights.max() - heights.mean())) * 100 + np.random.random(size=len(heights)) * 20 - 10) print(f"Correlation = {np.corrcoef(heights, salaries)[0,1]}")相关系数随之逐渐变小——这正是「相关系数衡量的是线性关系强度」的含义。
9.3 一个重要的数据陷阱:NaN 会污染相关系数
notebook 中直接计算真实两列的相关时遇到了一个典型问题:
np.corrcoef(df['Height'].ffill(), df['Weight']) # 结果全是 nan原因是Weight列含缺失值(NaN),而任一输入含 NaN 都会让整个相关系数变为未定义(从矩阵结构看,Height 的自相关能正常算出 1,而 Weight 的自相关为 NaN,即可定位问题列)。解决办法是先做数据准备:
np.corrcoef(df['Height'].ffill(), df['Weight']) # 补全后可正常计算,约 0.53这个例子说明了数据准备与清洗的重要性:没有干净的数据,任何统计计算都无从谈起。
10. 本课程内容小结
本课覆盖了以下核心知识点(对应 README 的 Conclusion 一节):
- 数据的基本统计性质:均值、方差、众数、四分位数;
- 随机变量的不同分布,包括正态分布;
- 如何求不同属性之间的相关性;
- 如何用数学与统计的严谨工具来检验假设;
- 如何基于数据样本计算随机变量的置信区间。
概率与统计是一个足够庞大、值得单独开设课程的主题,上述内容只提供了良好起点。
11. 动手挑战与糖尿病数据集作业
11.1 课程挑战(Challenge)
用 notebook 中的样本代码检验以下其他假设:
- 第一垒手比第二垒手更老(older);
- 第一垒手比第三垒手更高;
- 游击手(Shortstops)比第二垒手更高。
可直接套用第 7 节的模式:df.loc[df['Role']==..., ['Age'/'Height']]取出对应子列,再交给scipy.stats.ttest_ind(..., equal_var=False),观察 p 值是否足够小即可。
11.2 配套作业:小型糖尿病研究
本课的作业是 Small Diabetes Study,使用仓库中的 data/diabetes.tsv(442 条患者记录),列结构为:
AGE SEX BMI BP S1 S2 S3 S4 S5 S6 Y例如第一条记录:59, 2, 32.1, 101, 157, 93.2, 38, 4, 4.8598, 87, 151。作业要求(详见 assignment.ipynb,参考答案在 solution/assignment.ipynb):
- 计算所有变量的均值与方差;
- 按性别(SEX)绘制 BMI、BP、Y 的箱线图;
- 分析 Age、Sex、BMI、Y 各自服从什么分布;
- 检验不同变量与疾病进展指标 Y 的相关性;
- 检验假设:男性与女性的糖尿病进展程度(Y)是否存在差异(即第 7 节 t 检验的完整应用)。
作业评分标准(Rubric):优秀 = 所有任务完成、有图形展示与解释;合格 = 多数任务完成但缺少解释或从图表得出的结论;需改进 = 只完成了均值/方差和基础绘图,未从数据中得出任何结论。
12. 延伸学习
- 概率与统计课程级别的深入资料:NYU 的 Carlos Fernandez-Granda 的讲义Probability and Statistics for Data Science;
- Peter & Andrew Bruce 的Practical Statistics for Data Scientists(附 R 语言示例代码);
- James D. Miller 的Statistics for Data Science(附 R 语言示例代码)。
本课由 Dmitry Soshnikov 编写。文中所有计算均可在仓库配套 notebook 1-Introduction/04-stats-and-probability/notebook.ipynb 中复现,数据文件位于 data/SOCR_MLB.tsv;依赖 Python 环境安装numpy、pandas、scipy、matplotlib四个包即可运行。
【免费下载链接】Data-Science-For-Beginners10 Weeks, 20 Lessons, Data Science for All!项目地址: https://gitcode.com/GitHub_Trending/da/Data-Science-For-Beginners
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考