在前文中,我们对数据进行了初步处理,主要针对异常值和缺失值。
在本文中,我们将对数据做进一步处理,主要对特征进行分桶以及标准化。
离散化连续特征,例如将不同年龄段的人分成不同的类别。
是否使用特征离散化,其实是使用“海量离散特征+简单模型”,还是“少量连续特征+复杂模型”的问题。
对于线性模型,通常使用多特征。
对于非线性模型(如深度学习),通常使用“少量连续特征+复杂模型”。
采用同等宽度的不同尺度进行分桶,适合分布比较均匀的特征。例如一个人的年龄可以分为老,中,青三个桶。
这部分可以使用pandas中的cut实现,下面使用二手车数据举例。首先我们观察该数据的分布情况:
train_data['power'].plot.hist()
cut = pd.cut(train_data['power'], 6, labels=False)
cut.plot.hist()
分桶后分布如下:
同时也可以自己自定义分桶区间,根据业务逻辑定义分桶:
cut = pd.cut(train_data['power'], [0,100,200,300], labels=False, include_lowest=True)
有时候数据的分布是不均匀的,所以我们需要首先分析数据的分布情况然后再根据不同的分布进行分桶。
# 分别查看0.1-1之间的分位数的数据个数
w = train_data['power'].describe (percentiles=[i for i in np.arange(0,1,0.1)])[4:]
# 直接传入cut进行分桶
pd.cut(train_data['power'],w,labels=range(10), include_lowest=True)
先聚类(如k-means),然后对每一类的连续值进行标记。把每一类的中心点求出来加上每一类的首尾点构成分箱点,然后使用cut函数进行分箱。
kmodel=sklearn.cluster.KMeans(n_clusters=5)
data = np.array(train_data.loc[:,'power':])
kmodel.fit(data)
c = pd.DataFrame(kmodel.cluster_centers_)
c = c.sort_values(by=0)
# 使用滑动窗口求中心点的中心
c = c.rolling(2).mean()
c = c[0].iloc[1:].append(pd.Series(train_data['power'].min()))
c = c.append(pd.Series(train_data['power'].max()))
c.sort_values()
print(c)
cut = pd.cut(train_data['power'],c.sort_values(), include_lowest=True, labels=False)
cut.plot.hist()
以下两部分,均来自
卡方分箱是自底向上的(即基于合并的)数据离散化方法。它依赖于卡方检验:具有最小卡方值的相邻区间合并在一起,直到满足确定的停止准则。
对于精确的离散化,相对类频率在一个区间内应当完全一致。因此,如果两个相邻的区间具有非常类似的类分布,则这两个区间可以合并;否则,它们应当保持分开。而低卡方值表明它们具有相似的类分布。
KS(Kolmogorov-Smirnov)用于模型风险区分能力进行评估,指标衡量的是好坏样本累计部分之间的差距 。KS值越大,表示该变量越能将正,负客户的区分程度越大。
通常来说,KS>0.2即表示特征有较好的准确率。
虽然大部分机器学习模型都需要做标准化和归一化,也有不少模型可以不做做标准化和归一化,主要是基于概率分布的模型,比如决策树大家族的CART,随机森林等。当然此时使用标准化也是可以的,大多数情况下对模型的泛化能力也有改进。
将训练集中的数值特征的值缩放成为均值为0,方差为1的状态,几乎所有的线性模型都会用到此标准化。
代码如下:
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler().fit(train_data)
train_data = scaler.transform(train_data)
将训练集中某一列数值特征的值缩放到0和1之间,如果数据的输出结果有要求,数据较为稳定且不存在极端的最大最小值,则使用归一化。
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
train_data = scaler.fit_transform(train_data)
将训练集中的数值缩放到[-1,1]零均值之间。
def mean_norm(data, col):
# data: DataFrame 需要处理的数据
# col: 需要处理的列名
# return: DataFrame mean_normed
data_max = data[col].max()
data_min = data[col].min()
data_mean = data[col].mean()
data[col] = data[col].apply(lambda x:(x - data_mean)/(data_max - data_min))
return data
print(train_data['power'])
mean_norm(train_data, 'power')['power']
mean_norm(train_data, 'power')['power'].mean()
如果后续的模型构建中需要使用距离,则使得完整向量具有长度1。在一些应用中(例如直方图特征),使用特征向量的L1范数(即曼哈顿距离,城市块长度或出租车几何)可能更实际。
import sklearn
Normalizer = sklearn.preprocessing.Normalizer(norm='l2', copy=True)
Normalize = Normalizer.fit(train_data)
train_data = Normalize.transform(train_data)
专为稀疏数据而生。将每个要素缩放到[-1,1]范围,它不会移动/居中数据,因此不会破坏任何稀疏性。该估计器单独地缩放每个特征,使得训练集中的每个特征的最大绝对值将是1.0。该缩放器也可以应用于稀疏CSR或CSC矩阵。
MaxAbsScaler = sklearn.preprocessing.MaxAbsScaler()
MaxAbsScaler = MaxAbsScaler.fit(train_data)
train_data = MaxAbsScaler.transform(train_data)
如果你的数据包含许多异常值,使用均值和方差缩放可能并不是一个很好的选择。这种情况下,你可以使用 robust_scale 以及 RobustScaler 作为替代品。它们对你的数据的中心和范围使用更有鲁棒性的估计。
RobustScaler = sklearn.preprocessing.RobustScaler()
RobustScaler = RobustScaler.fit(train_data)
train_data = RobustScaler.transform(train_data)
train_data
对数缩放对于处理长尾分布且取值为正数的数值变量非常有效,它将大端长尾压缩为短尾,并将小端进行延伸,平方根或者对数变换是幂变换的特例,在统计学中都称为方差稳定的变换。
import numpy as np
train_data['fea'] = np.log(train_data['fea']
本文总结了多种不同的数据分桶和标准化的方法,他们都有不同的应用场景,我们应该结合实践经验选择相对应的方法。
另外,从数据处理到构建高效模型系列已经全部更新完毕,感兴趣的朋友可以点击下列链接进行支持一下: