目录
3. Sklearn中KBinsDiscretizer类分箱
5. 特征离散化
基础知识 前面提到了“离散型”和“连续型”这两种类型的特征。·
-
离散型∶在任意两个值之间具有可计数的值。
-
连续型∶在任意两个值之间具有无限个值。
-
算法要求:机器学习中的一些算法,比如决策树、朴素贝叶斯、对数概率回归(logistic回归)等算法,都要求变量必须是离散化的。
-
离散后数据好处:对于连续型特征,在离散化之后,能够降低对离群数据的影响,例如将表示年龄的特征离散化,大于50的是1,否则为0。如果此特征中出现了年龄为500的离群值,在离散化后,该离群值对特征的影响就被消除了。
-
相对于连续型特征,离散型特征在计算速度、表达能力、模型稳定性等方面都具有优势。
-
那么,如何实施特征离散化?
-
通常使用的离散化方法可以划分为“有监督的”和“无监督的”两类。离散化也可以称为“分箱”。
无监督离散化
无监督离散化就是不依据数据集的标签(有的数据集也没有标签),对特征实施离散化操作。
比如,考试结果等级评定规则是∶≥90分为A,80~<90分为B,60~<80分为C,<60分为D。 将学生百分制的卷面成绩依据规则转化为相应的等级,这样就实现了分数的离散化。在这个离散化过程中,没有根据学生的其他某种标签,这样的特征离散化就是无监督离散化。
import pandas as pd
ages = pd.DataFrame({'years':[10, 14, 30, 53, 67, 32, 45], 'name':['A', 'B', 'C', 'D', 'E', 'F', 'G']})
ages
1. pd.cut(等宽分箱)和pd.qcut(等频分箱)
#如果对特征“years”离散化,可以使用Pandas提供的函数cut。
pd.cut(ages['years'],3)
- cut 函数的第2个参数3,表示将 ages【years】划分为等宽的3个区间。
- 输出的“{(9.943,29.0]<(29.0,48.0]<(48.0,67.0]]”,表示每个区间的范围,依据每个样本的数值,将该样本标记为在相应的区间中。
因为离散化的别称是“分箱”,上面的操作称为“等宽分箱法”。
用pd.qcut函数实现。
pd.qcut(ages['years'],3)
输出对比:
- pd.cut(ages['years'],3)
- Categories (3, interval[float64]): [(9.943, 29.0] < (29.0, 48.0] < (48.0, 67.0]] 29-9.943=19.057 48-29=19 48.0-29=19 67-48=19
- pd.qcut(ages['years'],3)
- Categories (3, interval[float64]): [(9.999, 30.0] < (30.0, 45.0] < (45.0, 67.0]] 30-10=20 45-30=15 67-45=32
pd.qcut函数,按照数据出现频率百分比划分,比如要把数据分为四份,则四段分别是数据的0-25%,25%-50%,50%-75%,75%-100%
- pd.cut() 将指定序列 x,按指定数量等间距的划分(根据值本身而不是这些值的频率选择均匀分布的bins),或按照指定间距划分
- pd.qcut() 将指定序列 x,划分为 q 个区间,使落在每个区间的记录数一致
#重新指定value的标签
klass = pd.cut(ages['years'], 3, labels=[0, 1, 2]) # ①
ages['label'] = klass
ages
2. 不等距分箱
#另外解决方案,指定分割点,避免了离群值的影响。
ages2 = pd.DataFrame({'years':[10, 14, 30, 53, 300, 32, 45], 'name':['A', 'B', 'C', 'D', 'E', 'F', 'G']})
klass2 = pd.cut(ages2['years'], bins=[9, 30, 50, 300], labels=['Young', 'Middle', 'Senior']) # ③
ages2['label'] = klass2
ages2
3. Sklearn中KBinsDiscretizer类分箱
KBinsDiscretizer实现等宽分箱法的特征离散化,创建离散化模型,其中,
- n_bins表示所划分的区间个数(整数)或者划分区间的分割点(类数组对象),④的n_bins=3表示将特征数据划分为三部分。
- encode 表示离散化之后的结果保存方式(编码方式),可以选择三个值(onehot、onehot-dense、ordinal),默认值是onehot。这三个值的含义分别如下。
-
- onehot∶离散化之后再进行OneHot编码,并且返回一个稀疏矩阵。
-
- onehot-dense∶离散化之后再进行OneHot编码,并且返回数组。
-
- ordinal∶离散化之后,以整数数值标记相应的记录。
- strategy表示离散化所采用的策略。
-
- uniform∶每个分区的宽度相同。
-
- quantile∶默认值。每个分区的样本数量相同。
-
- kmeans∶根据k-means聚类算法设置分区。
⑤则使用所创建的模型对特征“years”的值进行转换,并用⑥合并到原有数据集中,比较特征"label"和"kbd"(忽略浮点数和整数的差异),会发现前面的操作的操作等效。
KBinsDiscretizer类的参数strategy的三个取值,代表了无监督离散化的三个常用方法。
#继续使用成的数据ages,用KBinsDiscretizer实现等宽分箱法的特征离散化
from sklearn.preprocessing import KBinsDiscretizer
kbd = KBinsDiscretizer(n_bins=3, encode='ordinal', strategy='uniform') # ④
trans = kbd.fit_transform(ages[['years']]) # ⑤
print(type(trans))
ages['kbd'] = trans[:, 0] # ⑥
ages
项目案例:
import numpy as np
from sklearn.datasets import load_iris
from sklearn.preprocessing import KBinsDiscretizer
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score
iris = load_iris()
#鸢尾花数据集中的各特征是连续值(花瓣、花萼的长度和宽度测量值),
#要求用此数据集训练机器学习的分类算法,并比较在离散化与原始值两种状态下的分类效果。
#鸢尾花数据集有4个特征
iris.feature_names
#简化问题,只选两个特征
X = iris.data
y = iris.target
X = X[:, [2, 3]]
%matplotlib inline
import matplotlib.pyplot as plt
plt.scatter(X[:, 0], X[:, 1], c=y, alpha=0.3, cmap=plt.cm.RdYlBu, edgecolor='black')
#离散化后的数据分布
Xd = KBinsDiscretizer(n_bins=10, encode='ordinal', strategy='uniform').fit_transform(X)
plt.scatter(Xd[:, 0], Xd[:, 1], c=y, cmap=plt.cm.RdYlBu, edgecolor='black')
离散化后的数据更泾渭分明,有利于分类算法应用。下面将以上两种数据用于决策树分类算法,比较优劣:
dtc = DecisionTreeClassifier(random_state=0) # ⑦
score1 = cross_val_score(dtc, X, y, cv=5) # ⑧
score2 = cross_val_score(dtc, X, y, cv=5) # ⑨
np.mean(score1), np.std(score1)
(0.9466666666666667, 0.039999999999999994)
np.mean(score2), np.std(score2)
(0.9466666666666667, 0.039999999999999994)
分别计算了X(未离散化)和Xd(已离散化)两个数据集在决策树模型中的评估得分,并分别计算了相应的平均值和标准差。从结果可以看出,在实施特征离散化之后,对优化模型的性能还是有价值的。 继续使用KBinsDiscretizer类对鸢尾花数据离散化,这次使用k-means方法
km = KBinsDiscretizer(n_bins=3, encode='ordinal', strategy='kmeans').fit_transform(X) # ⑩
s = cross_val_score(dtc, km, y, cv=5)
np.mean(s), np.std(s)
#模型的性能得到进一步提高。
(0.9733333333333334, 0.02494438257849294)
6. 数据规范化
标准化:
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
iris = datasets.load_iris()
#print(iris)
iris_std = StandardScaler().fit_transform(iris.data) # ①
使用了鸢尾花数据,①用StandardScaler类创建的标准化实例,并将数据标准化
iris['data'][:5]
#显示的是鸢尾花数据集中原有的前5个样本的数值,
>>>
array([[5.1, 3.5, 1.4, 0.2],
[4.9, 3. , 1.4, 0.2],
[4.7, 3.2, 1.3, 0.2],
[4.6, 3.1, 1.5, 0.2],
[5. , 3.6, 1.4, 0.2]])
iris_std[:5]
#显示的是这5个样本经过标准化变换之后的Z分数。
>>>
array([[-0.90068117, 1.01900435, -1.34022653, -1.3154443 ],
[-1.14301691, -0.13197948, -1.34022653, -1.3154443 ],
[-1.38535265, 0.32841405, -1.39706395, -1.3154443 ],
[-1.50652052, 0.09821729, -1.2833891 , -1.3154443 ],
[-1.02184904, 1.24920112, -1.34022653, -1.3154443 ]])
#经过标准化变换之后的数据集,各特征的平均值应该为0,标准差是1。
import numpy as np
np.mean(iris_std, axis=0)
>>>
array([-1.69031455e-15, -1.84297022e-15, -1.69864123e-15, -1.40924309e-15])
区间化
所谓区间化,是指特征的值经过变换后被限定在指定的区间。通常,会将特征的值限定在0~1的范围之内,为此要使用特征的最大值和最小值。
于是将某特征的值区间化到0与1之间的变换称为“Min-Max标准化”,公式:
(原始值-最小值) / (最大值 -最小值)
从上面公式不难看出,经过此类区间化的值,也去掉了原有的量纲,并表示了每个数值与最小值的相对距离。
●不适用场景:原始数据存在小部分很大/很小的数据时,会造成大部分数据规范化后接近于0/1,区分度不大
from sklearn.preprocessing import MinMaxScaler
iris_mm = MinMaxScaler().fit_transform(iris.data) # ②
iris_mm[:5]
#代码实现了对鸢尾花数据集的"Min-Max标准化"操作,显示了前5条样本的结果,这些数值分布在0与1之间,得到的“标准差标准化”的结果进行对比
>>>
array([[0.22222222, 0.625 , 0.06779661, 0.04166667],
[0.16666667, 0.41666667, 0.06779661, 0.04166667],
[0.11111111, 0.5 , 0.05084746, 0.04166667],
[0.08333333, 0.45833333, 0.08474576, 0.04166667],
[0.19444444, 0.66666667, 0.06779661, 0.04166667]])
np.concatenate 是numpy中对array进行拼接的函数
#对比缩放的效果
import numpy as np
import pandas as pd
X = pd.DataFrame({
'x1': np.concatenate([np.random.normal(20, 1, 1000), np.random.normal(1, 1, 25)]),
'x2': np.concatenate([np.random.normal(30, 1, 1000), np.random.normal(50, 1, 25)]),
})
X.sample(10)
#concatencate是对于数组的拼接函数 在numpy库内可以查询
#对X数据集分别用类RobustScaler和MinMaxScaler进行区间化
from sklearn.preprocessing import RobustScaler, MinMaxScaler
robust = RobustScaler()
robust_scaled = robust.fit_transform(X)
robust_scaled = pd.DataFrame(robust_scaled, columns=['x1', 'x2'])
minmax = MinMaxScaler()
minmax_scaled = minmax.fit_transform(X)
minmax_scaled = pd.DataFrame(minmax_scaled, columns=['x1', 'x2'])
%matplotlib inline
import matplotlib.pyplot as plt
import seaborn as sns
fig, (ax1, ax2, ax3) = plt.subplots(ncols=3, figsize=(9, 5))
ax1.set_title('Before Scaling')
sns.kdeplot(X['x1'], ax=ax1)
sns.kdeplot(X['x2'], ax=ax1)
ax2.set_title('After Robust Scaling')
sns.kdeplot(robust_scaled['x1'], ax=ax2)
sns.kdeplot(robust_scaled['x2'], ax=ax2)
ax3.set_title('After Min-Max Scaling')
sns.kdeplot(minmax_scaled['x1'], ax=ax3)
sns.kdeplot(minmax_scaled['x2'], ax=ax3)
归一化
#L2范数
from sklearn.preprocessing import Normalizer
norma = Normalizer() # ③
norma.fit_transform([[3, 4]])
>>>
array([[0.6, 0.8]])
#L1范数
norma1 = Normalizer(norm='l1')
norma1.fit_transform([[3, 4]])
>>>
array([[0.42857143, 0.57142857]])
#依据最大值进行归一
norma_max = Normalizer(norm='max')
norma_max.fit_transform([[3, 4]])
>>>
array([[0.75, 1. ]])
from mpl_toolkits.mplot3d import Axes3D
df = pd.DataFrame({
'x1': np.random.randint(-100, 100, 1000).astype(float),
'y1': np.random.randint(-80, 80, 1000).astype(float),
'z1': np.random.randint(-150, 150, 1000).astype(float),
})
scaler = Normalizer()
scaled_df = scaler.fit_transform(df)
scaled_df = pd.DataFrame(scaled_df, columns=df.columns)
fig = plt.figure(figsize=(9, 5))
ax1 = fig.add_subplot(121, projection='3d')
ax2 = fig.add_subplot(122, projection='3d')
ax1.scatter(df['x1'], df['y1'], df['z1'])
ax2.scatter(scaled_df['x1'], scaled_df['y1'], scaled_df['z1'])
一、数据标准化 / 归一化的作用
提升模型精度:标准化 / 归一化使不同维度的特征在数值上更具比较性,提高分类器的准确性。
提升收敛速度:对于线性模型,数据归一化使梯度下降过程更加平缓,更易正确的收敛到最优解
二、标准差标准化 StandardScaler
from sklearn.preprocessing import StandardScaler
使用均值与方差,对服从正态分布的数据处理,得到符合标准正态分布的数据
处理方法:标准化数据减去均值,然后除以标准差,经过处理后数据符合标准正态分布,即均值为0,标准差为1;
转化函数:x = (x-mean) / std;
适用性:适用于本身服从正态分布的数据;
Outlier 的影响:基本可用于有outlier的情况,但在计算方差和均值时outliers仍然会影响计算。
参数包括:with_mean, with_std, copy
with_mean:布尔型,默认为 True,表示在缩放前将数据居中,当尝试在稀疏矩阵上时,这不起作用(并且会引发异常),因为将它们居中需要构建一个密集矩阵,在常见的用例中,该矩阵可能太大而无法容纳在内存中;
with_std:布尔型,默认为True,表示将数据换算成单位方差(或等效的单位标准差);
copy : 布尔值,默认为True,可选参数,表示拷贝一份数据以避免在原数据上进行操作,若设置为 False 执行插入行规范化并避免复制。
属性包括:mean_, scale_, var_, n_samples_seen_
mean_:训练集中每个特征的平均值,当_mean=False时,为None;
scale_:每个特征数据的相对缩放;
var_:训练集中每个特征的方差,用于计算比例,当_ std =False时,为None;
n_samples_seen_:每个特征处理的样本数。如没有丢失的样本,n_samples_seen_是一个整数,否则是一个数组,并将被重置或递增。
三、极差标准化 / 归一化 MinMaxScaler
from sklearn.preprocessing import MinMaxScaler
区间缩放,基于最大最小值,将数据转换到0,1区间上的
处理方法:将特征缩放到给定的最小值和最大值之间,也可以将每个特征的最大绝对值转换至单位大小。这种方法是对原始数据的线性变换,将数据归一到[0,1]中间;
转换函数:x = (x-min) / (max-min);
适用性:适用于分布范围较稳定的数据,当新数据的加入导致max/min变化,则需重新定义;
Outlier 的影响:因为outlier会影响最大值或最小值,因此对outlier非常敏感。
参数包括:min, max, copy
min:默认为0,指定区间的下限;
max:默认为1,指定区间的上限;
copy : 布尔值,默认为True,可选参数,表示拷贝一份数据以避免在原数据上进行操作,若设置为 False 执行插入行规范化并避免复制。
属性包括:min_, scale_, data_min_, data_max_
min_:每个功能调整为最小;
scale_:每个特征数据的相对缩放;
data_min_:每个特征在数据中出现的最小值;
data_max_:每个特征在数据中心出现的最大值。
四、稳健标准化 RobustScaler
from sklearn.preprocessing import RobustScaler
使用具有鲁棒性的统计量缩放带有异常值(离群值)的数据
处理方法:该缩放器删除中位数,并根据百分位数范围(默认值为IQR:四分位间距)缩放数据;
IQR:是第1个四分位数(25%)和第3个四分位数(75%)之间的范围;
适用性:适用于包含许多异常值的数据;
Outlier 的影响:RobustScaler 利用IQR进行缩放来弱化 outlier 的影响。
参数包括:with_centering, with_scaling, quantile_range, copy
with_centering:布尔值,默认为 True,表示在缩放之前将数据居中。若使用稀疏矩阵时,这将导致转换引发异常,因为将它们居中需要建立一个密集的矩阵,在通常的使用情况下,该矩阵可能太大而无法容纳在内存中;
with_scaling : 布尔值,默认为True,表示将数据缩放到四分位数范围;
quantile_range : 元组,默认值为(25.0, 75.0)即 IQR,表示用于计算 scale_的分位数范围;
copy : 布尔值,默认为True,可选参数,表示拷贝一份数据以避免在原数据上进行操作,若设置为 False 执行插入行规范化并避免复制。
属性包括:center_, scale_
center_ :训练集中每个属性的中位数;
scale_ :训练集中每个属性的四分位间距。
五、总结
在分类、聚类算法中,需要使用距离来度量相似性的时候、或者使用PCA技术进行降维的时候,StandardScaler表现更好(避免不同量纲对方差、协方差计算的影响);
在不涉及距离度量、协方差、数据不符合正态分布、异常值较少的时候,可使用MinMaxScaler。(eg:图像处理中,将RGB图像转换为灰度图像后将其值限定在 [0, 255] 的范围);
在带有的离群值较多的数据时,推荐使用RobustScaler。