sklearn数据预处理和特征工程 数据挖掘五大流程
- 获取数据
- 数据预处理
- 目的:让数据适应模型,匹配模型的需求
- 特征工程
- 面对的问题:特征之间有相关性、特征和标签无关、特征太多或太少或无法展示数据的真实面貌。
- 目的:降低计算成本;提升模型上限;
- 建模,测试模型并预测出结果
- 上线,验证模型效果
**模块lmpute:**填补缺失值专用
模块feature_ selection: 包含特征选择的各种方法的实践
**模块decomposition:**包含降维算法
数据无量纲化: 将不同规格的数据转换为同一规则,或不同分布的数据转换到某个特定分布的需求。
以梯度和矩阵为核心的算法中,无量纲化可以加快求解速度;如逻辑回归、支持向量机、神经网络数据的无量纲化,可以时线性或非线性的。
**以距离为核心的模型,**无量纲化可以帮助我们提升模型精度,避免某一个取值范围特别大的特征对距离计算造成影响;如K近邻、K-Means聚类
决策树和树的集成算法,可以不“无量纲化”,树模型也可以将数据处理好;
线性包括中心化和缩放处理。
- 中心化:数据平移,所有记录加减一个值
- 缩放处理:所有数据乘除一个值,将数据固定在某个范围内。
- 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把不同列合在一起
将特征值化成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或很小的的方差过滤,再用更优的特征选择模仿继续削减特征数量。
卡方过滤
- 针对分类问题的(标签是离散的)
- 要求特征是非负的
- feature_selection.chi2+feature_selection.SelectKBest:chi2是评分标准,用来选取前k个分数最高的特征。
#随机森林-方差过滤前
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总结
- 当数据量很大的时候,优先使用方差过滤和互信息法调整,再上其他特征选择方法。
- 使用逻辑回归时,优先使用嵌入法。
- 使用支持向量机时,优先使用包装法。
- 迷茫的时候,从过滤法走起,具体数据具体分析
注:本文内容是本人在学习“菜菜的机器学习课程”网课过程中的记录、总结和整理,将重点部分整理出来的笔记。侵删。
推荐阅读
- sklearn|【数据预处理】sklearn实现数据预处理(归一化、标准化)
- sklearn|Sklearn中数据预处理
- pandas|数据挖掘代码实例学习——Pandas、sklearn数据预处理(包含pandas库以及所需依赖包安装教程)
- NLP|深度学习之文本分类 ----FastText
- 深度学习原理及实战|手把手教你深度学习和实战-----逻辑回归算法
- 人工智能|Performer(用随机投影将Attention的复杂度线性化)
- SfM|猿创征文|SfM(Structure from Motion)学习之路
- #|AI-无损检测方向速读(基于深度学习的表面缺陷检测方法综述)
- 算法|使用U-Nets和深度学习进行自动缺陷检查