统计学14:方差分析
1、什么是方差分析?
ANOVA,变异数分析。用于两个及以上样本均数差别的显著性检验。
ab测试可以用来比较两组的差异,但是需要对比多个组时,ab测试就很低效,需要用到方差分析
由于各种因素的影响,实验所得数据呈现波动,造成波动的原因分两类:一种是随机因素导致的波动,另一种就是实验中施加的干预对结果形成影响的可控因素;核心目标是通过分析组间和组内的方差,判断是否存在显著差异。
2、方差分析需要满足条件
2.1 独立性
方差分析要求各观测值之间是相互独立的
2.2 正态性
方差分析要求每个总体中的观测值都满足正态分布。当我们把因素的某一个水平对应的所有观测值单独拿出来看时,这些数据的分布应该是正态的
2.3 方差齐性
方差齐性是指因素的各个水平对应的总体方差是相等的。即不同组(不同水平)之间数据的离散程度应该是相同的。
当只有一个因素并比较不同组的均值差异时,用单因素方差分析;
当有两个因素,想分析两个因素之间的交互作用,使用双因素方差分析;
3、单因素方差分析
比如运营把商城页面主题色换成了 4 种颜色:红、黄、蓝、绿,每种颜色随机曝光 1000 人,想看 7 天后的人均消费是否有差异
【确定假设】
H0:消费无差异,均值相同
H1:至少有一个组均值不同
【组间差异与组内差异】
总平均值 = 120.5
组间差异:不同组之间的均值差异。

组内差异:同一组内的数据点与该组均值之间的差异。

组间差异越大,组内差异越小,越能得到我们想看到的结论
【计算F值】
F值是方差分析的核心统计量,F值越大,说明组间差异越大,组内差异越小,也就越有可能拒绝原假设。公式如下:
F =(组间差异 ÷ 组间自由度)/(组内差异 ÷ 组内自由度)
import numpy as np
from scipy.stats import f
# 汇总数据
groups = {
'红': {'n': 1000, 'mean': 120, 'var': 900},
'黄': {'n': 1000, 'mean': 118, 'var': 850},
'蓝': {'n': 1000, 'mean': 125, 'var': 920},
'绿': {'n': 1000, 'mean': 119, 'var': 880}
}
# 总平均值
grand_mean = 120.5
# 组间差异 SSB
SSB = sum(g['n'] * ((g['mean'] - grand_mean)**2) for g in groups.values())
# 组内差异 SSW
SSW = sum((g['n'] - 1) * g['var'] for g in groups.values())
# 自由度
df_between = len(groups) - 1 # 3
df_within = sum(g['n'] for g in groups.values()) - len(groups) # 3996
# 均方
MSB = SSB / df_between
MSW = SSW / df_within
# F 值
F = MSB / MSW
# 计算 p 值
p_value = 1 - f.cdf(F, df_between, df_within)
print(f"SSB={SSB:.2f}, SSW={SSW:.2f}, F={F:.2f}, p={p_value:.3f}")
【解读P值】
如果P值小于显著性水平(通常是0.05),则拒绝原假设。这里的P值<0.05,因此可以拒绝原假设,即至少有一种颜色人均消费显著不同于其它颜色
到这里,只是分析出了有差别,还不知道哪组有差别,需要使用事后检验(Tukey HSD)。
from statsmodels.stats.multicomp import pairwise_tukeyhsd
tukey = pairwise_tukeyhsd(endog=df['gmv'], groups=df['color'], alpha=0.05)
print(tukey)
- 蓝色vs黄色,P=0.004,显著差异,差¥7
- 红色vs绿色,P=0.62,不显著,差¥1
- 可以向运营建议先全量推蓝色,砍掉绿色
4、多因素方差分析
比如运营把页面主题色换成了红、黄、蓝,还设置了面额为5 元、10 元的优惠券。这样因素就多了个价格类别,需要用到多因素方差分析,解决:
- 颜色对人均消费有没有影响?
- 价格对人均消费有没有影响?
- 颜色+价格组合对人均消费有没有影响?


import numpy as np
import pandas as pd
from scipy.stats import f # 仅用来查 F 分布的 p 值
# 模拟参数
np.random.seed(42)
n_per_cell = 200 # 每个组合 200 人
color_levels = ['红', '黄', '蓝']
coupon_levels = ['5元', '10元']
# 真效应(可改动看结果)
mu = 120
alpha = {'红': -2, '黄': 0, '蓝': 3} # 颜色主效应
beta = {'5元': 0, '10元': 5} # 券主效应
interaction = {('红','5元'): 0, ('红','10元'): -3,
('黄','5元'): 0, ('黄','10元'): 0,
('蓝','5元'): 0, ('蓝','10元'): 4} # 交互效应
sigma = 5 # 随机误差标准差
records = []
for c in color_levels:
for p in coupon_levels:
mean = mu + alpha[c] + beta[p] + interaction[(c, p)]
gmv = np.random.normal(mean, sigma, n_per_cell)
for val in gmv:
records.append({'color': c, 'coupon': p, 'gmv': val})
df = pd.DataFrame(records)
# 全局均值
grand_mean = df.gmv.mean()
# 各组均值、样本量
group_stats = df.groupby(['color', 'coupon']).agg(
mean = ('gmv', 'mean'),
n = ('gmv', 'count')
)
# 颜色、券各自的均值、样本量
color_stats = df.groupby('color')['gmv'].agg(['mean', 'count'])
coupon_stats = df.groupby('coupon')['gmv'].agg(['mean', 'count'])
N = len(df) # 总样本量
a = len(color_levels) # 颜色水平数
b = len(coupon_levels) # 券水平数
# 1) 总平方和 SST
SST = ((df.gmv - grand_mean) ** 2).sum()
# 2) 颜色主效应 SSA
SSA = 0
for c in color_levels:
ni = color_stats.loc[c, 'count']
mean_i = color_stats.loc[c, 'mean']
SSA += ni * (mean_i - grand_mean) ** 2
# 3) 券主效应 SSB
SSB = 0
for p in coupon_levels:
nj = coupon_stats.loc[p, 'count']
mean_j = coupon_stats.loc[p, 'mean']
SSB += nj * (mean_j - grand_mean) ** 2
# 4) 交互平方和 SSAB
SSAB = 0
for (c, p), row in group_stats.iterrows():
n_ij = row['n']
mean_ij= row['mean']
# 交互偏差 = 组均值 - 颜色主效应 - 券主效应 + 总均值
inter = mean_ij - color_stats.loc[c, 'mean'] - coupon_stats.loc[p, 'mean'] + grand_mean
SSAB += n_ij * (inter ** 2)
# 5) 误差平方和 SSE
SSE = SST - SSA - SSB - SSAB
df_A = a - 1
df_B = b - 1
df_AB = (a - 1) * (b - 1)
df_E = N - a * b
MSA = SSA / df_A
MSB = SSB / df_B
MSAB= SSAB / df_AB
MSE = SSE / df_E
F_A = MSA / MSE
F_B = MSB / MSE
F_AB = MSAB / MSE
p_A = 1 - f.cdf(F_A, df_A, df_E)
p_B = 1 - f.cdf(F_B, df_B, df_E)
p_AB = 1 - f.cdf(F_AB, df_AB, df_E)
print(f"颜色主效应: F={F_A:.3f}, p={p_A:.4f}")
print(f"券主效应 : F={F_B:.3f}, p={p_B:.4f}")
print(f"交互效应 : F={F_AB:.3f}, p={p_AB:.4f}")
颜色主效应: F=44.679, p=0.0000
券主效应 : F=309.586, p=0.0000
交互效应 : F=39.857, p=0.0000
得出结论:颜色、券额的主效应都显著,且颜色和券额的交互作用也显著,接下来再看差别具体在哪里体现
from statsmodels.stats.multicomp import pairwise_tukeyhsd
#简单效应:固定颜色,比较券
for c in ['红', '黄', '蓝']:
sub = df[df.color == c]
tukey = pairwise_tukeyhsd(sub.gmv, sub.coupon, alpha=0.05)
print(f'\n【{c}色】券面额差异 (Tukey HSD)')print(tukey)
#简单效应:固定券,比较颜色
for p in ['5元', '10元']:
sub = df[df.coupon == p]
tukey = pairwise_tukeyhsd(sub.gmv, sub.color, alpha=0.05)
print(f'\n【{p}券】颜色差异 (Tukey HSD)')
print(tukey)
【红色】券面额差异 (Tukey HSD)
Multiple Comparison of Means - Tukey HSD, FWER=0.05
===================================================
group1 group2 meandiff p-adj lower upper reject
---------------------------------------------------
10元 5元 -2.6332 0.0 -3.5763 -1.6901 True
---------------------------------------------------
【黄色】券面额差异 (Tukey HSD)
Multiple Comparison of Means - Tukey HSD, FWER=0.05
===================================================
group1 group2 meandiff p-adj lower upper reject
---------------------------------------------------
10元 5元 -5.4731 0.0 -6.4629 -4.4834 True
---------------------------------------------------
【蓝色】券面额差异 (Tukey HSD)
Multiple Comparison of Means - Tukey HSD, FWER=0.05
==================================================
group1 group2 meandiff p-adj lower upper reject
--------------------------------------------------
10元 5元 -9.0269 0.0 -10.0038 -8.05 True
--------------------------------------------------
【5元券】颜色差异 (Tukey HSD)
Multiple Comparison of Means - Tukey HSD, FWER=0.05
====================================================
group1 group2 meandiff p-adj lower upper reject
----------------------------------------------------
红 蓝 5.8451 -0.0 4.7165 6.9737 True
红 黄 1.7756 0.0007 0.647 2.9042 True
蓝 黄 -4.0695 -0.0 -5.1981 -2.9409 True
----------------------------------------------------
【10元券】颜色差异 (Tukey HSD)
Multiple Comparison of Means - Tukey HSD, FWER=0.05
===================================================
group1 group2 meandiff p-adj lower upper reject
---------------------------------------------------
红 蓝 12.2388 -0.0 11.0494 13.4283 True
红 黄 4.6155 -0.0 3.4261 5.8049 True
蓝 黄 -7.6233 -0.0 -8.8128 -6.4339 True
---------------------------------------------------
结论:10元券比5元券显著提升人均消费;且蓝色+10元券组合效果最好,建议可作为全量方案投放使用;
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)