学习笔记——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=[[-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=[[-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.0

scaler.inverse_transform(x_std)#逆转
'''array([[-1. ,  2. ],
       [-0.5,  6. ],
       [ 0. , 10. ],
       [ 1. , 18. ]])'''

对于StandardScaler和MinMaxScaler来说,空值NaN会被当做是缺失值,在fit的时候忽略,在transform的时候保持缺失NaN的状态显示。

在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 = pd.read_csv("Narrativedata.csv",index_col=0)
data.info()
'''
----------------填补之前---------------

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)
# 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=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):
 #   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'''

直接处理

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):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   Age       889 non-null    float64
 1   Sex       889 non-null    object 
 2   Embarked  889 non-null    object 
 3   Survived  889 non-null    object 
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=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,chi2

x_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 plt

score=[]
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总结

  • 当数据量很大的时候,优先使用方差过滤和互信息法调整,再上其他特征选择方法。
  • 使用逻辑回归时,优先使用嵌入法。
  • 使用支持向量机时,优先使用包装法。
  • 迷茫的时候,从过滤法走起,具体数据具体分析

注:本文内容是本人在学习“菜菜的机器学习课程”网课过程中的记录、总结和整理,将重点部分整理出来的笔记。侵删。

你可能感兴趣的:(sklearn机器学习,学习,sklearn,机器学习)