赞
踩
我们的数据一般都是有单位的,比如身高的单位有m,cm,这个无量纲化并不是说把m变成cm,而是说,无论是m还是cm,最后都会变成1,也就是没有了单位。
无量纲化使不同规格的数据转换到同一规格。常见的无量纲化方法有标准化和归一化。
数据标准化的原因:
标准化的前提是特征值服从正态分布,标准化后,其转换成标准正态分布。
基于原始数据的均值(mean)和标准差(standarddeviation)进行数据的标准化。将A的原始值x使用z-score标准化到x’。z-score标准化方法适用于属性A的最大值和最小值未知的情况,或有超出取值范围的离群数据的情况。
均值和标准差都是在样本集上定义的,而不是在单个样本上定义的。标准化是针对某个属性的,需要用到所有样本在该属性上的值。
优点:
缺点:
from sklearn.preprocessing import StandardScaler
#标准化,返回值为标准化后的数据
standardScaler = StandardScaler().fit(X_train)
standardScaler.transform(X_train)
区间缩放法利用了边界值信息,将属性缩放到[0,1]。
实现代码
from sklearn.preprocessing import MinMaxScaler
#区间缩放,返回值为缩放到[0, 1]区间的数据
minMaxScaler = MinMaxScaler().fit(X_train)
minMaxScaler.transform(X_train)
缺点:
单独地缩放和转换每个特征,使得训练集中的每个特征的最大绝对值将为1.0,将属性缩放到[-1,1]。它不会移动/居中数据,因此不会破坏任何稀疏性。
缺点:
实现代码
from sklearn.preprocessing import MaxAbsScaler
maxAbsScaler = MaxAbsScaler().fit(X_train)
maxAbsScaler.transform(X_train)
正则化的过程是将每个样本缩放到单位范数(每个样本的范数为1),如果要使用如二次型(点积)或者其它核方法计算两个样本之间的相似性这个方法会很有用。
该方法是文本分类和聚类分析中经常使用的向量空间模型(Vector Space Model)的基础。
Normalization主要思想是对每个样本计算其p-范数,然后对该样本中每个元素除以该范数,这样处理的结果是使得每个处理后样本的p-范数(l1-norm,l2-norm)等于1。
实现代码
from sklearn.preprocessing import Normalizer
#归一化,返回值为归一化后的数据
normalizer = Normalizer(norm='l2').fit(X_train)
normalizer.transform(X_train)
相同点:
它们的相同点在于都能取消由于量纲不同引起的误差;都是一种线性变换,都是对向量X按照比例压缩再进行平移。
不同点:
一般来说,建议优先使用标准化。对于输出有要求时再尝试别的方法,如归一化或者更加复杂的方法。很多方法都可以将输出范围调整到[0, 1],如果我们对于数据的分布有假设的话,更加有效的方法是使用相对应的概率密度函数来转换。
除了上面介绍的方法外,还有一些相对没这么常用的处理方法:RobustScaler、PowerTransformer、QuantileTransformer和QuantileTransformer等。
离散化是数值型特征非常重要的一个处理,其实就是要将数值型数据转化成类别型数据。连续值的取值空间可能是无穷的,为了便于表示和在模型中处理,需要对连续值特征进行离散化处理。
分箱的重要性及其优势:
自定义分箱,是指根据业务经验或者常识等自行设定划分的区间,然后将原始数据归类到各个区间中。
定义
按照相同宽度将数据分成几等份。
从最小值到最大值之间,均分为 N 等份, 这样, 如果 A,B 为最小最大值, 则每个区间的长度为 W=(B−A)/N , 则区间边界值为A+W,A+2W,….A+(N−1)W 。这里只考虑边界,每个等份里面的实例数量可能不等。
缺点是受到异常值的影响比较大
import pandas as pd df = pd.DataFrame([[22,1],[13,1],[33,1],[52,0],[16,0],[42,1],[53,1],[39,1],[26,0],[66,0]],columns=['age','Y']) df['age_bin_2'] = pd.cut(df['age'],3) #新增一列存储等距划分的分箱特征 display(df) # 输出 age Y age_bin 0 22 1 (12.947, 30.667] 1 13 1 (12.947, 30.667] 2 33 1 (30.667, 48.333] 3 52 0 (48.333, 66.0] 4 16 0 (12.947, 30.667] 5 42 1 (30.667, 48.333] 6 53 1 (48.333, 66.0] 7 39 1 (30.667, 48.333] 8 26 0 (12.947, 30.667] 9 66 0 (48.333, 66.0]
定义
将数据分成几等份,每等份数据里面的个数是一样的。
区间的边界值要经过选择,使得每个区间包含大致相等的实例数量。比如说 N=10 ,每个区间应该包含大约10%的实例。
实现程序
import pandas as pd df = pd.DataFrame([[22,1],[13,1],[33,1],[52,0],[16,0],[42,1],[53,1],[39,1],[26,0],[66,0]],columns=['age','Y']) df['age_bin_1'] = pd.qcut(df['age'],3) #新增一列存储等频划分的分箱特征 display(df) # 输出 age Y age_bin 0 22 1 (12.999, 26.0] 1 13 1 (12.999, 26.0] 2 33 1 (26.0, 42.0] 3 52 0 (42.0, 66.0] 4 16 0 (12.999, 26.0] 5 42 1 (26.0, 42.0] 6 53 1 (42.0, 66.0] 7 39 1 (26.0, 42.0] 8 26 0 (12.999, 26.0] 9 66 0 (42.0, 66.0]
定义
基于k均值聚类的分箱:k均值聚类法将观测值聚为k类,但在聚类过程中需要保证分箱的有序性:第一个分箱中所有观测值都要小于第二个分箱中的观测值,第二个分箱中所有观测值都要小于第三个分箱中的观测值,等等。
实现步骤
from sklearn.cluster import KMeans
kmodel=KMeans(n_clusters=k) #k为聚成几类
kmodel.fit(data.reshape(len(data),1))) #训练模型
c=pd.DataFrame(kmodel.cluster_centers_) #求聚类中心
c=c.sort_values(by=’列索引') #排序
w=pd.rolling_mean(c,2).iloc[1:] #用滑动窗口求均值的方法求相邻两项求中点,作为边界点
w=[0] +list(w[0] + [ data.max() ] #把首末边界点加上
d3= pd.cut(data,w,labels=range(k)) #cut函数
定义
二值化可以将数值型(numerical)的feature进行阀值化得到boolean型数据。这对于下游的概率估计来说可能很有用(比如:数据分布为Bernoulli分布时)。
实现代码
from sklearn.preprocessing import Binarizer
# Binarizer函数也可以设定一个阈值,结果数据值大于阈值的为1,小于阈值的为0
binarizer = Binarizer(threshold=0.0).fit(X_train)
binarizer.transform(X_train)
定义
自底向上的(即基于合并的)数据离散化方法。它依赖于卡方检验:具有最小卡方值的相邻区间合并在一起,直到满足确定的停止准则。
基本思想
对于精确的离散化,相对类频率在一个区间内应当完全一致。因此,如果两个相邻的区间具有非常类似的类分布,则这两个区间可以合并;否则,它们应当保持分开。而低卡方值表明它们具有相似的类分布。
实现步骤
根据要离散的属性对实例进行排序,每个实例属于一个区间;
阈值的意义
类别和属性独立时,有90%的可能性,计算得到的卡方值会小于4.6。大于阈值4.6的卡方值就说明属性和类不是相互独立的,不能合并。如果阈值选的大,区间合并就会进行很多次,离散后的区间数量少、区间大。
注意
2)最小熵法分箱
需要使总熵值达到最小,也就是使分箱能够最大限度地区分因变量的各类别。
熵是信息论中数据无序程度的度量标准,提出信息熵的基本目的是找出某种符号系统的信息量和冗余度之间的关系,以便能用最小的成本和消耗来实现最高效率的数据存储、管理和传递。
数据集的熵越低,说明数据之间的差异越小,最小熵划分就是为了使每箱中的数据具有最好的相似性。给定箱的个数,如果考虑所有可能的分箱情况,最小熵方法得到的箱应该是具有最小熵的分箱。
数据分布的倾斜有很多负面的影响。我们可以使用特征工程技巧,利用统计或数学变换来减轻数据分布倾斜的影响。使原本密集的区间的值尽可能的分散,原本分散的区间的值尽量的聚合。
这些变换函数都属于幂变换函数簇,通常用来创建单调的数据变换。它们的主要作用在于它能帮助稳定方差,始终保持分布接近于正态分布并使得数据与分布的平均值无关。
Log变换通常用来创建单调的数据变换。它的主要作用在于帮助稳定方差,始终保持分布接近于正态分布并使得数据与分布的平均值无关。
Log 变换属于幂变换函数簇。该函数用数学表达式表示为
y = \log_b(x)
自然对数使用 b=e,e=2.71828,通常叫作欧拉常数。你可以使用通常在十进制系统中使用的 b=10 作为底数。
当应用于倾斜分布时 Log 变换是很有用的,因为Log变换倾向于拉伸那些落在较低的幅度范围内自变量值的范围,倾向于压缩或减少更高幅度范围内的自变量值的范围。从而使得倾斜分布尽可能的接近正态分布。
针对一些数值连续特征的方差不稳定,特征值重尾分布我们需要采用Log化来调整整个数据分布的方差,属于方差稳定型数据转换。比如在词频统计中,有些介词的出现数量远远高于其他词,这种词频分布的特征就会现有些词频特征值极不协调的状况,拉大了整个数据分布的方差。这个时候,可以考虑Log化。尤其在分本分析领域,时间序列分析领域,Log化非常常见, 其目标是让方差稳定,把目标关注在其波动之上。
代码示例
numeric_subset['log_' + col] = np.log(numeric_subset[col])
Box-Cox 变换是另一个流行的幂变换函数簇中的一个函数。该函数有一个前提条件,即数值型值必须先变换为正数(与 log 变换所要求的一样)。万一出现数值是负的,使用一个常数对数值进行偏移是有帮助的。
Box-Cox变换是Box和Cox在1964年提出的一种广义幂变换方法,是统计建模中常用的一种数据变换,用于连续的响应变量不满足正态分布的情况。Box-Cox变换之后,可以一定程度上减小不可观测的误差和预测变量的相关性。Box-Cox变换的主要特点是引入一个参数,通过数据本身估计该参数进而确定应采取的数据变换形式,Box-Cox变换可以明显地改善数据的正态性、对称性和方差相等性,对许多实际数据都是行之有效的。
4)实现代码
import scipy.stats as spstats
# 从数据分布中移除非零值
income = np.array(fcc_survey_df['Income'])
income_clean = income[~np.isnan(income)]
# 计算最佳λ值
l, opt_lambda = spstats.boxcox(income_clean)
print('Optimal lambda value:', opt_lambda)
# 进行Box-Cox变换
fcc_survey_df['Income_boxcox_lambda_opt'] = spstats.boxcox(fcc_survey_df['Income'],lmbda=opt_lambda)
或
from sklearn.preprocessing import PowerTransformer
bc = PowerTransformer(method='box-cox')
bc.fit(X_train).transform(X_test)
Yeo-Johnson变换(Yeo-Johnson transformation)或“Yeo-Johnson幂变换(Yeo-Johnson power transformation)”是幂变换(power transformation)的方法之一,通过构建一组单调函数对随机变量进行数据变换 。
Yeo-Johnson变换具有幂变换的一般性质,能够减小随机变量的异方差性(heteroscedasticity)并放大其正态性(normality),使其概率密度函数的形态向正态分布靠近 。Yeo-Johnson变换的特点在于其可被应用于包含0值和负值的样本中,因此其也被认为是Box-Cox变换在实数域的推广
Yeo-Johnson变换作为数据标准化的方法之一被应用于数据挖掘和机器学习的数据预处理阶段。当随机变量正态性较差时,对其使用Yeo-Johnson变换进行预处理,有利于对该随机变量进行基于正态假设的统计分析
实现代码
from sklearn.preprocessing import PowerTransformer
yj = PowerTransformer(method='yeo-johnson')
yj.fit(X_train).transform(X_test)
实现代码
from sklearn.preprocessing import QuantileTransformer
qt = QuantileTransformer(n_quantiles=500, output_distribution='normal',
random_state=rng)
qt.fit(X_train).transform(X_test)
1
在统计学中,分类特征是可以采用有限且通常固定数量的可能值之一的变量,基于某些定性属性将每个个体或其他观察单元分配给特定组或名义类别。
LabelEncoder是对不连续的数字或者文本进行编号,编码值介于0和n_classes-1之间的标签。
优点:相对于OneHot编码,LabelEncoder编码占用内存空间小,并且支持文本特征编码。
缺点:它隐含了一个假设:不同的类别之间,存在一种顺序关系。在具体的代码实现里,LabelEncoder会对定性特征列中的所有独特数据进行一次排序,从而得出从原始输入到整数的映射。所以目前还没有发现标签编码的广泛使用,一般在树模型中可以使用。
例如:比如有[dog,cat,dog,mouse,cat],我们把其转换为[1,2,1,3,2]。这里就产生了一个奇怪的现象:dog和mouse的平均值是cat。
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
le.fit(["paris", "paris", "tokyo", "amsterdam"])
print('特征:{}'.format(list(le.classes_)))
# 输出 特征:['amsterdam', 'paris', 'tokyo']
print('转换标签值:{}'.format(le.transform(["tokyo", "tokyo", "paris"])))
# 输出 转换标签值:array([2, 2, 1]...)
print('特征标签值反转:{}'.format(list(le.inverse_transform([2, 2, 1]))))
# 输出 特征标签值反转:['tokyo', 'tokyo', 'paris']
OneHotEncoder用于将表示分类的数据扩维。最简单的理解就是与位图类似,设置一个个数与类型数量相同的全0数组,每一位对应一个类型,如该位为1,该数字表示该类型。
OneHotEncode只能对数值型变量二值化,无法直接对字符串型的类别变量编码。
独热编码是因为大部分算法是基于向量空间中的度量来进行计算的,为了使非偏序关系的变量取值不具有偏序性,并且到圆点是等距的。使用one-hot编码,将离散特征的取值扩展到了欧式空间,离散特征的某个取值就对应欧式空间的某个点。将离散型特征使用one-hot编码,会让特征之间的距离计算更加合理。
为什么特征向量要映射到欧式空间?
将离散特征通过one-hot编码映射到欧式空间,是因为,在回归、分类、聚类等机器学习算法中,特征之间距离的计算或相似度的计算是非常重要的,而我们常用的距离或相似度的计算都是在欧式空间的相似度计算。
假如有三种颜色特征:红、黄、蓝。
在利用机器学习的算法时一般需要进行向量化或者数字化。那么你可能想令 红=1,黄=2,蓝=3。那么这样其实实现了标签编码,即给不同类别以标签。然而这意味着机器可能会学习到“红<黄<蓝”,但这并不是我们的让机器学习的本意,只是想让机器区分它们,并无大小比较之意。
所以这时标签编码是不够的,需要进一步转换。因为有三种颜色状态,所以就有3个比特。即红色:1 0 0 ,黄色: 0 1 0,蓝色:0 0 1 。如此一来每两个向量之间的距离都是根号2,在向量空间距离都相等,所以这样不会出现偏序性,基本不会影响基于向量空间度量算法的效果。
优点:独热编码解决了分类器不好处理属性数据的问题,在一定程度上也起到了扩充特征的作用。它的值只有0和1,不同的类型存储在垂直的空间。
缺点:当类别的数量很多时,特征空间会变得非常大。在这种情况下,一般可以用PCA来减少维度。而且one hot encoding+PCA这种组合在实际中也非常有用。
使用sklearn实现
注:当特征是字符串类型时,需要先用 LabelEncoder() 转换成连续的数值型变量,再用 OneHotEncoder() 二值化 。
from sklearn.preprocessing import OneHotEncoder
enc = OneHotEncoder()
enc.fit([[0, 0, 3], [1, 1, 0], [0, 2, 1], [1, 0, 2]]) # fit来学习编码
enc.transform([[0, 1, 3]]).toarray() # 进行编码
# 输出:array([[ 1., 0., 0., 1., 0., 0., 0., 0., 1.]])
使用pandas实现
import pandas as pd import numpy as np sex_list = ['MALE', 'FEMALE', np.NaN, 'FEMALE', ] df = pd.DataFrame({'SEX': sex_list}) display(df) # 输出 SEX 0 MALE 1 FEMALE 2 NaN 3 FEMALE df = pd.get_dummies(df['SEX'],prefix='IS_SEX') display(df) # 输出 IS_SEX_FEMALE IS_SEX_MALE 0 0 1 1 1 0 2 0 0 3 1 0
功能与OneHotEncoder一样,但是OneHotEncode只能对数值型变量二值化,无法直接对字符串型的类别变量编码,而LabelBinarizer可以直接对字符型变量二值化。
from sklearn.preprocessing import LabelBinarizer lb = LabelBinarizer() lb.fit([1, 2, 6, 4, 2]) print(lb.classes_) # 输出 array([1, 2, 4, 6]) print(lb.transform([1, 6])) # 输出 array([[1, 0, 0, 0], [0, 0, 0, 1]]) print(lb.fit_transform(['yes', 'no', 'no', 'yes'])) # 输出 array([[1], [0], [0], [1]])
用于label encoding,生成一个(n_examples * n_classes)大小的0~1矩阵,每个样本可能对应多个label。
用户兴趣特征(如特征值:
”健身 电影 音乐”)适合使用多标签二值化,因为每个用户可以同时存在多种兴趣爱好。
from sklearn.preprocessing import MultiLabelBinarizer mlb = MultiLabelBinarizer() print(mlb.fit_transform([(1, 2), (3,)])) # 输出 array([[1, 1, 0], [0, 0, 1]]) print(mlb.classes_) # 输出:array([1, 2, 3]) print(mlb.fit_transform([{'sci-fi', 'thriller'}, {'comedy'}])) # 输出:array([[0, 1, 1], [1, 0, 0]]) print(list(mlb.classes_)) # 输出:['comedy', 'sci-fi', 'thriller']
平均数编码(mean encoding),针对高基数类别特征的有监督编码。当一个类别特征列包括了极多不同类别时(如家庭地址,动辄上万)时,可以采用。
平均数编码(mean encoding)的编码方法,在贝叶斯的架构下,利用所要预测的应变量(target variable),有监督地确定最适合这个定性特征的编码方式。在Kaggle的数据竞赛中,这也是一种常见的提高分数的手段。
算法原理详情可参考:平均数编码:针对高基数定性特征(类别特征)的数据预处理/特征工程。
如果某一个特征是定性的(categorical),而这个特征的可能值非常多(高基数),那么平均数编码(mean encoding)是一种高效的编码方式。在实际应用中,这类特征工程能极大提升模型的性能。
因为定性特征表示某个数据属于一个特定的类别,所以在数值上,定性特征值通常是从0到n的离散整数。例子:花瓣的颜色(红、黄、蓝)、性别(男、女)、地址、某一列特征是否存在缺失值(这种NA 指示列常常会提供有效的额外信息)。
一般情况下,针对定性特征,我们只需要使用sklearn的OneHotEncoder或LabelEncoder进行编码,这类简单的预处理能够满足大多数数据挖掘算法的需求。定性特征的基数(cardinality)指的是这个定性特征所有可能的不同值的数量。在高基数(high cardinality)的定性特征面前,这些数据预处理的方法往往得不到令人满意的结果。
和独热编码相比,节省内存、减少算法计算时间、有效增强模型表现。
MeanEnocodeFeature = ['item_city_id','item_brand_id'] #声明需要平均数编码的特征
ME = MeanEncoder(MeanEnocodeFeature) #声明平均数编码的类
trans_train = ME.fit_transform(X,y)#对训练数据集的X和y进行拟合
test_trans = ME.transform(X_test)#对测试集进行编码
MeanEncoder实现源码详情可参考:平均数编码:针对高基数定性特征(类别特征)的数据预处理/特征工程。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。