OLS 适合 python,具有系数误差和转换目标

OLS fit for python with coefficient error and transformed target

在 python 中似乎有两种 OLS 拟合方法。 Sklearn 一个和 Statsmodel 一个。我更喜欢 statsmodel 模型,因为它通过 summary() 函数给出了系数误差。但是,我想使用 sklearn 中的 TransformedTargetRegressor 来记录我的目标。看来我需要在获取 statsmodel 中的拟合系数误差和能够在 statsmodel 中转换目标之间做出选择。有什么好的方法可以在任一系统中同时执行这两项操作吗?

在统计模型中,它会像这样完成

import statsmodels.api as sm
X = sm.add_constant(X)
ols = sm.OLS(y, X)
ols_result = ols.fit()
print(ols_result.summary())

到return与系数的拟合及其误差

对于 Sklearn,您可以使用 TransformedTargetRegressor

from sklearn.compose import TransformedTargetRegressor
from sklearn.linear_model import LinearRegression
regr = TransformedTargetRegressor(regressor=LinearRegression(),func=np.log1p, inverse_func=np.expm1)
regr.fit(X, y)
print('Coefficients: \n', regr.coef_)

但是如果不自己计算,就无法得到系数的误差。有什么两全其美的好方法吗?

编辑

我在这里为我关心的特殊情况找到了一个很好的例子

https://web.archive.org/web/20160322085813/http://www.ats.ucla.edu/stat/mult_pkg/faq/general/log_transformed_regression.htm

简而言之,Scikit learn 无法帮助您计算系数标准误差。但是,如果您选择使用它,您可以自己计算误差。在问题 Python scikit learn Linear Model Parameter Standard Error 中,@grisaitis 提供了一个很好的答案,解释了它背后的主要概念。

如果您只想使用与 sciait-learn 一起使用的即插即用功能,您可以使用这个:

def get_coef_std_errors(reg: 'sklearn.linear_model.LinearRegression',
                        y_true: 'np.ndarray', X: 'np.ndarray'):
    """Function that calculates the standard deviation of the coefficients of 
    a linear regression. 

    Parameters
    ----------
    reg : sklearn.linear_model.LinearRegression
        LinearRegression object which has been fitted 
    y_true : np.ndarray
        array containing the target variable
    X : np.ndarray
        array containing the features used in the regression

    Returns
    -------
    beta_std
        Standard deviation of the regression coefficients 
    """
    y_pred = reg.predict(X) # get predictions
    errors = y_true - y_pred # calculate residuals
    sigma_sq_hat = np.var(errors) # calculate residuals std error

    sigma_beta_hat = sigma_sq_hat * np.linalg.inv(X.T @ X)
    
    return np.sqrt(np.diagonal(sigma_beta_hat)) # diagonal to recover variances

只是在这里添加一个冗长的评论,我相信 TransformedTargetRegressor 并没有按照您的想法去做。据我所知,逆变换功能仅在调用 predict 方法时应用。它不以未转换结果的单位表示系数。

示例:
import pandas as pd
import statsmodels.api as sm

from sklearn.compose import TransformedTargetRegressor
from sklearn.linear_model import LinearRegression
import numpy as np
from sklearn import datasets
创建一些示例数据:
df = pd.DataFrame(datasets.load_iris().data)
df.columns = datasets.load_iris().feature_names

X = df.loc[:,['sepal length (cm)', 'sepal width (cm)']]
y = df.loc[:, 'petal width (cm)']
首先学习Sklearn:
regr = TransformedTargetRegressor(regressor=LinearRegression(),func=np.log1p, inverse_func=np.expm1)
regr.fit(X, y)

print(regr.regressor_.intercept_)
for coef in regr.regressor_.coef_:
    print(coef)
#-0.45867804195769357
# 0.3567583897503805
# -0.2962942997303887
转化结果的统计模型:
X = sm.add_constant(X)
ols_trans = sm.OLS(np.log1p(y), X).fit()
print(ols_trans.params)

#const               -0.458678
#sepal length (cm)    0.356758
#sepal width (cm)    -0.296294
#dtype: float64

您会看到,在这两种情况下,系数都是 identical.That,使用带有 TransformedTargetRegressor 的回归会产生与带有转换结果的 statsmodels.OLS 相同的系数。 TransformedTargetRegressor 不会将系数回译为原始未转换的 space。请注意,系数在原始 space 中将是非线性的,除非变换本身是线性的,在这种情况下这是微不足道的(与常数相加和相乘)。 This discussion 这里指向一个相似的方向 - 在 most/many 情况下反向转换 beta 是不可行的。

该怎么做?

如果解释是您的目标,我相信您最接近您希望实现的目标是使用预测值,其中您改变回归量或系数。因此,让我举个例子:如果您的目标是说明 sepal length 的一个标准误差较高对未转换结果的影响是什么,您可以创建拟合的预测值以及1-sigma 场景(通过调整系数,或通过调整 X 中的相应列)。

示例:
# Toy example to add one sigma to sepal length coefficient
coeffs = ols_trans.params.copy()
coeffs['sepal length (cm)'] +=  0.018 # this is one sigma


# function to predict and translate predictions back:
def get_predicted_backtransformed(coeffs, data, inv_func):
    return inv_func(data.dot(coeffs))

# get standard predicted values, backtransformed:
original = get_predicted_backtransformed(ols_trans.params, X, np.expm1)
# get counterfactual predicted values, backtransformed:
variant1 = get_predicted_backtransformed(coeffs, X, np.expm1)

然后你可以说例如关于未转换结果的均值偏移:

variant1.mean()-original.mean()
#0.2523083548367202