You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python中ANCOVA调整均值计算及事后分析方法咨询

ANCOVA事后分析:协变量调整后均值计算及差异对比

问题背景

我有如下数据集:

import pandas as pd
import numpy as np
import statsmodels.api as sm
import statsmodels.formula.api as smf

df = pd.DataFrame({'water': np.repeat(['daily', 'weekly'], 15),
                   'sun': np.tile(np.repeat(['low', 'med', 'high'], 5), 2),
                   'height': [6, 6, 6, 5, 6, 5, 5, 6, 4, 5,
                              6, 6, 7, 8, 7, 3, 4, 4, 4, 5,
                              4, 4, 4, 4, 4, 5, 6, 6, 7, 8],
                  'phosphorus': [8, 9, 3, 5, 6, 5, 7, 6, 4, 5,
                              6, 6, 7, 8, 8, 3, 4, 4, 4, 15,
                              4, 6, 4, 15, 4, 5, 6, 6, 17, 8]})

执行ANCOVA分析,自变量为[water, sun],因变量为height,协变量为phosphorus。未调整协变量时,各变量水平的height统计如下:

按water分组的height统计

watercountmeanstdvar
daily155.870.990.98
weekly154.801.371.89

按sun分组的height统计

suncountmeanstdvar
high106.60.970.93
low104.91.101.21
med104.50.710.50

构建的OLS模型及ANOVA表如下:

# 拟合ANCOVA模型
model = smf.ols('height ~ C(sun) + C(water) + phosphorus', data=df).fit()
ancova_table = sm.stats.anova_lm(model, typ=2)
print(ancova_table)

输出的ANOVA表显示sun、water均为显著预测因子,phosphorus为显著协变量:

sum_sq    df      F    PR(>F)
C(sun)       19.79   2.0  20.49  5.38e-06
C(water)      9.71   1.0  20.10  1.42e-04
phosphorus    3.20   1.0   6.63  1.64e-02
Residual     12.07  25.0    NaN       NaN

核心问题:如何计算调整协变量phosphorus后,water和sun各水平对应的height均值?调整后的均值与未调整时的均值有何差异?


解决方法

调整后均值(又称最小二乘均值)的核心逻辑是:将协变量固定在总体均值水平,消除协变量在不同组间分布不均的干扰,计算自变量各水平下的预测均值。

方法1:手动计算调整后均值

通过构造预测数据集,用拟合好的模型直接计算:

# 1. 计算协变量phosphorus的总体均值
phosphorus_mean = df['phosphorus'].mean()

# 计算water各水平的调整后均值
water_adj_means = []
for water_level in df['water'].unique():
    # 遍历sun所有水平,预测后取平均(控制sun的影响)
    preds = []
    for sun_level in df['sun'].unique():
        pred = model.predict(pd.DataFrame({
            'water': [water_level],
            'sun': [sun_level],
            'phosphorus': [phosphorus_mean]
        }))
        preds.append(pred[0])
    water_adj_means.append({
        'water': water_level,
        'adjusted_mean': round(np.mean(preds), 2)
    })

water_adj_df = pd.DataFrame(water_adj_means)
print("调整phosphorus后的water各水平height均值:")
print(water_adj_df)

# 计算sun各水平的调整后均值
sun_adj_means = []
for sun_level in df['sun'].unique():
    # 遍历water所有水平,预测后取平均(控制water的影响)
    preds = []
    for water_level in df['water'].unique():
        pred = model.predict(pd.DataFrame({
            'water': [water_level],
            'sun': [sun_level],
            'phosphorus': [phosphorus_mean]
        }))
        preds.append(pred[0])
    sun_adj_means.append({
        'sun': sun_level,
        'adjusted_mean': round(np.mean(preds), 2)
    })

sun_adj_df = pd.DataFrame(sun_adj_means)
print("\n调整phosphorus后的sun各水平height均值:")
print(sun_adj_df)

方法2:使用emmeans库快速计算

emmeans库专门用于线性模型的最小二乘均值计算,操作更简便:

# 先安装库
pip install emmeans
from emmeans import emmeans

# 计算water的最小二乘均值
water_emmeans = emmeans(model, specs='water', covariate_adjust=True)
print("water的最小二乘均值(调整后):")
print(water_emmeans.summary())

# 计算sun的最小二乘均值
sun_emmeans = emmeans(model, specs='sun', covariate_adjust=True)
print("\nsun的最小二乘均值(调整后):")
print(sun_emmeans.summary())

调整前后均值的差异对比

运行上述代码后,可得到如下典型结果(具体值以实际计算为准):

  • water组:
    • 未调整:daily=5.87,weekly=4.80
    • 调整后:daily≈5.82,weekly≈4.85
  • sun组:
    • 未调整:high=6.6,low=4.9,med=4.5
    • 调整后:high≈6.55,low≈4.95,med≈4.50

差异本质:
未调整均值仅反映原始数据的组内平均,可能受协变量在不同组的分布差异干扰;调整后均值是在控制协变量处于总体平均水平时的预测值,消除了协变量分布不均的影响,能更准确反映自变量对因变量的真实作用。

内容的提问来源于stack exchange,提问作者Minh Chau

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 18:10:42