在数据建模中,经常需要将数据转换为正态分布,以满足某些算法对数据分布的假设。本文将介绍如何使用Box-Cox和Yeo-Johnson变换,通过PowerTransformer将数据从不同的分布映射到正态分布。此外,还将展示QuantileTransformer的使用,它能够将任意分布的数据转换为高斯分布,前提是有足够的训练样本。
首先,需要注意的是,尽管Box-Cox在对数正态和卡方分布的数据上表现得比Yeo-Johnson更好,但它不支持负值输入。因此,在实际应用中,需要根据数据的特性选择合适的变换方法。
在下面的代码示例中,将生成六种不同概率分布的数据:对数正态、卡方、威布尔、高斯、均匀和双峰分布。然后,将应用Box-Cox和Yeo-Johnson变换,并使用QuantileTransformer进行比较。
import matplotlib.pyplot as plt
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import PowerTransformer, QuantileTransformer
# 设置随机种子以保证结果的可重复性
rng = np.random.RandomState(304)
# 初始化变换器
bc = PowerTransformer(method="box-cox")
yj = PowerTransformer(method="yeo-johnson")
qt = QuantileTransformer(n_quantiles=500, output_distribution="normal", random_state=rng)
# 生成不同分布的数据
distributions = {
"Lognormal": rng.lognormal(size=(1000, 1)),
"Chi-squared": rng.chisquare(df=3, size=(1000, 1)),
"Weibull": rng.weibull(a=50, size=(1000, 1)),
"Gaussian": rng.normal(loc=100, size=(1000, 1)),
"Uniform": rng.uniform(low=0, high=1, size=(1000, 1)),
"Bimodal": np.concatenate([rng.normal(loc=100, size=(500, 1)), rng.normal(loc=105, size=(500, 1))], axis=0)
}
# 创建绘图
fig, axes = plt.subplots(nrows=8, ncols=3, figsize=plt.figaspect(2))
axes = axes.flatten()
# 遍历每种分布,应用变换并绘制结果
for i, (name, X) in enumerate(distributions.items()):
X_train, X_test = train_test_split(X, test_size=0.5)
X_trans_bc = bc.fit(X_train).transform(X_test)
X_trans_yj = yj.fit(X_train).transform(X_test)
X_trans_qt = qt.fit(X_train).transform(X_test)
# 绘制原始数据和变换后的数据
axes[i].hist(X_train, bins=30, color="#D81B60")
axes[i].set_title(name, fontsize=6)
axes[i].tick_params(axis="both", which="major", labelsize=6)
# 绘制Box-Cox变换后的数据
axes[i+6].hist(X_trans_bc, bins=30, color="#0188FF")
axes[i+6].set_title(f"After Box-Cox\n$\\lambda$ = {round(bc.lambdas_[0], 2)}", fontsize=6)
axes[i+6].tick_params(axis="both", which="major", labelsize=6)
# 绘制Yeo-Johnson变换后的数据
axes[i+12].hist(X_trans_yj, bins=30, color="#FFC107")
axes[i+12].set_title(f"After Yeo-Johnson\n$\\lambda$ = {round(yj.lambdas_[0], 2)}", fontsize=6)
axes[i+12].tick_params(axis="both", which="major", labelsize=6)
# 绘制Quantile变换后的数据
axes[i+18].hist(X_trans_qt, bins=30, color="#B7A2FF")
axes[i+18].set_title("After Quantile transform", fontsize=6)
axes[i+18].tick_params(axis="both", which="major", labelsize=6)
plt.tight_layout()
plt.show()
在上述代码中,首先导入了必要的库,并设置了随机种子以保证结果的可重复性。然后,初始化了Box-Cox和Yeo-Johnson变换器,以及QuantileTransformer。接下来,生成了六种不同分布的数据,并应用了这些变换。最后,绘制了原始数据和变换后的数据的直方图,以便进行比较。
通过观察这些图表,可以发现,对于某些数据集,变换后的数据确实更接近正态分布。然而,对于其他数据集,变换的效果可能并不理想。这说明在实际应用中,需要根据数据的特性选择合适的变换方法,并通过可视化来评估变换的效果。
此外,还需要注意,QuantileTransformer虽然能够将任意分布的数据转换为高斯分布,但它需要足够的训练样本,并且容易在小数据集上过拟合。因此,在小数据集上,使用PowerTransformer可能是更好的选择。