反向 Box-Cox 转换

34

我正在使用SciPy的boxcox函数对一个连续变量执行Box-Cox转换

from scipy.stats import boxcox
import numpy as np
y = np.random.random(100)
y_box, lambda_ = ss.boxcox(y + 1) # Add 1 to be able to transform 0 values

然后,我拟合了一个统计模型来预测这个Box-Cox转换后的变量的值。模型预测结果是在Box-Cox比例尺上的,我想把它们转换为原始变量的比例尺。

from sklearn.ensemble import RandomForestRegressor
rf = RandomForestRegressor()
X = np.random.random((100, 100))
rf.fit(X, y_box)
pred_box = rf.predict(X)

然而,我找不到一个SciPy函数,它可以根据已转换的数据和lambda执行反向Box-Cox变换。是否存在这样的函数?目前我编写了一个反向转换。

pred_y = np.power((y_box * lambda_) + 1, 1 / lambda_) - 1

2
这似乎是一个非常好的问题。 - Russia Must Remove Putin
2
Box-Cox函数的反函数在scipy开发版本中(即https://github.com/scipy/scipy的主分支)已经存在,并将在0.16版本中发布。请参见https://github.com/scipy/scipy/pull/4211。 - Warren Weckesser
5个回答

22

SciPy新增了一种反Box-Cox转换方法。

https://docs.scipy.org/doc/scipy/reference/generated/scipy.special.inv_boxcox.html

scipy.special.inv_boxcox scipy.special.inv_boxcox(y, lmbda) =

计算Box-Cox变换的逆运算。

找到x使得:

y = (x**lmbda - 1) / lmbda  if lmbda != 0
    log(x)                  if lmbda == 0

参数: y : array_like

需要变换的数据。

lmbda : array_like

Box-Cox变换的幂参数。

返回值:
x : array

变换后的数据。

注意事项

0.16.0版本中新增。

示例:

from scipy.special import boxcox, inv_boxcox
y = boxcox([1, 4, 10], 2.5)
inv_boxcox(y, 2.5)

output: array([1., 4., 10.])

请注意,如果您想使用预测值进行计算,或者如果您关心原始数据上的最小RMSE,则需要使用平均值,并且必须对转换中的偏差进行校正。https://otexts.org/fpp2/transformations.html - Gere
@jeffhale 为什么将数值 2.5 传递给 boxcox 和 inv_boxcox 方法? - undefined

14
  1. 这里是代码,已经运行并通过测试。Scipy使用自然对数,我查看了BoxCox变换的论文,发现他们使用的是以10为底的对数。我仍然坚持使用自然对数,因为它可以与Scipy一起使用。
  2. 按照以下代码操作:

#Function
def invboxcox(y,ld):
   if ld == 0:
      return(np.exp(y))
   else:
      return(np.exp(np.log(ld*y+1)/ld))

# Test the code
x=[100]
ld = 0
y = stats.boxcox(x,ld)
print invboxcox(y[0],ld)

6

感谢 @Warren Weckesser 的帮助,我了解到当前 SciPy 的实现中没有反转 Box-Cox 转换的函数。但是,未来的 SciPy 版本可能会有这个功能。目前,我在我的问题中提供的代码可能对其他人反转 Box-Cox 转换有所帮助。


1
参考文献,此处已有:https://docs.scipy.org/doc/scipy-0.19.0/reference/generated/scipy.special.inv_boxcox.html#scipy.special.inv_boxcox - Diego Jancic
@Gyan Veda 如果我们想要像在scipy.stats中使用它一样使用scipy.special中的这个函数,Lambda应该设置为多少?我是这样做的: y_train, self.y_train_lambda_ = boxcox(y_train),lambda默认为None,在这种情况下应该如何恢复? - Perl Del Rey
1
@Perl。如果您在Scipy中进行了Boxcox转换,则返回的第二个输出参数将是lambda。请参阅文档https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.boxcox.html。然后,您可以在撤销转换时使用该lambda。 - Jakob

3
我建议看一下Yeo-Johnson转换,它是Box-Cox的类似物,但可以处理负值,并且在scikit-learn库中得到了很好的实现,具有易于反向转换的特点。
我正在与fbprophet库(预测)一起使用它:
from sklearn.preprocessing import PowerTransformer

from fbprophet import Prophet
from fbprophet.plot import plot_cross_validation_metric
from fbprophet.diagnostics import cross_validation
from fbprophet.diagnostics import performance_metrics
import numpy as np
import pandas as pd

def inverse_transform(df, pt_instance, features):
    for feature in features:
        df[feature] = pt_instance.inverse_transform(np.array(df[feature]).reshape(-1,1))
    return df

pt = PowerTransformer(method='yeo-johnson')

train_df_transformed = train_df.copy()
train_df_transformed['y'] = pt.fit_transform(np.array(train_df['y']).reshape(-1,1))

model = Prophet(**hyperparams)
model.fit(train_df_transformed)
df_cv = cross_validation(model, initial='14 days', period='3 days', horizon='1 day', parallel="processes")
df_cv = inverse_transform(df_cv, pt, ['yhat','yhat_lower','yhat_upper'])
df_cv = pd.merge(df_cv.drop(columns=['y']),train_df, left_on='ds', right_on='ds')
df_p = performance_metrics(df_cv, metrics=['mae','mape'], rolling_window=1)
fig1 = plot_cross_validation_metric(df_cv, metric='mape')
fig2 = plot_cross_validation_metric(df_cv, metric='mae')


2
为了使用scipy.special.inv_boxcox反向转换scipy.stats.boxcox的结果,您需要先确定生成的参数lambda。
首先应用转换并打印lambda值。
df[feature_boxcox], param = stats.boxcox(df[feature])
print('Optimal lambda', param)

然后为了反转变换,您输入生成的lambda。
inv_boxcox(df[feature_boxcox], param)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接