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 元的优惠券。这样因素就多了个价格类别,需要用到多因素方差分析,解决:

  1. 颜色对人均消费有没有影响?
  2. 价格对人均消费有没有影响?
  3. 颜色+价格组合对人均消费有没有影响?

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元券组合效果最好,建议可作为全量方案投放使用;

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐