想象你是一位班主任。期末考试刚结束,五个班的数学成绩堆在你桌上。你要向年级组长汇报,但不能把几百个分数念一遍——你得把数据"压缩"成几个数,让人一听就懂。
统计学干的就是这件事:用尽量少的数字,把数据最重要的特征说清楚。
算术平均值
最直觉的"代表值"。把所有数加起来,除以个数:
其中 是算术平均值, 是第 个观测值, 是观测个数。
假设班里五个人的考试成绩是 72、78、85、90、95。平均分就是把这五个数加起来再除以 5,得到 84。
scores = [72, 78, 85, 90, 95]
mean_score = sum(scores) / len(scores)
print(f"算术平均值: {mean_score}")

算术平均值
平均值的优点是好算、好懂。但它有个致命弱点——被极端值拖着跑。如果班上来了个考 20 分的转学生,平均分直接从 84 掉到 73.3,但其他五个人的水平其实没变。
所以我们需要别的指标来补充。
几何平均值
算术平均值适合"加法关系"的数据,比如考试分数叠加。但有些数据是"乘法关系"——比如投资收益率。
你买了只基金,第一年涨 50%,第二年跌 50%。算术平均告诉你平均收益是 0%,好像没亏没赚。但实际呢?100 块 × 1.5 × 0.5 = 75 块,亏了 25%。算术平均在这里骗了你。
几何平均才对。把各期收益率加 1 后连乘、再开 次方,最后减 1:
对于收益率场景,,最终结果再减 1 还原为收益率。
import numpy as np
returns = [0.50, -0.50]
geo_mean = np.prod([1 + r for r in returns]) ** (1 / len(returns)) - 1
arith_mean = np.mean(returns)
print(f"算术平均收益率: {arith_mean:.2%}")
print(f"几何平均收益率: {geo_mean:.2%}")
actual = 100 * (1 + returns[0]) * (1 + returns[1])
print(f"100元两年后实际值: {actual:.1f}元")

几何平均值
几何平均值永远是"保守"的那一方。只要数据有波动,几何平均就小于算术平均。金融里看长期回报,几何平均才是真朋友。
中位数
回到考试成绩。五个人考了 72、78、85、90、95,中位数就是中间那个——85。如果有六个人,比如 20、72、78、85、90、95,中位数取中间两个的平均,即 (78+85)/2 = 81.5。
中位数为奇数为偶数
其中 是排序后的数据。
scores_with_outlier = [20, 72, 78, 85, 90, 95]
median_score = np.median(scores_with_outlier)
mean_score = np.mean(scores_with_outlier)
print(f"含极端值的均值: {mean_score:.1f}")
print(f"含极端值的中位数: {median_score:.1f}")

中位数
中位数不怕极端值捣乱。房价统计里常用中位数,就是因为几套上亿的豪宅就能把均值拉飞,但中位数纹丝不动。当数据偏态严重时,中位数比均值更能代表"典型情况"。
众数
出现次数最多的那个数。班上 30 个人的鞋码,穿 42 码的有 9 人最多,42 就是众数。售货员进货看众数,不看平均。
众数
其中 是值 在数据中出现的频次。
众数可以不止一个。如果 42 码和 43 码都是 9 个人,那就叫双众数。
shoe_sizes = [38, 39, 40, 40, 41, 41, 42, 42, 42, 42, 42, 42, 42, 42, 42,
43, 43, 43, 43, 43, 43, 43, 43, 43, 44, 44, 44, 45, 46, 47]
from scipy import stats as sp_stats
mode_result = sp_stats.mode(shoe_sizes, keepdims=True)
print(f"众数: {mode_result.mode[0]},出现 {mode_result.count[0]} 次")

众数
均值、中位数、众数的选取没有标准答案,取决于数据特性和你要回答的问题。正态分布时三者几乎相等,偏态分布时三者分家——均值被尾巴拖着走,中位数稳在中间,众数守在峰顶。
期望
期望是概率论版的均值。区别在于:均值是对已有数据求平均,期望是对概率模型求加权平均。
其中 是所有可能的结果, 是该结果发生的概率。
掷骰子,点数 1 到 6 概率各 1/6。期望 = 1×(1/6) + 2×(1/6) + ... + 6×(1/6) = 3.5。你没掷过一次,但数学告诉你"长期来看"的结果就是 3.5。
outcomes = np.array([1, 2, 3, 4, 5, 6])
probs = np.array([1/6] * 6)
expectation = np.sum(outcomes * probs)
print(f"骰子点数的期望: {expectation}")

期望
现实中的彩票也有期望。花 2 块钱买一张,中奖概率 1%,奖金 100 块。期望收益 = 100×0.01 + 0×0.99 = 1 块,低于票价 2 块。长期买一定亏。赌场同理,每局的期望收益都是负的,这就是"赌场优势"。
当数据量大且每个观测等概率时,样本均值就是期望的估计。小样本时两者差别可能很大——你掷十次骰子均值可能是 4.2,但期望永远是 3.5。
方差
知道了"中心在哪",还得知道"数据有多散"。方差就是衡量散度的工具。
方差把每个数与均值的差平方后求平均。为什么要平方?因为正负偏差会抵消,差值 10 和 -10 一加等于零,好像没偏差。平方一下就都是正数了,跑不掉。
其中 是样本方差,分母用 (贝塞尔校正), 是样本均值。
scores = [72, 78, 85, 90, 95]
var_score = np.var(scores, ddof=1)
print(f"方差: {var_score:.2f}")

方差
这里 ddof=1 是因为样本方差除以 n-1 而非 n。这是贝塞尔校正:用样本估计总体时,n 个数据点中只有 n-1 个是自由变动的(最后一个被均值锁定了),所以除以 n 会系统低估真实方差。n 很大时二者差别不大,n 小的时候不校正就差得远。
方差直观上不好理解——"分数方差 84.5"到底是啥意思?因为单位变成了"分的平方",跟原数据不在一个量纲上。所以我们需要标准差。
标准差
标准差就是方差开根号。回到原始量纲,秒变好懂。
std_score = np.std(scores, ddof=1)
print(f"标准差: {std_score:.2f}")

标准差
标准差 9.19 的意思:每个人大概在均值 84 上下浮动 9 分左右。大约 68% 的数据落在均值 ±1 个标准差内,95% 落在 ±2 个标准差内(这是正态分布的特性)。
身高、考试、温度——只要数据大致正态,标准差就是你最实用的"散度标尺"。报一个均值加一个标准差(比如 170 cm ± 6 cm),比列几百个数据有用得多。
标准误
标准差和标准误,名字差一个字,说的完全不是一回事。
标准差描述个体差异。一个班 30 人身高标准差 6 cm,说的是同学之间身高差多少。
标准误描述均值的精度。你从全校随机抽 30 人算平均身高,得 170 cm。再抽 30 人,可能得 171 cm。反复抽样,这些均值的波动程度就是标准误:
其中 是样本标准差, 是样本量。
sample_size = 30
sd = 6.0
se = sd / np.sqrt(sample_size)
print(f"标准差 SD = {sd}")
print(f"标准误 SE = {se:.2f}")

标准误
关键区别:标准差不会因为多量几个人而变小——人的身高差异是客观存在的。标准误会!量 100 人时 SE = 0.6,量 1000 人时 SE = 0.19。样本越大,均值估计越精确,标准误越小。
论文里报"被试年龄 25.3 ± 4.2 岁"用的是标准差,描述样本本身。"均值差 = 3.1, SE = 0.8"用的是标准误,描述估计的可靠性。混用是最常见的统计报告错误。
自由度
前面算样本方差用了 n-1,这个减掉的 1 就是"损失的自由度"。
什么叫自由度?自由变动的数据点个数。3 个数的均值是 10,其中两个是 8 和 12,第三个必须是 10——被均值锁定了,自由度只有 2。
其中 是数据点总数, 是需要估计的参数个数(单样本均值消耗 1 个,所以 ;独立双样本各消耗 1 个,所以 )。
独立样本 t 检验里,两组各 n 人,自由度是 2n-2。因为每组的均值消耗了 1 个自由度。自由度决定 t 分布的形状:样本越小,t 分布越胖(尾部越厚),因为信息不足,判断要更保守。
n1, n2 = 30, 30
df_pooled = n1 + n2 - 2
print(f"合并自由度: {df_pooled}")

自由度
t 值
现在进入推断统计。t 值回答的问题是:两组均值差异,是真差异还是抽样噪声?
其中 是两组样本均值, 是均值差的标准误, 是合并标准差:
分母用的是标准误而非标准差。因为你检验的是"均值差是否为零",均值差的波动由标准误度量。
control = np.random.normal(loc=70, scale=12, size=30)
experiment = np.random.normal(loc=78, scale=12, size=30)
mean_diff = np.mean(experiment) - np.mean(control)
sd_c, sd_e = np.std(control, ddof=1), np.std(experiment, ddof=1)
pooled_sd = np.sqrt(((30-1)*sd_c**2 + (30-1)*sd_e**2) / (30+30-2))
se_diff = pooled_sd * np.sqrt(1/30 + 1/30)
t_value = mean_diff / se_diff
print(f"均值差: {mean_diff:.2f}")
print(f"均值差标准误: {se_diff:.2f}")
print(f"t 值: {t_value:.3f}")

t值
t 值本质上是个信噪比。分子是信号(差异),分母是噪声(抽样的自然波动)。t 大说明信号强于噪声,差异可能真实;t 小说明信号淹没在噪声里,差异可能只是巧合。
t 值与 Cohen's d 有精确的数学关系:
t 值同时编码了效应大小和样本量,而 d 通过乘以样本量因子把 n 的影响剥离出去。
p 值
t 值还不够,你需要一步概率翻译:在零假设(两组没有真实差异)成立的前提下,观测到当前 t 值或更极端情况的概率,就是 p 值。
其中 是服从 分布的随机变量, 是实际观测到的 t 值, 是零假设。
p 值小意味着"如果没差异,出现这么大的 t 值太离谱了",因此有理由怀疑零假设。p 值大意味着"没差异也能轻松产生这样的 t 值",没有理由拒绝零假设。
from scipy import stats
t_stat, p_value = stats.ttest_ind(experiment, control)
print(f"t = {t_stat:.3f}, p = {p_value:.4f}")

p值
但 p 值有个坑:它对样本量极度敏感。n 很大时,微不足道的差异也能给出极小的 p 值。1 万人测出两组身高差 0.1 cm,p < 0.001,统计上"显著",实际上毫无意义。p 值只告诉你"差异是否可检测",不告诉你"差异有多大"。这就是为什么必须报告效应量。
Cohen's d 效应量
Cohen's d 用标准差作分母,度量两组分布在原始尺度上的分离程度:
其中 是合并标准差(与 t 值公式中同一个), 是均值差。
注意分母是标准差,不是标准误!效应量是总体的属性,不应该因为多量了几个人就变大。标准差估计的是总体真值,和 n 无关。
cohens_d = mean_diff / pooled_sd
print(f"Cohen's d = {cohens_d:.3f}")

Cohen's d
Cohen 本人给了粗略参考:0.2 是小效应,0.5 是中等,0.8 是大效应。但这些只是经验约定,具体领域差异很大。心理学里 d = 0.3 可能很重要,流行病学里 d = 0.1 就值得干预。
一个完整的统计报告应该是:t = 3.57, p = 0.001, d = 0.65。三个数各司其职——t 说什么尺度上的信噪比,p 说零假设下这有多不可能,d 说效应本身有多大。
回头看整条逻辑线:
算术平均、几何平均、中位数、众数是从不同角度看"数据中心在哪";
方差和标准差衡量"数据有多散";标准误把散度从个体层面转换到均值层面,是迈向推断的桥梁;
t 值用标准误当尺子衡量差异的显著性,p 值把 t 值翻译成概率,Cohen's d 用标准差当尺子衡量差异的实际大小。
标准差属于数据本身,标准误属于估计过程——这个区分搞清楚了,t 值和 Cohen's d 的分母为什么不同就不言自明。
预览时标签不可点
Close
更多
Name cleared
微信扫一扫赞赏作者
Like the AuthorOther Amount
赞赏后展示我的头像
作品
暂无作品
Like the Author
Other Amount
¥
最低赞赏 ¥0
OK
Back
Other Amount
更多
赞赏金额
¥
最低赞赏 ¥0
1
2
3
4
5
6
7
8
9
0
.
基础知识 · 目录
基础知识
上一篇电车难题?厂商的智驾会在事故前突然退出么?下一篇你的电脑每秒能做多少次浮点运算?—— FLOPS 的故事
Close
更多
搜索「」网络结果
Close
调整当前正文文字大小
更多
100%