【问题标题】:Why is Sklearn R-squared different from that of statsmodels when fit_intercept=False?为什么当 fit_intercept=False 时 Sklearn R-squared 与 statsmodels 不同?
【发布时间】:2022-01-07 18:07:54
【问题描述】:

我正在使用 Sklearn 和 statsmodels 执行线性回归。

我知道 Sklearn 和 statsmodels 产生相同的结果。如下图所示,Sklearn 和 statsmodels 得到了相同的结果,但是在 Sklearn 中使用fit_intercept=False 时,即使截距为零时系数相同,结果也不同。

你能解释一下原因吗?或者当我在 Sklearn 中使用fit_intercept=False 时给我任何方法。

import numpy as np
import statsmodels.api as sm
from sklearn.linear_model import LinearRegression

# dummy data:
y = np.array([1,3,4,5,2,3,4])
X = np.array(range(1,8)).reshape(-1,1) # reshape to column

# intercept is not zero : the result are the same
# scikit-learn:
lr = LinearRegression()
lr.fit(X,y)
print(lr.score(X,y))
# 0.16118421052631582

# statsmodels
X_ = sm.add_constant(X)
model = sm.OLS(y,X_)
results = model.fit()
print(results.rsquared)
# 0.16118421052631582

# intercept is zero : the result are different
# scikit-learn:
lr = LinearRegression(fit_intercept=False)
lr.fit(X,y)
print(lr.score(X,y))
# -0.4309210526315792

# statsmodels
model = sm.OLS(y,X)
results = model.fit()
print(results.rsquared)
# 0.8058035714285714

【问题讨论】:

    标签: python scikit-learn statsmodels


    【解决方案1】:

    不一致是因为statsmodels 根据模型是否包含截距使用不同的公式计算 R 平方。如果包括截距,statsmodels 将残差平方和除以居中的总平方和,而如果不包括截距,statsmodels 将残差平方和除以未居中的总平方和。这意味着statsmodels使用以下公式计算R平方,可以在documentation中找到:

    import numpy as np
    
    def rsquared(y_true, y_pred, fit_intercept=True):
        '''
        Statsmodels R-squared, see https://www.statsmodels.org/dev/generated/statsmodels.regression.linear_model.RegressionResults.rsquared.html.
        '''
        if fit_intercept:
            return 1 - np.sum((y_true - y_pred) ** 2) / np.sum((y_true - np.mean(y_true)) ** 2)
        else:
            return 1 - np.sum((y_true - y_pred) ** 2) / np.sum(y_true ** 2)
    

    另一方面,sklearn 总是在分母处使用居中的总平方和,而不管截距是否实际包含在模型中(即不管是 fit_intercept=True 还是 fit_intercept=False)。另见this answer

    import numpy as np
    import statsmodels.api as sm
    from sklearn.linear_model import LinearRegression
    
    def rsquared(y_true, y_pred, fit_intercept=True):
        '''
        Statsmodels R-squared, see https://www.statsmodels.org/dev/generated/statsmodels.regression.linear_model.RegressionResults.rsquared.html.
        '''
        if fit_intercept:
            return 1 - np.sum((y_true - y_pred) ** 2) / np.sum((y_true - np.mean(y_true)) ** 2)
        else:
            return 1 - np.sum((y_true - y_pred) ** 2) / np.sum(y_true ** 2)
    
    # dummy data:
    y = np.array([1, 3, 4, 5, 2, 3, 4])
    X = np.array(range(1, 8)).reshape(-1, 1) # reshape to column
    
    # intercept is not zero: the result are the same
    # scikit-learn:
    lr = LinearRegression(fit_intercept=True)
    lr.fit(X, y)
    print(lr.score(X, y))
    # 0.16118421052631582
    print(rsquared(y, lr.predict(X), fit_intercept=True))
    # 0.16118421052631582
    
    # statsmodels
    X_ = sm.add_constant(X)
    model = sm.OLS(y, X_)
    results = model.fit()
    print(results.rsquared)
    # 0.16118421052631582
    print(rsquared(y, results.fittedvalues, fit_intercept=True))
    # 0.16118421052631593
    
    # intercept is zero: the result are different
    # scikit-learn:
    lr = LinearRegression(fit_intercept=False)
    lr.fit(X, y)
    print(lr.score(X, y))
    # -0.4309210526315792
    print(rsquared(y, lr.predict(X), fit_intercept=True))
    # -0.4309210526315792
    print(rsquared(y, lr.predict(X), fit_intercept=False))
    # 0.8058035714285714
    
    # statsmodels
    model = sm.OLS(y, X)
    results = model.fit()
    print(results.rsquared)
    # 0.8058035714285714
    print(rsquared(y, results.fittedvalues, fit_intercept=False))
    # 0.8058035714285714
    

    【讨论】:

    • 感谢您链接到我的答案;但我认为你在解释这个特殊情况(即两个模型都安装不带拦截)方面做得比我好得多,所以我也更新了我的答案以链接这里。干杯;)
    • 谢谢 :) 我后来才意识到(在我发布我的答案之后)问题中包含的代码实际上是直接从你的答案中获取的,所以它可能应该首先链接到那里。
    • 糟糕...我没注意到那个! :) 你是对的,OP 应该自己链接到那里,如果不是为了其他原因,只是为了解释为什么这个特定案例仍然没有得到很好的解释。
    • 感谢您的详细解释:)您的好意是一个很大的帮助。
    • 欢迎@yoon 口头感谢,但如果答案解决了您的问题,请接受 - 请参阅What should I do when someone answers my question?
    猜你喜欢
    • 1970-01-01
    • 2020-11-24
    • 2015-06-22
    • 2018-07-27
    • 2021-05-30
    • 2016-10-18
    • 1970-01-01
    • 1970-01-01
    • 2021-04-25
    相关资源
    最近更新 更多