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

Sklearn与Statsmodels多元线性回归结果差异疑问(含分类变量)

为什么sklearn LinearRegression和statsmodels OLS的多元线性回归结果(截距+分类变量系数)不一致?

问题背景

你用OneHotEncoder将2个分类变量(floorLevel、buildingType)转换成了全部虚拟变量,结合3个连续变量构建多元线性回归模型,但分别用sklearn和statsmodels实现后,发现仅截距和分类变量的系数存在差异,连续变量的系数完全一致。

你的自变量与因变量

y = df['price']
x = df[['age', 'totalRooms', 'elevator',
        'floorLevel_bottom', 'floorLevel_high', 
        'floorLevel_low', 'floorLevel_medium','floorLevel_top',
        'buildingType_bungalow', 'buildingType_plate', 
        'buildingType_plate_tower', 'buildingType_tower']]

sklearn实现与输出

from sklearn.linear_model import LinearRegression

mlr = LinearRegression()
mlr.fit(x, y)

print('Intercept: \n', mlr.intercept_)
print("Coefficients:")
list(zip(x, mlr.coef_))

输出:

Intercept:
35228.96453917408
Coefficients:
[('age', 1046.5347118942063),
('totalRooms', -797.7667275033103),
('elevator', 11940.629576736419),
('floorLevel_bottom', 1011.5929167549165),
('floorLevel_high', 157.60625500592502),
('floorLevel_low', 483.89164772666277),
('floorLevel_medium', 630.9547280568961),
('floorLevel_top', -2284.0455475443687),
('buildingType_bungalow', 31610.88176756009),
('buildingType_plate', -9649.087529585862),
('buildingType_plate_tower', -8813.187607409624),
('buildingType_tower', -13148.606630564624)]

statsmodels实现与输出

import statsmodels.api as sm

x_in = sm.add_constant(x)
model = sm.OLS(y, x_in).fit()
print(model.summary())

输出(关键参数):

Intercept 2.43e+04

age 1046.5347

totalRooms -797.7667

elevator 1.194e+04

floorLevel_bottom 5870.7604

floorLevel_high 5016.7738

floorLevel_low 5343.0592

floorLevel_medium 5490.1223

floorLevel_top 2575.1220

buildingType_bungalow 3.768e+04

buildingType_plate -3575.1281

buildingType_plate_tower -2739.2282

buildingType_tower -7074.6472


核心原因:虚拟变量陷阱的不同处理逻辑

你将每个分类变量的全部虚拟变量都纳入了模型,这会引发完全多重共线性(虚拟变量陷阱):比如floorLevel的5个虚拟变量中,任意4个都能推导第5个的值(因为每个样本必然属于其中一个类别,5个变量的和恒为1)。此时模型参数是不可唯一识别的,sklearn和statsmodels选择了不同的求解策略,导致参数表现不同,但预测结果完全一致。

1. sklearn LinearRegression的处理

sklearn会自动通过约束条件消除共线性:它会默认将某一个虚拟变量设为参考类别(系数隐含为0,实际表现为截距包含该类别的基准值),其他虚拟变量的系数是相对于该参考类别的差值。

  • 从你的输出看,它把floorLevel_top和buildingType_tower作为参考类别:
    • floorLevel_bottom的系数1011.59 → 底层比顶层的价格高1011.59
    • buildingType_plate的系数-9649.09 → 板楼比塔楼的价格低9649.09
    • 截距35228.96 → 代表“顶层+塔楼”组合的基准价格

2. statsmodels OLS的处理

statsmodels不会自动移除变量,而是使用广义逆矩阵求解最小范数解。此时的参数解释完全不同:

  • 截距2.43e+04 → 代表全局平均价格的基准值
  • 每个虚拟变量的系数 → 代表该类别价格相对于全局平均的差值
    • floorLevel_bottom的系数5870.76 → 底层价格比全局平均高5870.76

验证:两个模型的预测结果完全一致

尽管参数数值不同,但你可以用同一组输入数据测试两个模型的预测值,结果会完全相同——因为它们本质上拟合的是同一个线性模型,只是参数的解释方式不同。


解决方法:统一参考类别,避免虚拟变量陷阱

要让两个库的参数结果完全对应,你需要在生成虚拟变量时,为每个分类变量去掉一个类别,作为参考基准:

from sklearn.preprocessing import OneHotEncoder
from sklearn.compose import ColumnTransformer

# 定义分类变量列
cat_cols = ['floorLevel', 'buildingType']
# 初始化OneHotEncoder,drop='first'表示去掉每个分类的第一个类别作为参考
encoder = OneHotEncoder(drop='first', sparse_output=False)
# 构建转换器,只对分类变量编码
ct = ColumnTransformer(
    transformers=[('onehot', encoder, cat_cols)],
    remainder='passthrough'  # 保留连续变量
)

# 转换得到处理后的特征矩阵
x_processed = ct.fit_transform(df[['age', 'totalRooms', 'elevator'] + cat_cols])

此时再分别用sklearn和statsmodels建模,截距和系数会完全一致,都是以被去掉的类别为参考基准。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 11:25:08