数据预处理与特征工程
课纲概述
数据挖掘五大流程
获取数据
数据预处理 - 使数据匹配与模型
特征工程 - 将原始数据转换为更能代表特征的数据
目的:降低计算成本、提高模型上限。
提取特征
创造特征
建模
上线
数据预处理
数据的无量纲化
将不同规格的数据转换到统一规格。可以是线性的、非线性的。
数据归一化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 from sklearn.preprocessing import MinMaxScalerdata = [[-1 , 2 ], [-0.5 , 6 ], [0 , 10 ], [1 , 18 ]] import pandas as pdpd.DataFrame(data) scaler = MinMaxScaler() scaler = scaler.fit(data) result = scaler.transform(data) result """ array([[0. , 0. ], [0.25, 0.25], [0.5 , 0.5 ], [1. , 1. ]]) """ result_ = scaler.fit_transform(data) result_ """ array([[0. , 0. ], [0.25, 0.25], [0.5 , 0.5 ], [1. , 1. ]]) """ scaler.inverse_transform(result) """ array([[ 5. , 5. ], [ 6.25, 6.25], [ 7.5 , 7.5 ], [10. , 10. ]]) """ data = [[-1 , 2 ], [-0.5 , 6 ], [0 , 10 ], [1 , 18 ]] scaler = MinMaxScaler(feature_range=[5 ,10 ]) result = scaler.fit_transform(data) result """ array([[ 5. , 5. ], [ 6.25, 6.25], [ 7.5 , 7.5 ], [10. , 10. ]]) """ scaler = scaler.partial_fit(data)
数据标准化
数据标准化(Standardization,又称Z-score normalization) :当数据(x)按均值(μ)中心化后,再按标准差(σ)缩放,数据就会服从为均值为0,方差为1的正态分布(即标准正态分布),公式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 from sklearn.preprocessing import StandardScalerdata = [[-1 , 2 ], [-0.5 , 6 ], [0 , 10 ], [1 , 18 ]] scaler = StandardScaler() scaler.fit(data) scaler.mean_ scaler.var_ x_std = scaler.transform(data) x_std.mean() x_std.std() scaler.fit_transform(data) """ array([[-1.18321596, -1.18321596], [-0.50709255, -0.50709255], [ 0.16903085, 0.16903085], [ 1.52127766, 1.52127766]]) """ scaler.inverse_transform(x_std) """ array([[-1. , 2. ], [-0.5, 6. ], [ 0. , 10. ], [ 1. , 18. ]]) """
对于StandardScaler和MinMaxScaler来说,空值NaN会被当做是缺失值
在fit的时候会被忽略
在transform时保持NaN的状态显示
输入的X为特征矩阵,一般只接受二维以上 数组,一维导入会报错。
标准化和归一化的选择
大多数机器学习算法中,会选择StandardScaler进行特征缩放,因为MinMaxScaler对异常值非常敏感
在PCA,聚类,逻辑回归,支持向量机,神经网络这些算法中,StandardScaler往往是最好的选择
MinMaxScaler在不涉及距离度量、梯度、协方差计算以及数据需要被压缩到特定区间时使用广泛,比如数字图像处理中量化像素强度 时,都会使用MinMaxScaler将数据压缩于[0,1]区间之中。
建议先试试看StandardScaler,效果不好换MinMaxScaler。
除了StandardScaler和MinMaxScaler之外,sklearn中也提供了各种其他缩放处理(中心化只需要一个pandas广播一下减去某个数就好了,因此sklearn不提供任何中心化功能)
在希望压缩数据,却不影响数据的稀疏性时(不影响矩阵中取值为0的个数时),我们会使用MaxAbsScaler(只压缩,不中心化)
在异常值多,噪声非常大时,我们可能会选用分位数来无量纲化,此时使用RobustScaler
缺失值处理
机器学习和数据挖掘中所使用的数据,永远不可能是完美的 。很多特征,对于分析和建模来说意义非凡,但对于实际收集数据的人却不是如此,因此数据挖掘之中,常常会有重要的字段缺失值很多,但又不能舍弃字段的情况。因此,数据预处理中非常重要的一项就是处理缺失值 。
1 2 3 4 5 6 7 8 class sklearn .impute .SimpleImputer ( missing_values=nan, strategy='mean' , fill_value=None , verbose=0 , copy=True )
missing_values
strategy
填补策略,默认采用均值
输入"mean"使用均值填补(仅对数值型特征可用)
输入"median"用中值填补(仅对数值型特征可用)
输入"most_frequent"用众数填补(对数值型和字符型特征都可用)
输入"constant"表示请参考参数"fill_value"中的值(对数值型和字符型特征都可用)
fill_value
当参数startegy为"constant"的时候可用,可输入字符串或数字表示要填充的值,常用0
copy
默认为True,将创建特征矩阵的副本,反之则会将缺失值填补到原本的特征矩阵中去
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 import pandas as pddata = pd.read_csv(r"..\datasets\Narrativedata.csv" ,index_col=0 ) data.head() data.info() """ <class 'pandas.core.frame.DataFrame'> Int64Index: 891 entries, 0 to 890 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Age 714 non-null float64 1 Sex 891 non-null object 2 Embarked 889 non-null object 3 Survived 891 non-null object dtypes: float64(1), object(3) memory usage: 34.8+ KB """ Age = data.loc[:,"Age" ].values.reshape(-1 ,1 ) Age[:20 ] """ array([[22.], [38.], [26.], [35.], [35.], [nan], [54.], [ 2.], [27.], [14.]]) """ Age = data.loc[:,"Age" ].values.reshape(-1 ,1 ).shape """ (-1, 1) """ from sklearn.impute import SimpleImputerimp_mean = SimpleImputer() imp_median = SimpleImputer(strategy="median" ) imp_0 = SimpleImputer(strategy="constant" ,fill_value=0 ) imp_mean = imp_mean.fit_transform(Age) imp_median = imp_median.fit_transform(Age) imp_0 = imp_0.fit_transform(Age) imp_mean[:20 ] imp_median[:10 ] imp_0[:10 ] data.loc[:,"Age" ] = imp_median Embarked = data.loc[:,"Embarked" ].values.reshape(-1 ,1 ) imp_mode = SimpleImputer(strategy = "most_frequent" ) data.loc[:,"Embarked" ] = imp_mode.fit_transform(Embarked) data.info() """ <class 'pandas.core.frame.DataFrame'> Int64Index: 891 entries, 0 to 890 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Age 891 non-null float64 1 Sex 891 non-null object 2 Embarked 891 non-null object 3 Survived 891 non-null object dtypes: float64(1), object(3) memory usage: 34.8+ KB """
我们同样可以使用Numpy和Pandas进行填补
1 2 3 4 5 6 7 8 9 import pandas as pddata = pd.read_csv(r"..\datasets\Narrativedata.csv" ,index_col=0 ) data.head() data.loc[:,"Age" ] = data.loc[:,"Age" ].fillna(data.loc[:,"Age" ].median()) data.dropna(axis=0 ,inplace=True )
处理分类型数据
在现实中,许多标签和特征在数据收集完毕的时候,都不是以数字来表现的:
付费方式可能包含 [“支付宝”,“现金”,“微信”]
学历的取值可以是 [“小学”,“初中”,“高中”,“大学”]
在这种情况下,为了让数据适应算法和库,我们必须将数据进行编码 ,也就是要将文字型数据转换为数值型 。
preprocessing.LabelEncoder
标签专用,将分类转换为分类数值
1 2 from sklearn.preprocessing import LabelEncodery = data.iloc[:,-1 ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 le = LabelEncoder() le = le.fit(y) label = le.transform(y) """ array(['No', 'Unknown', 'Yes'], dtype=object) """ data.iloc[:,-1 ] = label
也可以一步完成
1 2 from sklearn.preprocessing import LabelEncoderdata.iloc[:,-1 ] = LabelEncoder().fit_transform(data.iloc[:,-1 ])
preprocessing.OrdinalEncoder
特征专用,将分类特征转换为分类数值
1 2 3 4 5 6 7 8 9 from sklearn.preprocessing import OrdinalEncoderdata_ = data.copy() data_.iloc[:,1 :-1 ] = OrdinalEncoder().fit_transform(data_.iloc[:,1 :-1 ])
preprocessing.OneHotEncoder
独热编码,创建哑变量
我们刚才已经用OrdinalEncoder把分类变量Sex和Embarked都转换成数字对应的类别了。在舱门Embarked这一列中,我们使用 [0,1,2] 代表了三个不同的舱门,然而这种转换是正确的吗?
我们来思考三种不同性质的分类数据:
舱门(S,C,Q)
三种取值S,C,Q是相互独立的,彼此之间完全没有联系,表达的是 S≠C≠Q 的概念。这是名义变量
学历(小学,初中,高中)
三种取值不是完全独立的,我们可以明显看出,在性质上可以有高中>初中>小学这样的联系,学历有高低,但是学历取值之间却不是可以计算的,我们不能说小学 + 某个取值 = 初中。这是有序变量
体重(>45kg,>90kg,>135kg)
各个取值之间有联系,且是可以互相计算的,比如135kg - 45kg = 90kg,分类之间可以通过数学计算互相转换。这是有距变量 。
然而在对特征进行编码的时候,这三种分类数据都会被我们转换为 [0,1,2],这三个数字在算法看来,是连续且可以计算的,这三个数字相互不等,有大小,并且有着可以相加相乘的联系。所以算法会把舱门,学历这样的分类特征,都误会成是体重这样的分类特征。我们把分类转换成数字的时候,忽略了数字中自带的数学性质 ,所以给算法传达了一些不准确的信息,这会影响我们的建模。
OrdinalEncoder可以用来处理有序变量 ,但对于名义变量 ,我们只有使用哑变量 的方式来处理,才能够尽量向算法传达最准确的信息:
三个取值是没有可计算性质的,是“有你就没有我”的不等概念。
因此我们需要使用独热编码,将性别与舱门都转换为哑变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 from sklearn.preprocessing import OneHotEncoderX = data.iloc[:,1 :-1 ] enc = OneHotEncoder(categories='auto' ).fit(X) result = enc.transform(X).toarray() enc.get_feature_names() newdata = pd.concat([data,pd.DataFrame(result)],axis=1 ) newdata.head() newdata.drop(["Sex" ,"Embarked" ],axis=1 ,inplace=True ) newdata.columns = ["Age" ,"Survived" ,"Female" ,"Male" ,"Embarked_C" ,"Embarked_Q" ,"Embarked_S" ] newdata.head()
总结
数据类型以及常用的统计量
连续性特征性特征
sklearn.preprocessing.Binarizer
根据阈值将数据二值化(将特征值设置为0或1),用于处理连续型变量。大于阈值的值映射为1,而小于或等于阈值的值映射为0。默认阈值为0时,特征中所有的正值都映射到1。二值化是对文本计数数据的常见操作,分析人员可以决定仅考虑某种现象的存在与否。它还可以用作考虑布尔随机变量的估计器的预处理步骤(例如,使用贝叶斯设置中的伯努利分布建模)。
1 2 3 4 5 6 7 8 9 data_2 = data.copy() from sklearn.preprocessing import BinarizerX = data_2.iloc[:,0 ].values.reshape(-1 ,1 ) transformer = Binarizer(threshold=30 ).fit_transform(X)
preprocessing.KBinsDiscretize
这是将连续型变量划分为分类变量的类,能够将连续型变量排序后按顺序分箱后编码。
总共包含三个重要参数:
1 2 3 4 5 6 7 8 9 10 11 12 from sklearn.preprocessing import KBinsDiscretizerX = data.iloc[:,0 ].values.reshape(-1 ,1 ) est = KBinsDiscretizer(n_bins=3 , encode='ordinal' , strategy='uniform' ) est.fit_transform(X) set (est.fit_transform(X).ravel())est = KBinsDiscretizer(n_bins=3 , encode='onehot' , strategy='uniform' ) est.fit_transform(X).toarray()