sklearn机器学习|学习笔记——sklearn数据预处理和特征工程(过滤法、嵌入法、包装法)

sklearn数据预处理和特征工程 数据挖掘五大流程

  • 获取数据
  • 数据预处理
    • 目的:让数据适应模型,匹配模型的需求
  • 特征工程
    • 面对的问题:特征之间有相关性、特征和标签无关、特征太多或太少或无法展示数据的真实面貌。
    • 目的:降低计算成本;提升模型上限;
  • 建模,测试模型并预测出结果
  • 上线,验证模型效果
数据预处理 **模块preprocessing:**几乎包含数据预处理的所有内容
**模块lmpute:**填补缺失值专用
模块feature_ selection: 包含特征选择的各种方法的实践
**模块decomposition:**包含降维算法
数据无量纲化: 将不同规格的数据转换为同一规则,或不同分布的数据转换到某个特定分布的需求。
以梯度和矩阵为核心的算法中,无量纲化可以加快求解速度;如逻辑回归、支持向量机、神经网络
**以距离为核心的模型,**无量纲化可以帮助我们提升模型精度,避免某一个取值范围特别大的特征对距离计算造成影响;如K近邻、K-Means聚类
决策树和树的集成算法,可以不“无量纲化”,树模型也可以将数据处理好;
数据的无量纲化,可以时线性或非线性的。
线性包括中心化和缩放处理。
  • 中心化:数据平移,所有记录加减一个值
  • 缩放处理:所有数据乘除一个值,将数据固定在某个范围内。
归一化preprocessing.MinMaxScaler
  • scaler.inverse_transform(转换后的结果),可以返回归一化前的原始数据
  • 参数feature_range=[],可以指定归一化的范围
  • **partial_fit()**可以解决特征数量太多的问题
from sklearn.preprocessing import MinMaxScaler import pandas as pd data=https://www.it610.com/article/[[-1,2],[-0.5,6],[0,10],[1,18]] scaler=MinMaxScaler() scaler.fit(data) result=scaler.transform(data)'''array([[0., 0.], [0.25, 0.25], [0.5 , 0.5 ], [1., 1.]])'''result=scaler.fit_transform(data) '''array([[0., 0.], [0.25, 0.25], [0.5 , 0.5 ], [1., 1.]])'''scaler.inverse_transform(result) '''array([[-1. ,2. ], [-0.5,6. ], [ 0. , 10. ], [ 1. , 18. ]])'''scaler2=MinMaxScaler(feature_range=[5,10]) result2=scaler2.fit_transform(data) result2 ''' array([[ 5.,5.], [ 6.25,6.25], [ 7.5 ,7.5 ], [10., 10.]])'''#当x中的特征数量太多时,fit会报错,因为数据量太大了计算不了 #可以使用partial_fit作为训练接口 scaler3=MinMaxScaler() scaler3.partial_fit(data) result3=scaler3.transform(data) result3 ''' array([[0., 0.], [0.25, 0.25], [0.5 , 0.5 ], [1., 1.]])'''

标准化preprocessing.StandardScaler
  • scaler.inverse_transform(转换后的结果),可以返回归一化前的原始数据
  • .mean_属性可以查看原始数据的均值
  • .var_属性可以查看原始数据的方差
from sklearn.preprocessing import StandardScaler data=https://www.it610.com/article/[[-1,2],[-0.5,6],[0,10],[1,18]] scaler=StandardScaler() scaler.fit(data) scaler.mean_#查看原始数据均值 # array([-0.125,9.]) scaler.var_#查看原始数据方差 # array([ 0.546875, 35.])x_std=scaler.transform(data)''' array([[-1.18321596, -1.18321596], [-0.50709255, -0.50709255], [ 0.16903085,0.16903085], [ 1.52127766,1.52127766]])''' x_std.mean()#0.0 x_std.std()#1.0scaler.inverse_transform(x_std)#逆转 '''array([[-1. ,2. ], [-0.5,6. ], [ 0. , 10. ], [ 1. , 18. ]])'''

对于StandardScaler和MinMaxScaler来说,空值NaN会被当做是缺失值,在fit的时候忽略,在transform的时候保持缺失NaN的状态显示。
【sklearn机器学习|学习笔记——sklearn数据预处理和特征工程(过滤法、嵌入法、包装法)】在fit接口中,只允许导入至少二维数组,一维数组导入会报错。
如何选择?
大多数机器学习算法中,会选择StandardScaler来进行特征缩放
MinMaxScaler对异常值非常敏感。
在PCA,聚类,逻辑回归,支持向量机,神经网络这些算法中,StandardScaler往往是最好的选择。
MinMaxScaler在不涉及距离度量、梯度、协方差计算以及数据需要被压缩到特定区间时使用广泛,比如数字图像处理中量化像素强度时,都会使用MinMaxScaler将数据压缩于[0,1]区间之中。
建议先试试看StandardScaler,效果不好换MinMaxScaler。
缺失值处理 sklearn.impute.SimpleImputer()
参数 含义
missing_values 告诉SimpleImputer,数据中的缺失值长什么样,默认空值np.nan
strategy 我们填补缺失值的策略,默认均值。
输入“mean”使用均值填补(仅对数值型特征可用)
输入“median"用中值填补(仅对数值型特征可用)
输入"most_frequent”用众数填补(对数值型和字符型特征都可用)
输入“constant"表示请参考参数“fill_value"中的值(对数值型和字符型特征都可用)
fill_value 当参数startegy为”constant"的时候可用,可输入字符串或数字表示要填充的值,常用0
copy 默认为True,将创建特征矩阵的副本,反之则会将缺失值填补到原本的特征矩阵中去。
import pandas as pd data = https://www.it610.com/article/pd.read_csv("Narrativedata.csv",index_col=0) data.info() ''' ----------------填补之前--------------- Int64Index: 891 entries, 0 to 890 Data columns (total 4 columns): #ColumnNon-Null CountDtype ---------------------------- 0Age714 non-nullfloat64 1Sex891 non-nullobject 2Embarked889 non-nullobject 3Survived891 non-nullobject dtypes: float64(1), object(3) memory usage: 34.8+ KB '''#填补年龄 Age=data.loc[:,'Age'].values.reshape(-1,1) # Dataframe无法直接reshape # data.loc[:,'Age'].values的类型是numpy.ndarray from sklearn.impute import SimpleImputer imp_mean=SimpleImputer()#实例化,默认为均值填补 imp_median=SimpleImputer(strategy='median') imp_0=SimpleImputer(strategy='constant',fill_value=https://www.it610.com/article/0)imp_mean=imp_mean.fit_transform(Age) imp_median=imp_median.fit_transform(Age) imp_0=imp_0.fit_transform(Age)#这里我们用中位数填补Age data.loc[:,"Age"]=imp_median#使用众数填补Embarked Embarked = data.loc[:,"Embarked"].values.reshape(-1,1) imp_mode=SimpleImputer(strategy='most_frequent') data.loc[:,"Embarked"]=imp_mode.fit_transform(Embarked) data.info()''' ----------------填补之后--------------- Int64Index: 891 entries, 0 to 890 Data columns (total 4 columns): #ColumnNon-Null CountDtype ---------------------------- 0Age891 non-nullfloat64 1Sex891 non-nullobject 2Embarked891 non-nullobject 3Survived891 non-nullobject dtypes: float64(1), object(3) memory usage: 34.8+ KB'''

直接处理
data.loc[:,"Age"]=data.loc[:,"Age"].fillna(data.loc[:,"Age"].median()) #EM=data.loc[:,"Embarked"].fillna(data.loc[:,"Embarked"].mode()) data.dropna(axis=0,inplace=True)#axis等于0,删除行;=1,删除列 data.info() ''' ----------填充之后------------- Int64Index: 889 entries, 0 to 890 Data columns (total 4 columns): #ColumnNon-Null CountDtype ---------------------------- 0Age889 non-nullfloat64 1Sex889 non-nullobject 2Embarked889 non-nullobject 3Survived889 non-nullobject dtypes: float64(1), object(3) memory usage: 34.7+ KB'''

处理分类型特征 preprocessing.LabelEncoder
标签专用,能够将分类转换为分类数值
  • .**classes_**属性查看标签中有多少类别
  • .inverse_transform可以逆转
  • 允许一维数据
from sklearn.preprocessing import LabelEncoder data.iloc[:,-1]=LabelEncoder().fit_transform(data.iloc[:,-1]) data.head() ''' Age Sex Embarked Survived 0 22.0 male S 0 1 38.0 female C 2 2 26.0 female S 2 3 35.0 female S 2 4 35.0 male S 0'''

preprocessing.OrdinalEncoder
特征专用,与LabelEncoder用法类似
  • 输入数据是二维的
  • fit之后的属性**categories_**查看特征中的类别
from sklearn.preprocessing import OrdinalEncoder OrdinalEncoder().fit(data.iloc[:,1:-1]).categories_ #[array(['female', 'male'], dtype=object), array(['C', 'Q', 'S'], dtype=object)]

preprocessing.OneHotEncoder
  • fit之后的属性**get_feature_names()**返回每一列的名字
  • .inverse_transform可以逆转
from sklearn.preprocessing import OneHotEncoder X=data.iloc[:,1:-1] enc=OneHotEncoder(categories='auto').fit(X) # pd.DataFrame(enc.inverse_transform(result)) #逆转,返回原始数据 enc.get_feature_names()#返回每一列的名字 # array(['x0_female', 'x0_male', 'x1_C', 'x1_Q', 'x1_S'], dtype=object)result=OneHotEncoder(categories='auto').fit_transform(X).toarray() #要转成数组,.fit_transform(X)之后得到的是一个稀疏矩阵对象

axis=1
  • 删除列
  • 合并列
  • 队列求均值
  • concat把不同列合在一起
处理连续性特征 二值化sklearn.preprocessing.Binarize
将特征值化成0和1
from sklearn.preprocessing import Binarizer X = data.iloc[:,0].values.reshape(-1,1) #类为特征专用,所以不能使用一维数组 transformer = Binarizer(threshold=30).fit_transform(X) transformer

分箱
参数 含义&输入
n_bins 每个特征中分箱的个数,默认5,一次会被运用到所有导入的特征
encode 编码的方式,默认“onehot”
“onehot”:做哑变量,之后返回一个稀疏矩阵,每一列是一个特征中的一个类别,含有该类别的样本表示为1,不含的表示为0
“ordinal”:每个特征的每个箱都被编码为一个整数,返回每一列是一个特征.类似OrdinalEncoder每个特征下含有不同整数编码的箱的矩阵"onehot-dense":做哑变量,之后返回一个密集数组。
strategy 用来定义箱宽的方式,默认"quantile"
“uniform”:表示等宽分箱,即每个特征中的每个箱的最大值之间的差为(特征.max() -特征.min())/(n_bins)
“quantile”:表示等位分箱,即每个特征中的每个箱内的样本数量都相同
“kmeans”:表示按聚类分箱,每个箱中的值到最近的一维k均值聚类的簇心得距离都相同
from sklearn.preprocessing import KBinsDiscretizer X = 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())#{0.0, 1.0, 2.0} est = KBinsDiscretizer(n_bins=3, encode='onehot',strategy='uniform') #查看转换后分的箱:变成了哑变量 est.fit_transform(X).toarray() '''array([[1., 0., 0.], [0., 1., 0.], [1., 0., 0.], ..., [0., 1., 0.], [1., 0., 0.], [0., 1., 0.]])'''

也可以用pandas中的qcut\cut
2特征工程 2.1过滤法 通常用于预处理,过滤法是独立于机器学习算法的,依据各种统计检验中的分数以及相关性的各种指标来选择特征。
2.1.1方差过滤
即通过特征本身的方差来选择特征,方差特别小的特征对于样本区分没有什么作用。
sklearn.feature_selection.VarianceThreshold
  • 重要参数:threshold:方差的阈值,舍弃所有方差小于threshold的特征,不填默认为0
  • 一般都要消除方差为0的特征
#导入数据 import pandas as pd data=https://www.it610.com/article/pd.read_csv('digit recognizor.csv') X=data.iloc[:,1:] y=data.iloc[:,0] X.shape#(42000, 784) #本质是维度太高 #如果用支持向量机和神经网络,可能直接跑不出来,(这两个特征本质是升维)#阈值为0 from sklearn.feature_selection import VarianceThreshold selector=VarianceThreshold()#默认为零,删除方差为0的特征 X_var0=selector.fit_transform(X) X_var0.shape#(42000, 708)#阈值设为所有方差中位数 import numpy as np # X.var()#查看方差的中位数,得到的是pandas.core.series.Series类型 # np.median(X.var().values)所有特征方差的中值1352.286703180131 X_fsvar=VarianceThreshold(np.median(X.var().values)).fit_transform(X) X_fsvar.shape#(42000, 392)#当特征式二分类问题时,方差=p(1-p); 若特征是伯努利随机变量,假设p=0.8,即二分类特种中某种分类占到80%以上时删除特征 X_vbar=VarianceThreshold(0.8*0.2).fit_transform(X)#删除特征中,某一分类占到0.8以上的 X_vbar.shape#(42000, 685)

方差过滤前后模型效果对比
from sklearn.ensemble import RandomForestClassifier from sklearn.neighbors import KNeighborsClassifier from sklearn.model_selection import cross_val_score X=data.iloc[:,1:] y=data.iloc[:,0] X_fsvar=VarianceThreshold(np.median(X.var().values)).fit_transform(X)#默 #KNN_方差过滤前 cross_val_score(KNeighborsClassifier(),X,y,cv=5).mean() # 0.965857142857143 #KNN_方差过滤后 cross_val_score(KNeighborsClassifier(),X_fsvar,y,cv=5).mean() #0.966#随机森林-方差过滤前 cross_val_score(RandomForestClassifier(n_estimators=100,random_state=0),X,y,cv=5).mean() #0.9642142857142856 #随机森林-方差过滤后 cross_val_score(RandomForestClassifier(n_estimators=100,random_state=0),X_fsvar,y,cv=5).mean() #0.9637142857142857

  • 方差过滤后,删除了一半的特征(392),KNN效果提高了0.0002,随机森林效果降低了0.0005;
  • 对于KNN、单棵决策树、SVM、神经网络、回归算法,需要遍历特征或升维进行计算。本身的计算量很大,方差过滤这样的特征选择的效果比较好
  • 对于随机森林,它随机选取特征进行分枝,本身速度较快(n_estimators不高的情况),方差过滤对其影响不大。
  • 虽然随机森林和单棵决策树都是随机选择特征,但是决策树选的特征要比随机森林中每棵树随机选的特征多得多,所以特征数量对决策树运行快慢也有一定影响。
  • 如果过滤后,结果变好了,说明对于该模型,方差过滤舍弃的特征是噪音;否之,舍弃的特征中含有有效特征。
  • 过滤法的主要对象是:需要遍历特征或升维的算法
  • 过滤法的主要目的是:在维持算法表现的前提下,降低计算成本。
  • 一般过滤法用于预处理,我们会先用阈值为0或很小的的方差过滤,再用更优的特征选择模仿继续削减特征数量。
2.1.2相关性过滤
卡方过滤
  • 针对分类问题的(标签是离散的)
  • 要求特征是非负的
  • feature_selection.chi2+feature_selection.SelectKBest:chi2是评分标准,用来选取前k个分数最高的特征。
这里我们用轻量级(n_estimators=10)的随机森林来实践。
#随机森林-方差过滤前 cross_val_score(RandomForestClassifier(c,random_state=0),X,y,cv=5).mean() #0.9373571428571429#随机森林-方差过滤后 cross_val_score(RandomForestClassifier(n_estimators=10,random_state=0),X_fsvar,y,cv=5).mean() #0.9390476190476191#由于方差过滤后的效果变好了,就直接用方差过滤后的数据 from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import cross_val_score from sklearn.feature_selection import SelectKBest,chi2x_fschi=SelectKBest(chi2,k=300).fit_transform(X_fsvar,y) x_fschi.shape#(42000, 300) cross_val_score(RandomForestClassifier(n_estimators=10,random_state=0),X_fsvar,y,cv=5).mean()#0.9390476190476191

这里的k要根据实际情况来做调整。k值可以通过学习曲线或者p值来确定
学习曲线
  • 学习曲线花费计算量较大
import matplotlib.pyplot as pltscore=[] for i in range(350,200,-10): x_fschi =SelectKBest(chi2,k=i).fit_transform(X_fsvar,y) once=cross_val_score(RandomForestClassifier(n_estimators=10,random_state=0),x_fschi,y,cv=5).mean() score.append(once) plt.plot(range(350,200,-10),score) plt.show()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iR6ygd7S-1660645683500)(C:\Users\章ky\AppData\Roaming\Typora\typora-user-images\image-20220814142027328.png)]
p值
卡方检验的本质是推测两组数据之间的差异,原假设是“两组数据是相互独立的”。
  • p值<=0.05或0.01:拒绝原假设,两组数据是相关的
  • p值<=0.05或0.01:接受原假设,两组数据不相关。
#看p值 chivalue,pvalue_chi=chi2(X_fsvar,y) chivalue#返回所有特征对标签的卡方值 k=chivalue.shape[0]-(pvalue_chi>0.05).sum()#392,与标签相关的特征数量 #也就是(pvalue_chi<=0.05).sum()

F检验
  • 又称方差齐性检验,捕捉每个特征与标签之间的线性关系
  • 可以做回归也可以做分类
  • SelectKBest+feature_selection.f_classif/feature_selection.f_regression
  • F检验在数据服从正态分布时会非常稳定,最好先做预处理
F检验的本质是寻找两组数据之间的线性关系,原假设是“数据不存在线性的线性关系”。
  • p值<=0.05或0.01:拒绝原假设,两组数据显著线性相关
  • p值<=0.05或0.01:接受原假设,两组数据没有显著线性相关。
from sklearn.feature_selection import f_classif F,pvalues_f=f_classif(X_fsvar,y) k=(pvalues_f<=0.5).sum() k#392

互信息法
  • 用于捕捉特征与标签之间的任何关系,包括线性和非线性
  • feature_selection.mutual_info_classif/feature_selection.mutual_info_regression
  • 不返回统计量,只返回“每个特征与目标之间的互信息量的估计”,这个估计在[0,1]之间。0表示相互独立,1表示完全相关
  • 不能用于稀疏矩阵
from sklearn.feature_selection import mutual_info_classif as MIC result=MIC(X_fsvar,y) k=(result>=0).sum() k#392

2.2嵌入法SelectFromModel
  • 特征选择与算法训练同时进行
  • 每次从全部特征中选择特征子集进行训练和模型评估
  • 得到各个特征的权值系数
  • 优点:结果会更加精确到模型的效用本身,对提高模型效用有更好的效果;无关的特征和无区分度的特征也为被过滤掉,是过滤法的进化版
  • 缺点:①嵌入法的权值系数无法人为界定一个有效值,需要借助学习曲线或对模型的深入理解。②计算量很大,计算缓慢的算法时非常耗时
class sklearn.feature_selection.SelectFromModel (estimator, threshold=None, prefifit=False, norm_order=1,max_features=None)
SelectFromModel是一个元变换器,可以与任何在拟合后具有coef_,feature_importances_属性或参数中可选惩罚项的评估器一起使用(比如随机森林和树模型就具有属性feature_importances_,逻辑回归就带有l1和l2惩罚项,线性支持向量机也支持l2惩罚项)。
参数 解释
estimator 使用的模型评估器,只要是带feature_importances_或者coef_属性,或带有l1和l2惩罚项的模型都可以使用
threshold 特征重要性的阈值,重要性低于这个阈值的特征都将被删除
prefit 默认False,判断是否将实例化后的模型直接传递给构造函数。如果为True,则必须直接调用fit和transform,不能使用fit_transform,并且SelectFromModel不能与cross_val_score,GridSearchCV和克隆估计器的类似实用程序一起使用。
norm_order k可输入非零整数,正无穷,负无穷,默认值为1 在评估器的coef_属性高于一维的情况下,用于过滤低于阈值的系数的向量的范数的阶数。
max_features 在阈值设定下,要选择的最大特征数。要禁用阈值并仅根据max_features选择,请设置threshold = -np.inf
from sklearn.feature_selection import SelectFromModel from sklearn.ensemble import RandomForestClassifier as RFC RFC_ = RFC(n_estimators =10,random_state=0) X_embedded = SelectFromModel(RFC_,threshold=0.00067).fit_transform(X,y) #具体K值,可以通过学习曲线来查找 X_embedded.shape#(42000, 324) cross_val_score(RFC_,X_embedded,y,cv=5).mean()#0.9391190476190475

2.3包装法RFE 包装法与嵌入法类似,不同的是
  • 包装法使用目标函数来选取特征,典型的目标函数是递归特征消除法RFE
  • 包装法的特征是在上一组特征修剪之后留下来的特征中选取的,以递归的方式直到最终达到所需数量的要选择的特征
  • 计算成本低于嵌入法
  • 包装法的效果是所有特征选择方法中最有利于提升模型表现的,可以利用很少的特征达到优秀的效果。
class sklearn.feature_selection.RFE (estimator, n_features_to_select=None, step=1, verbose=0)
参数estimator是需要填写的实例化后的评估器,n_features_to_select是想要选择的特征个数,step表示每次迭代中希望移除的特征个数。
RFE类有两个很重要的属性
.support_:返回所有的特征的是否最后被选中的布尔矩阵
.ranking_:返回特征的按数次迭代中综合重要性的排名。
类feature_selection.RFECV会在交叉验证循环中执行RFE以找到最佳数量的特征,增加参数cv,其他用法都和RFE一模一样。
from sklearn.feature_selection import RFE RFC_=RFC(n_estimators =10,random_state=0) selector=RFE(RFC_,n_features_to_select=340,step=50).fit(X,y) X_wrapper=selector.transform(X) cross_val_score(RFC_,X_wrapper,y,cv=5).mean()#0.9379761904761905 #selector.support_#返回所有的特征的是否最后被选中的布尔矩阵 #selector.ranking_#返回特征的按数次迭代中综合重要性的排名。

RFE类有两个很重要的属性
.support_:返回所有的特征的是否最后被选中的布尔矩阵
.ranking_:返回特征的按数次迭代中综合重要性的排名。
类feature_selection.RFECV会在交叉验证循环中执行RFE以找到最佳数量的特征,增加参数cv,其他用法都和RFE一模一样。
from sklearn.feature_selection import RFE RFC_=RFC(n_estimators =10,random_state=0) selector=RFE(RFC_,n_features_to_select=340,step=50).fit(X,y) X_wrapper=selector.transform(X) cross_val_score(RFC_,X_wrapper,y,cv=5).mean()#0.9379761904761905 #selector.support_#返回所有的特征的是否最后被选中的布尔矩阵 #selector.ranking_#返回特征的按数次迭代中综合重要性的排名。

2.4总结
  • 当数据量很大的时候,优先使用方差过滤和互信息法调整,再上其他特征选择方法。
  • 使用逻辑回归时,优先使用嵌入法。
  • 使用支持向量机时,优先使用包装法。
  • 迷茫的时候,从过滤法走起,具体数据具体分析
注:本文内容是本人在学习“菜菜的机器学习课程”网课过程中的记录、总结和整理,将重点部分整理出来的笔记。侵删。

    推荐阅读