参加kaggle比赛过程中,看到很多人在预处理阶段会对某些特征X做如下操作 Y = log(1+X), 说是可以把这个特征的分布正态化, 使其更加符合后面数据挖掘方法对数据分布的假设. 自己试了一下,有时的确可以提高准确度,有时却降低了准确度,很好奇其中的原理,遂在网上搜索了一番,整理如下.
Y = log(1+X)这个操作的真名应该时boxcox变换,用来降低X的skewness值,达到接近正态分布的目的. boxcox变换定义如下
参数
λ
λ
, 输入X,输出Y
ifλ!=0,Y=(Yλ−1)/λ
i
f
λ
!
=
0
,
Y
=
(
Y
λ
−
1
)
/
λ
ifλ=0,Y=log(1+X)
i
f
λ
=
0
,
Y
=
l
o
g
(
1
+
X
)
上图lambda取不同值时, (X,Y)的曲线, boxcox变换的工作原理就在这些曲线的斜率中: 曲线斜率越大的区域,则对应区域的X变换后将被拉伸, 变换后这段区域的方差加大; 曲线斜率越小的区域, 对应区域的X变换后将被压缩, 变换后这段区域的方差变小.
右图中看出lambda = 0时, 取值较小的部分被拉伸, 取值较大的部分被压缩; lambda > 1时则相反. 所以boxcox变换的应用必须先分析输入X的分布是哪一种偏斜: X分布左偏,则应该应用lambda = 0的变换; X分布又偏,则应该应用lambda > 1的变换.
下图是一个左偏的例子
本次实验代码
import os,sys,pdb
import numpy as np
import matplotlib.pyplot as plt
import math
from scipy.stats import skew
X = [x/10.0 for x in range(100)]
plt.figure()
for p in range(0,4,1):
if p != 0:
Y = (np.power(X,p) - 1) / p
else:
X = [x + 1 for x in X]
Y = np.log(X)
plt.subplot('22%d'%(p+1))
plt.plot(X,Y)
plt.title('lambda = %d'%p)
plt.suptitle('boxcox transform \n Y = (X^lambda - 1) / lambda if lambda != 0 \n Y = log(1+X) if labmda = 0')
X = [x/10.0 for x in range(100)]
plt.figure()
Y = []
for x in X:
if x < 3:
Y.append( math.exp(-(x-3)**2/(0.5) ) )
else:
Y.append( math.exp(-(x-3)**2/(20.0) ) )
plt.plot(X,Y,label='before transform skew=%f'%skew(Y))
Y = np.log(map( lambda y: y+1, Y))
#Y = map(lambda y: y**2-1, Y)
plt.plot(X,Y,label='after transform log(1+Y), skew=%f'%skew(Y))
plt.legend()
plt.show()