Statsmodel从ANOVA提取系数及二次函数拟合问题求解
问题根因与解决方案
问题1:系数数量不符合预期
原因:你使用C()将3水平的自变量转为分类变量时,statsmodels默认采用哑变量编码方案,3个水平会生成k-1=2个哑变量,对应2个系数,和你的预期不符。如果希望每个变量仅输出1个线性/二次项系数,不需要转分类变量,直接将其作为连续变量传入公式即可。转字符串后二次项报错的原因是字符串类型变量无法执行幂运算,属于数据类型错误。
问题2:二次项被ANOVA忽略
原因:你的公式写法错误。statsmodels的公式解析中,算术运算(比如平方、乘法)需要用I()包裹才能被识别为数值运算,否则会被解析为公式语法里的交互项规则。同时你将分类变量做平方运算本身就无意义,也导致二次项没有被正确纳入模型。anova_lm本身支持所有OLS可拟合的模型,不存在仅支持线性模型的限制。
修正后代码示例
import statsmodels.api as sm import statsmodels.formula.api as smf import pandas as pd import matplotlib.pyplot as plt import matplotlib as mpl data = pd.read_csv('ANOVA Data.csv') # 不需要转分类,直接用原始数值变量建模 # 线性模型(连续变量) model_t_lin = smf.ols(formula="Thickness ~ Pressure + Speed + Spacing", data=data).fit() # 带交互项模型 model_t_int = smf.ols(formula="Thickness ~ Pressure + Speed + Spacing + Pressure:Speed + Pressure:Spacing + Speed:Spacing", data=data).fit() # 带二次项模型 注意二次项用I()包裹 model_t_qd = smf.ols(formula="Thickness ~ Pressure + Speed + Spacing + Pressure:Speed + Pressure:Spacing + Speed:Spacing + I(Pressure**2) + I(Speed**2) + I(Spacing**2)", data=data).fit() models = [model_t_lin, model_t_int, model_t_qd] modelnoms = ['t_linear','t_interactions','t_quadratic'] A_type = 3 for i in range(len(models)): print(' ') print(f'{modelnoms[i]}:') print(models[i].summary()) print(' ') print(sm.stats.anova_lm(models[i], typ=A_type)) # 系数归一化和绘图部分逻辑不变 a_t_lin = model_t_lin.params / model_t_lin.params[0] a_t_int = model_t_int.params / model_t_int.params[0] a_t_qd = model_t_qd.params / model_t_qd.params[0] coeff = [a_t_lin, a_t_int, a_t_qd] fullnoms = ['Thickness - linear','Thickness - interactions','Thickness - quadratic'] mpl.rc('font', family='Verdana') mpl.rcParams.update({'figure.autolayout': True}) for i in range(len(models)): plt.figure() plt.title(fullnoms[i]) coeff[i].plot.bar() plt.show()
额外建议
- 如果你的实验变量本身属于有序分类而非连续变量,想要做二次项趋势检验,可以用多项式编码
C(Pressure, Poly)替代默认哑变量编码,会输出线性、二次趋势对应的系数,符合3水平变量的方差分析需求。 - 模型筛选可以结合AIC、BIC值以及ANOVA的显著性结果综合判断,优先选择显著项多且信息准则值低的模型。
内容的提问来源于stack exchange,提问作者Lythes
相关产品推荐
相关产品推荐

