参考并推荐:菜菜的sklearn课堂: http://edu.cda.cn/course/982
目录
1.归一化:preprocessing.MinMaxScaler
2.标准化 preprocessing.StandardScaler
5.1 preprocessing.LabelEncoder
5.2 preprocessing.OrdinalEncoder
5.3 preprocessing.OneHotEncoder
6.1 sklearn.preprocessing.Binarizer
6.2 preprocessing.KBinsDiscretizer
1.归一化:preprocessing.MinMaxScaler
数据归一化(Normalization):将数据缩放到0-1之间。服从正态分布。计算公式如下
缺点:对异常值敏感
1.1实战部分:
from sklearn.preprocessing import MinMaxScaler
import pandas as pd
pd.DataFrame(data)
#待归一化数据
data = [[-1, 3], [-0.5, 4], [0, 10], [1, 18]]
#实例化
scaler = MinMaxScaler()
######################################两步法
#先拟合训练fit,本质是生成min(x)和max(x)
scaler = scaler.fit(data)
#然后通过接口导出结果
result = scaler.transform(data)
#####################################一步法
#不用先fit再transform,而是一步完成结果导出
result_ = scaler.fit_transform(data)
#inverse_transform()结果逆转回去
scaler.inverse_transform(result) #将归一化后的结果逆转
#参数feature_range实现将数据归一化到[0,1]以外的范围中,自己指定范围
data = [[-1, 2], [-0.5, 6], [0, 10], [1, 18]]
#实例化,并设置参数指定范围是5~10之间
scaler = MinMaxScaler(feature_range=[5,10])
#fit_transform一步导出结果
result = scaler.fit_transform(data)
#当X中的特征数量非常多的时候,fit会报错并表示,数据量太大了计算不了
#此时使用partial_fit作为训练接口
#scaler = scaler.partial_fit(data)
1.2实战部分:numpy实现归一化
import numpy as np
X = np.array([[-1, 2], [-0.5, 6], [0, 10], [1, 18]])
#归一化
X_nor = (X - X.min(axis=0)) / (X.max(axis=0) - X.min(axis=0))
#逆转归一化
X_returned = X_nor * (X.max(axis=0) - X.min(axis=0)) + X.min(axis=0)
2.标准化 preprocessing.StandardScaler
数据标准化(Standardization):数据服从均值为0,方差为1的正态分布。计算公式如下:
2.1实战部分
from sklearn.preprocessing import StandardScaler
data = [[-1, 2], [-0.5, 6], [0, 10], [1, 18]]
#实例化标准化对象
scaler = StandardScaler()
#fit,本质是生成均值和方差
scaler.fit(data)
#查看均值的属性mean_
scaler.mean_
#查看方差的属性var_
scaler.var_
#通过接口导出结果
x_std = scaler.transform(data)
#导出的结果是一个数组,用mean()查看均值,用std()查看方差
x_std.mean()
x_std.std()
#使用fit_transform(data)一步达成结果
scaler.fit_transform(data)
#使用inverse_transform逆转标准化
scaler.inverse_transform(x_std)
3.汇总
4.缺失值处理
class sklearn.impute.SimpleImputer (
missing_values=nan, #告诉simpleimputer,数据中的缺失值是什么,默认是np.nan
strategy=’mean’, #填补策略,mean、median、most_frequent(众数)、constant
fill_value=None, #当参数startegy为”constant"的时可用,输入要填充的数字或字符串值,常用0
verbose=0,
copy=True) #创建副本,否则缺失值填补到原特征矩阵中
4.1实战部分
import pandas as pd
data = pd.read_csv("Narrativedata.csv",index_col=0)
data.info() #查看信息,可看到数据情况
Age = data.loc[:,"Age"].values.reshape(-1,1) #sklearn当中特征矩阵必须是二维
Age[:20]
from sklearn.impute import SimpleImputer
imp_mean = SimpleImputer() #实例化,默认均值填补
imp_median = SimpleImputer(strategy="median") #用中位数填补
imp_0 = SimpleImputer(strategy="constant",fill_value=0) #用0填补
imp_mean = imp_mean.fit_transform(Age) #fit_transform一步完成调取结果
imp_median = imp_median.fit_transform(Age)
imp_0 = imp_0.fit_transform(Age)
imp_mean[:20]
imp_median[:20]
imp_0[:20]
#在这里我们使用中位数填补Age
data.loc[:,"Age"] = imp_median
data.info()
#使用众数填补Embarked
Embarked = data.loc[:,"Embarked"].values.reshape(-1,1)
#实例化
imp_mode = SimpleImputer(strategy = "most_frequent")
data.loc[:,"Embarked"] = imp_mode.fit_transform(Embarked)
4.2实战部分:pandas
import pandas as pd
data = pd.read_csv("Narrativedata.csv",index_col=0)
data.head()
#.fillna 在DataFrame里面直接进行填补
data.loc[:,"Age"] = data.loc[:,"Age"].fillna(data.loc[:,"Age"].median())
#.dropna(axis=0)删除所有有缺失值的行,.dropna(axis=1)删除所有有缺失值的列
#参数inplace,为True表示在原数据集上进行修改,为False表示生成一个复制对象,不修改原数据,默认False
data.dropna(axis=0,inplace=True)
5.分类型特征处理
在机器学习中,大多数算法,如逻辑回归,支持向量机SVM,k近邻算法等都只能够处理数值型数据,不能处理文字,在sklearn当中,除了专用来处理文字的算法,其他算法在fit的时候全部要求输入数组或矩阵,也不能够导入文字型数据(其实自己写的决策树和普斯贝叶斯可以处理文字型数据,因为只是求概率而已,但是sklearn中规定必须导入数值型)
5.1 preprocessing.LabelEncoder
标签专用,能够将分类型转换为相应的分类数值 如 ['女人',‘男人’,‘人妖’]----》 0 女人,1 男人 ,2人妖
from sklearn.preprocessing import LabelEncoder
y = data.iloc[:,-1] #要输入的是标签,不是特征矩阵,所以允许一维
le = LabelEncoder() #实例化
le = le.fit(y) #导入数据
le.classes_ #属性classes_查看标签中究竟有多少类别
label = le.transform(y) #transform接口调取结果
le.fit_transform(y) #也可以直接fit_transform一步到位
le.inverse_transform(label) #使用inverse_transform可以逆转
data.iloc[:,-1] = label #让标签等于我们运行出来的结果
#两行搞定效果一样
#from sklearn.preprocessing import LabelEncoder
#data.iloc[:,-1] = LabelEncoder().fit_transform(data.iloc[:,-1])
5.2 preprocessing.OrdinalEncoder
特征专用,将分类特征转换为分类数值 (适合处理有序变量,变量之间不完全独立,如 小学-初中-高中,但是却又不可以数值计算)
from sklearn.preprocessing import OrdinalEncoder
data_ = data.copy()
data_.head()
#接口categories_对应LabelEncoder的接口classes_,一模一样的功能
OrdinalEncoder().fit(data_.iloc[:,1:-1]).categories_
data_.iloc[:,1:-1] = OrdinalEncoder().fit_transform(data_.iloc[:,1:-1])
data_.head()
5.3 preprocessing.OneHotEncoder
独热编码(适合处理名义变量,变量之间完全独立,没有可计算性,有你就没有我这类的特征数据,如 性别 :男、女)
from sklearn.preprocessing import OneHotEncoder
X = data.iloc[:,1:-1]
enc = OneHotEncoder(categories='auto').fit(X)
result = enc.transform(X).toarray()
enc.get_feature_names()
result.shape
#依然可以直接一步到位,
OneHotEncoder(categories='auto').fit_transform(X).toarray()
#还原
pd.DataFrame(enc.inverse_transform(result))
#编码好了特征
#axis=1,表示跨行进行合并,也就是将表左右相连,如果是axis=0,就是将量表上下相连
newdata = pd.concat([data,pd.DataFrame(result)],axis=1)
#删除特征
newdata.drop(["Sex","Embarked"],axis=1,inplace=True)
#给特征重新命名
newdata.columns =["Age","Survived","Female","Male","Embarked_C","Embarked_Q","Embarked_S"]
newdata.head()
可以,使用类sklearn.preprocessing.LabelBinarizer对标签做哑变量,许多算法都可以处理多标签问题(比如说决策树),但是这样的做法在现实中不常见
5.4 编码汇总
6.连续型特征处理
6.1 sklearn.preprocessing.Binarizer
二值化:
根据阈值将数据二值化(将特征值设置为0或1),用于处理连续型变量。大于阈值的值映射为1,而小于或等于阈值的值映射为0。
#将年龄二值化
data_2 = data.copy()
from sklearn.preprocessing import Binarizer
X = data_2.iloc[:,0].values.reshape(-1,1) #reshape的原因 类为特征专用,所以不能使用一维数组
transformer = Binarizer(threshold=30).fit_transform(X)
6.2 preprocessing.KBinsDiscretizer
这是将连续型变量划分为分类变量的类,能够将连续型变量排序后按顺序分箱后编码
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())
est = KBinsDiscretizer(n_bins=3, encode='onehot', strategy='uniform')
#查看转换后分的箱:变成了哑变量
est.fit_transform(X).toarray()
7. 特征选择
7.1 VarianceThreshold(方差过滤)
VarianceThreshold有重要参数threshold,表示方差的阈值,表示舍弃所有方差小于threshold的特征,不填默认为0,即删除所有的记录都相同的特征。
过滤法的主要对象是:需要遍历特征或升维的算法们,而过滤法的主要目的是:在维持算法表现的前提下,帮助算法们降低计算成本,如KNN。随机森林的话过滤法基本没有多大的提升效果。
X =
from sklearn.feature_selection import VarianceThreshold
selector = VarianceThreshold() #实例化,不填参数默认方差为0
X_var0 = selector.fit_transform(X) #获取删除不合格特征之后的新特征矩阵
#也可以直接写成 X = VairanceThreshold().fit_transform(X)
X_var0.shape
#可以设置方差的中位数最为阈值
import numpy as np
X_fsvar = VarianceThreshold(np.median(X.var().values)).fit_transform(X)
X.var().values
np.median(X.var().values)
#KNN vs 随机森林在不同方差过滤效果下的对比
from sklearn.ensemble import RandomForestClassifier as RFC
from sklearn.neighbors import KNeighborsClassifier as KNN
from sklearn.model_selection import cross_val_score
import numpy as np
X = data.iloc[:,1:]
y = data.iloc[:,0]
#获取过滤后的数据,过滤阈值是特征方差的中位数
X_fsvar = VarianceThreshold(np.median(X.var().values)).fit_transform(X)
#KNN,使用不过滤和使用过滤对比
cross_val_score(KNN(),X,y,cv=5).mean()
cross_val_score(KNN(),X_fsvar,y,cv=5).mean()
#随机森林
cross_val_score(RFC(n_estimators=10,random_state=0),X,y,cv=5).mean()
cross_val_score(RFC(n_estimators=10,random_state=0),X_fsvar,y,cv=5).mean()
运行结果:对于KNN的运行效率有大的提升,对于随机森林没有明显帮助。
扩展过滤对决策树和随机森林的区别:传统决策树需要遍历所有特征,计算不纯度后进行分枝,而随机森林却是随机选择特征进行计算和分枝,因此随机森林的运算更快,过滤法对随机森林无用,对决策树却有用。
7.2 相关性过滤
7.2.1 卡方过滤
卡方过滤是专门针对离散型标签(即分类问题)的相关性过滤。
卡方检验类feature_selection.chi2计算每个非负特征和标签之间的卡方统计量,并依照卡方统计量由高到低为特征排名,再结合feature_selection.SelectKBest这个可以输入”评分标准“来选出前K个分数最高的特征的类,我们可以借此除去最可能独立于标签,与我们分类目的无关的特征。
from sklearn.ensemble import RandomForestClassifier as RFC
from sklearn.model_selection import cross_val_score
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
#假设在这里需要300个特征
X_fschi = SelectKBest(chi2, k=300).fit_transform(X_fsvar, y)
X_fschi.shape
#验证模型
cross_val_score(RFC(n_estimators=10,random_state=0),X_fschi,y,cv=5).mean()
#如果模型的表现提升,则说明我们的相关性过滤是有效的,否则K太小了,应该调整K或者放弃相关性过滤
"""
#学习曲线看效果,设置K
%matplotlib inline
import matplotlib.pyplot as plt
score = []
for i in range(390,200,-10):
X_fschi = SelectKBest(chi2, k=i).fit_transform(X_fsvar, y)
once = cross_val_score(RFC(n_estimators=10,random_state=0),X_fschi,y,cv=5).mean()
score.append(once)
plt.plot(range(350,200,-10),score)
plt.show()
"""
7.2.2 F检验
F检验,又称ANOVA,方差齐性检验,是用来捕捉每个特征与标签之间的线性关系的过滤方法。它即可以做回归也可以做分类,因此包含feature_selection.f_classif(F检验分类)和feature_selection.f_regression(F检验回归)两个类。其中F检验分类用于标签是离散型变量的数据,而F检验回归用于标签是连续型变量的数据。
和卡方检验一样,这两个类需要和类SelectKBest连用,并且我们也可以直接通过输出的统计量来判断我们到底要设置一个什么样的K。需要注意的是,F检验在数据服从正态分布时效果会非常稳定,因此如果使用F检验过滤,我们会先将数据转换成服从正态分布的方式。
F检验的本质是寻找两组数据之间的线性关系,其原假设是”数据不存在显著的线性关系“。它返回F值和p值两个统计量。
from sklearn.feature_selection import f_classif
F, pvalues_f = f_classif(X_fsvar,y)
F
pvalues_f
k = F.shape[0] - (pvalues_f > 0.05).sum()
#X_fsF = SelectKBest(f_classif, k=填写具体的k).fit_transform(X_fsvar, y)
#cross_val_score(RFC(n_estimators=10,random_state=0),X_fsF,y,cv=5).mean()
7.2.3 互信息法
互信息法是用来捕捉每个特征与标签之间的任意关系(包括线性和非线性关系)的过滤方法。和F检验相似,它既可以做回归也可以做分类,并且包含两个类feature_selection.mutual_info_classif(互信息分类)feature_selection.mutual_info_regression(互信息回归)。这两个类的用法和参数都和F检验一模一样,不过互信息法比F检验更加强大,F检验只能够找出线性关系,而互信息法可以找出任意关系。
互信息法不返回p值或F值类似的统计量,它返回“每个特征与目标之间的互信息量的估计”,这个估计量在[0,1]之间
取值,为0则表示两个变量独立,为1则表示两个变量完全相关。
from sklearn.feature_selection import mutual_info_classif as MIC
result = MIC(X_fsvar,y)
k = result.shape[0] - sum(result <= 0)
#X_fsmic = SelectKBest(MIC, k=填写具体的k).fit_transform(X_fsvar, y)
#cross_val_score(RFC(n_estimators=10,random_state=0),X_fsmic,y,cv=5).mean()
7.2.4 总结
通常来说,我会建议,先使用方差过滤,然后使用互信息法来捕捉相关性,不过了解各种各样的过滤方式也是必要的。所有信息被总结在下表
8. 嵌入法Embedded
嵌入法是一种让算法自己决定使用哪些特征的方法,即特征选择和算法训练同时进行。
class sklearn.feature_selection.SelectFromModel (
estimator, #使用的模型评估器,只要是带feature_importances_或者coef_属性,或带有l1和l2惩罚
#项的模型都可以使用
threshold=None, #特征重要性的阈值,重要性低于这个阈值的特征都将被删除
prefit=False, #默认False,判断是否将实例化后的模型直接传递给构造函数。如果为True,则必须直接
#调用fit和transform,不能使用fit_transform,并且SelectFromModel不能与
#cross_val_score,GridSearchCV和克隆估计器的类似实用程序一起使用。
norm_order=1,
max_features=None)
SelectFromModel是一个元变换器,可以与任何在拟合后具有coef_,feature_importances_属性或参数中可选惩罚项的评估器一起使用(比如随机森林和树模型就具有属性feature_importances_,逻辑回归就带有l1和l2惩罚项,线性支持向量机也支持l2惩罚项)
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.005).fit_transform(X,y)
#在这里我只想取出来有限的特征。0.005这个阈值对于有780个特征的数据来说,是非常高的阈值,因为平均每个#特征只能够分到大约0.001的feature_importances_
X_embedded.shape
#模型的维度明显被降低了
#同样的,我们也可以画学习曲线来找最佳阈值
import numpy as np
import matplotlib.pyplot as plt
RFC_.fit(X,y).feature_importances_
threshold = np.linspace(0,(RFC_.fit(X,y).feature_importances_).max(),20)
score = []
for i in threshold:
X_embedded = SelectFromModel(RFC_,threshold=i).fit_transform(X,y)
once = cross_val_score(RFC_,X_embedded,y,cv=5).mean()
score.append(once)
plt.plot(threshold,score)
plt.show()
#随着阈值越来越高,模型的效果逐渐变差,被删除的特征越来越多,信息损失也逐渐变大。
#验证
X_embedded = SelectFromModel(RFC_,threshold=0.00067).fit_transform(X,y)
X_embedded.shape
cross_val_score(RFC_,X_embedded,y,cv=5).mean()
9 包装法Wrapper
包装法也是一个特征选择和算法训练同时进行的方法,与嵌入法十分相似,它也是依赖于算法自身的选择,比如coef_属性或feature_importances_属性来完成特征选择。但不同的是,我们往往使用一个目标函数作为黑盒来帮助我们选取特征,而不是自己输入某个评估指标或统计量的阈值
class sklearn.feature_selection.RFE (
estimator, #参数estimator是需要填写的实例化后的评估器
n_features_to_select=None, #想要选择的特征个数
step=1, #每次迭代中希望移除的特征个数
verbose=0)
除此之外,RFE类有两个很重要的属性,.support_:
返回所有的特征的是否最后被选中的布尔矩阵,
以及.ranking_返回特征的按数次迭代中综合重要性的排名
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)
selector.support_.sum()
selector.ranking_
X_wrapper = selector.transform(X)
cross_val_score(RFC_,X_wrapper,y,cv=5).mean()
#包装法学习曲线
score = []
for i in range(1,751,50):
X_wrapper = RFE(RFC_,n_features_to_select=i, step=50).fit_transform(X,y)
once = cross_val_score(RFC_,X_wrapper,y,cv=5).mean()
score.append(once)
plt.figure(figsize=[20,5])
plt.plot(range(1,751,50),score)
plt.xticks(range(1,751,50))
plt.show()
总结:
真正的高手,往往使用特征创造或特征提取来寻找高级特征。