2020-12-24人体行为数据分析之数据导入与特征选择

1.txt文件的读取与导入

参考

https://blog.csdn.net/jinbo1997/article/details/111597731

2.CSV数据集的导入

#因为需要导入的数据集已经分为数据集和测试集,因此使用两行代码分别导入测试集和训练集
data_train=pd.read_csv('csv文件的路径')
data_test=pd.read_csv('csv文件的路径')
#如果觉得分的不好的话,可以对数据集进行合并操作
newdata=pd.concat([data_train,data_test],axis=0)
#查看数据集的各个信息
data_train.info()
#查看各个列的名字
data_train.columns()

3.使用随机森林进行分类处理,因此不可以出现字符串类型,只允许字符型,因此需要查看数据里面是否有字符串类型的,要将这些转化为数值型

(1)因为数据集没有进行特征和标签的划分,首先要进行训练集

#特征在前562行,标签在最后一行,要将其进行分离
Xtrain=data_train.loc[:,'tBodyAcc-mean()-X':'subject']
Ytrain=data_train.loc[:,['Activity']]
Xtest=data_test.loc[:,'tBodyAcc-mean()-X':'subject']
Ytest=data_test.loc[:,['Activity']]
#也可以使用下边的方法,从第一列取,一直取到倒数第二列
data_train.loc[:,0:-1]

(2)可以看到,标签全部是字符型,我们可以查看标签的有几类

np.unique(data_train['Activity'])
#运行结果array(['LAYING', 'SITTING', 'STANDING', 'WALKING', 'WALKING_DOWNSTAIRS',
#       'WALKING_UPSTAIRS'], dtype=object)

(3)将标签转化为数值型,使用LabelEncoder:标签专用,能够将分类转换为分类数值

 

from sklearn.preprocessing import LabelEncoder
#实例化并导入数据
le_train=LabelEncoder().fit(Ytrain)
#通过transform接口调取结果
Ytrain_num=le_train.transform(Ytrain)
Ytrain_num.shape
#测试集同样的方法
#preprocessing.LabelEncoder:标签专用,能够将分类转换为分类数值
#LAYING' :0
#'SITTING':1 
#'STANDING':2 
#'WALKING':3 
#'WALKING_DOWNSTAIRS':4 
#'WALKING_UPSTAIRS'5

这里只是标签的处理,特征的处理分为

分类型特征处理和

preprocessing.OrdinalEncoder:特征专用,能够将分类特征转换为分类数值

from sklearn.preprocessing import OrdinalEncoder
 
#接口categories_对应LabelEncoder的接口classes_,一模一样的功能
data_ = data.copy()
 
data_.head()
 
OrdinalEncoder().fit(data_.iloc[:,1:-1]).categories_
 
data_.iloc[:,1:-1] = OrdinalEncoder().fit_transform(data_.iloc[:,1:-1])
 
data_.head()

preprocessing.OneHotEncoder:独热编码,创建哑变量

data.head()
 
from sklearn.preprocessing import OneHotEncoder
X = data.iloc[:,1:-1]
 
enc = OneHotEncoder(categories='auto').fit(X)
result = enc.transform(X).toarray()
result
 
#依然可以直接一步到位,但为了给大家展示模型属性,所以还是写成了三步
OneHotEncoder(categories='auto').fit_transform(X).toarray()
 
#依然可以还原
pd.DataFrame(enc.inverse_transform(result))
 
enc.get_feature_names()
 
result
result.shape
 
#axis=1,表示跨行进行合并,也就是将量表左右相连,如果是axis=0,就是将量表上下相连
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。二值化是对文本计数数据的常见操作,分析人员
可以决定仅考虑某种现象的存在与否。它还可以用作考虑布尔随机变量的估计器的预处理步骤(例如,使用贝叶斯
设置中的伯努利分布建模)。

#将年龄二值化
 
data_2 = data.copy()
 
from sklearn.preprocessing import Binarizer
X = data_2.iloc[:,0].values.reshape(-1,1)               #类为特征专用,所以不能使用一维数组
transformer = Binarizer(threshold=30).fit_transform(X)
 
transformer

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()

 

(4)对于最原始的数据,我们使用随机森林和决策树先试一下

from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
#实例化
clf=DecisionTreeClassifier(random_state=0)
rfc=RandomForestClassifier(random_state=0)
#导入训练集训练
clf=clf.fit(Xtrain,Ytrain)
rfc=rfc.fit(Xtrain,Ytrain)

#导入测试集,使用score(精确率)进行测试
score_c=clf.score(Xtest,Ytest)
score_r=rfc.score(Xtest,Ytest)

print("Single Tree:{}".format(score_c),"Random Forest:{}".format(score_r))

运行结果:

Single Tree:0.8639294197488971 Random Forest:0.9250084832032576

可以看出随机森林的分类效果还是比较高的

也可以画出十次交叉验证的结果

rfc_l = []
clf_l = []
 
for i in range(10):
    rfc = RandomForestClassifier(n_estimators=25)
    rfc_s = cross_val_score(rfc,Xtrain,Ytrain,cv=10).mean()
    rfc_l.append(rfc_s)
    clf = DecisionTreeClassifier()
    clf_s = cross_val_score(clf,Xtrain,Ytrain,cv=10).mean()
    clf_l.append(clf_s)
plt.plot(range(1,11),rfc_l,label = "Random Forest")
plt.plot(range(1,11),clf_l,label = "Decision Tree")
plt.legend()
plt.show()

4.数据预处理

因为数据的特征有562个,可能会影响分类效率,接下来我们对数据进行一些预处理,主要是特征选择,看看对分类结果有没有影响

(1)数据归一化处理

在机器学习算法实践中,我们往往有着将不同规格的数据转换到同一规格,或不同分布的数据转换到某个特定分布
的需求,这种需求统称为将数据“无量纲化”。譬如梯度和矩阵为核心的算法中,譬如逻辑回归,支持向量机,神经
网络,无量纲化可以加快求解速度;而在距离类模型,譬如K近邻,K-Means聚类中,无量纲化可以帮我们提升模
型精度,避免某一个取值范围特别大的特征对距离计算造成影响。(一个特例是决策树和树的集成算法们,对决策
树我们不需要无量纲化,决策树可以把任意数据都处理得很好。)
数据的无量纲化可以是线性的,也可以是非线性的。线性的无量纲化包括中心化(Zero-centered或者Mean-
subtraction)处理和缩放处理(Scale)。中心化的本质是让所有记录减去一个固定值,即让数据样本数据平移到
某个位置。缩放的本质是通过除以一个固定值,将数据固定在某个范围之中,取对数也算是一种缩放处理。

preprocessing.MinMaxScaler

当数据(x)按照最小值中心化后,再按极差(最大值 - 最小值)缩放,数据移动了最小值个单位,并且会被收敛到
[0,1]之间,而这个过程,就叫做数据归一化(Normalization,又称Min-Max Scaling)。注意,Normalization是归
一化,不是正则化,真正的正则化是regularization,不是数据预处理的一种手段。归一化之后的数据服从正态分
布,公式如下

from sklearn.preprocessing import MinMaxScaler #导包

scaler = MinMaxScaler()                             #实例化
scaler = scaler.fit(Xtrain)                           #fit,在这里本质是生成min(x)和max(x)
result = scaler.transform(Xtrain)                     #通过接口导出结果
Xtrain=result
Xtrain

(2)数据标准化

preprocessing.StandardScaler

 

当数据(x)按均值(μ)中心化后,再按标准差(σ)缩放,数据就会服从为均值为0,方差为1的正态分布(即标准正态分
布),而这个过程,就叫做数据标准化(Standardization,又称Z-score normalization),公式如下:

大多数机器学习算法中,会选择StandardScaler来进行特征缩放,因为MinMaxScaler对异常值非常敏
感。在PCA,聚类,逻辑回归,支持向量机,神经网络这些算法中,StandardScaler往往是最好的选择。
MinMaxScaler在不涉及距离度量、梯度、协方差计算以及数据需要被压缩到特定区间时使用广泛,比如数字图像
处理中量化像素强度时,都会使用MinMaxScaler将数据压缩于[0,1]区间之中。
建议先试试看StandardScaler,效果不好换MinMaxScaler。

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()                           #实例化
scaler.fit(Xtrain)                                    #fit,本质是生成均值和方差

scaler.mean_

scaler.var_

Xtrain_tmp=scaler.transform(Xtrain)

Xtrain_tmp.mean()

Xtrain_tmp.std()

(3)对于标准化处理之后吗,查看分类准确率

#实例化
clf=DecisionTreeClassifier(random_state=0)
rfc=RandomForestClassifier(random_state=0)
#导入训练集训练
clf=clf.fit(Xtrain,Ytrain)
rfc=rfc.fit(Xtrain,Ytrain)

#导入测试集,使用score(精确率)进行测试
score_c=clf.score(Xtest,Ytest)
score_r=rfc.score(Xtest,Ytest)

print("Single Tree:{}".format(score_c),"Random Forest:{}".format(score_r))
#运行结果:
Single Tree:0.8072616219884629 Random Forest:0.9250084832032576

决策树的效率一样,可能数据再发布之前就已经处理过了

5.特征选择

既然数据预处理对于分类准确率没有提高,那么就进行数据的特征选择,筛选出无用的特征放弃

(1)方差过滤法

VarianceThreshold 
这是通过特征本身的方差来筛选特征的类。比如一个特征本身的方差很小,就表示样本在这个特征上基本没有差
异,可能特征中的大多数值都一样,甚至整个特征的取值都相同,那这个特征对于样本区分没有什么作用。所以无
论接下来的特征工程要做什么,都要优先消除方差为0的特征。VarianceThreshold有重要参数threshold,表示方
差的阈值,表示舍弃所有方差小于threshold的特征,不填默认为0,即删除所有的记录都相同的特征。

from sklearn.feature_selection import VarianceThreshold
#删除方差为0的列
selector = VarianceThreshold()                      #实例化,不填参数默认方差为0
X_var0=selector.fit_transform(Xtrain)
X_var0.shape

结果竟然是:

(7352, 562)

说明数据里面没有方差为0的列

换个阈值,以方差中位数为阈值,删除小于中位数的特征

Xtrain=VarianceThreshold(np.median(Xtrain.var().values)).fit_transform(Xtrain)

运行结果:

(7352, 281)

筛选特征结果还是可以的,但是我们来看看分类准确度

#实例化
clf=DecisionTreeClassifier(random_state=0)
rfc=RandomForestClassifier(random_state=0)
#导入训练集训练
clf=clf.fit(Xtrain,Ytrain)
rfc=rfc.fit(Xtrain,Ytrain)

#导入测试集,使用score(精确率)进行测试
score_c=clf.score(Xtest,Ytest)
score_r=rfc.score(Xtest,Ytest)

print("Single Tree:{}".format(score_c),"Random Forest:{}".format(score_r))
Single Tree:0.12249745503902273 Random Forest:0.34441805225653205

可谓是惨不忍睹,还不如蒙的

可以看出,方差过滤之后,准确率急剧下降,从新把数据恢复

Xtrain=data_train.loc[:,'tBodyAcc-mean()-X':'subject']
Xtest=data_test.loc[:,'tBodyAcc-mean()-X':'subject']
Xtrain.shape,Xtest.shape

(2)卡方过滤

卡方过滤是专门针对离散型标签(即分类问题)的相关性过滤。卡方检验类feature_selection.chi2计算每个非负
特征和标签之间的卡方统计量,并依照卡方统计量由高到低为特征排名。再结合feature_selection.SelectKBest
这个可以输入”评分标准“来选出前K个分数最高的特征的类,我们可以借此除去最可能独立于标签,与我们分类目
的无关的特征。
另外,如果卡方检验检测到某个特征中所有的值都相同,会提示我们使用方差先进行方差过滤。并且,刚才我们已
经验证过,当我们使用方差过滤筛选掉一半的特征后,模型的表现时提升的。因此在这里,我们使用threshold=中
位数时完成的方差过滤的数据来做卡方检验(如果方差过滤后模型的表现反而降低了,那我们就不会使用方差过滤
后的数据,而是使用原数据):

 

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(Xtrain,Ytrain)
X_fschi.shape

程序会报错,因为卡方过滤必须要求数据是正值,但传感器数据有负值,因此放弃该方法

(3)F值过滤

 

F检验,又称ANOVA,方差齐性检验,是用来捕捉每个特征与标签之间的线性关系的过滤方法。它即可以做回归也
可以做分类,因此包含feature_selection.f_classif(F检验分类)和feature_selection.f_regression(F检验回
归)两个类。其中F检验分类用于标签是离散型变量的数据,而F检验回归用于标签是连续型变量的数据。
和卡方检验一样,这两个类需要和类SelectKBest连用,并且我们也可以直接通过输出的统计量来判断我们到底要
设置一个什么样的K。需要注意的是,F检验在数据服从正态分布时效果会非常稳定,因此如果使用F检验过滤,我
们会先将数据转换成服从正态分布的方式。
F检验的本质是寻找两组数据之间的线性关系,其原假设是”数据不存在显著的线性关系“。它返回F值和p值两个统
计量。和卡方过滤一样,我们希望选取p值小于0.05或0.01的特征,这些特征与标签时显著线性相关的,而p值大于
0.05或0.01的特征则被我们认为是和标签没有显著线性关系的特征,应该被删除。以F检验的分类为例,我们继续
在数字数据集上来进行特征选择:

 

from sklearn.feature_selection import f_classif
F, pvalues_f = f_classif(Xtrain,Ytrain)
k=F.shape[0]-(pvalues_f>0.05).sum()
#得到k值之后,使用下边代码进行f值过滤
Xtest=SelectKBest(f_classif,k=556).fit_transform(Xtest,Ytest)
cross_val_score(RFC(n_estimators=10,random_state=0),Xtest,Ytest,cv=5).mean()

运行结果:

0.8751425858248684

不增反降,因此需要继续调整方法

(4)互信息法

互信息法是用来捕捉每个特征与标签之间的任意关系(包括线性和非线性关系)的过滤方法。和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(Xtrain,Ytrain)
(result>0.1).sum()
Xtrain=SelectKBest(MIC,k=392).fit_transform(Xtrain,Ytrain)
cross_val_score(RFC(n_estimators=10,random_state=0),Xtest,Ytest,cv=5).mean()

结果只有:

0.8896916808871745

还是没有原始数据高

我们再使用其他方法

(5)嵌入式特征选择

嵌入法是一种让算法自己决定使用哪些特征的方法,即特征选择和算法训练同时进行。在使用嵌入法时,我们先使
用某些机器学习的算法和模型进行训练,得到各个特征的权值系数,根据权值系数从大到小选择特征。这些权值系
数往往代表了特征对于模型的某种贡献或某种重要性,比如决策树和树的集成模型中的feature_importances_属
性,可以列出各个特征对树的建立的贡献,我们就可以基于这种贡献的评估,找出对模型建立最有用的特征。因此
相比于过滤法,嵌入法的结果会更加精确到模型的效用本身,对于提高模型效力有更好的效果。并且,由于考虑特
征对模型的贡献,因此无关的特征(需要相关性过滤的特征)和无区分度的特征(需要方差过滤的特征)都会因为
缺乏对模型的贡献而被删除掉,可谓是过滤法的进化版。

过滤法中使用的统计量可以使用统计知识和常识来查找范围(如p值应当低于显著性水平0.05),而嵌入法中使用
的权值系数却没有这样的范围可找——我们可以说,权值系数为0的特征对模型丝毫没有作用,但当大量特征都对
模型有贡献且贡献不一时,我们就很难去界定一个有效的临界值。这种情况下,模型权值系数就是我们的超参数,
我们或许需要学习曲线,或者根据模型本身的某些性质去判断这个超参数的最佳值究竟应该是多少。在我们之后的
学习当中,每次讲解新的算法,我都会为大家提到这个算法中的特征工程是如何处理,包括具体到每个算法的嵌入
法如何使用。在这堂课中,我们会为大家讲解随机森林和决策树模型的嵌入法。
另外,嵌入法引入了算法来挑选特征,因此其计算速度也会和应用的算法有很大的关系。如果采用计算量很大,计
算缓慢的算法,嵌入法本身也会非常耗时耗力。并且,在选择完毕之后,我们还是需要自己来评估模型。

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.001).fit_transform(Xtrain,Ytrain)
cross_val_score(RFC(n_estimators=10,random_state=0),X_embedded,Ytrain,cv=5).mean()

最终的结果是:

0.8896916808871745

还是不尽人意,试试包装法

(6)包装法

包装法也是一个特征选择和算法训练同时进行的方法,与嵌入法十分相似,它也是依赖于算法自身的选择,比如
coef_属性或feature_importances_属性来完成特征选择。但不同的是,我们往往使用一个目标函数作为黑盒来帮
助我们选取特征,而不是自己输入某个评估指标或统计量的阈值。包装法在初始特征集上训练评估器,并且通过
coef_属性或通过feature_importances_属性获得每个特征的重要性。然后,从当前的一组特征中修剪最不重要的
特征。在修剪的集合上递归地重复该过程,直到最终到达所需数量的要选择的特征。区别于过滤法和嵌入法的一次
训练解决所有问题,包装法要使用特征子集进行多次训练,因此它所需要的计算成本是最高的。

注意,在这个图中的“算法”,指的不是我们最终用来导入数据的分类或回归算法(即不是随机森林),而是专业的
数据挖掘算法,即我们的目标函数。这些数据挖掘算法的核心功能就是选取最佳特征子集。
最典型的目标函数是递归特征消除法(Recursive feature elimination, 简写为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(Xtrain,Ytrain)
selector.support_.sum() #结果是340
X_wrapper=selector.transform(Xtrain)
cross_val_score(RFC_,X_wrapper,Ytrain,cv=5).mean()

在340个特征的情况下,准确率只有

0.8918682741621462

我们画一个学习曲线来探索一下有没有更好的

score = []
for i in range(1,751,50):
    X_wrapper = RFE(RFC_,n_features_to_select=i, step=50).fit_transform(Xtrain,Ytrain)
    once = cross_val_score(RFC_,X_wrapper,Ytrain,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()

可以看出151的时候还是比较高的,因此我们把n_features_to_select设置成151

X_wrapper = RFE(RFC_,n_features_to_select=151, step=50).fit_transform(Xtrain,Ytrain)
cross_val_score(RFC_,X_wrapper,Ytrain,cv=5).mean()
0.9039753603684846

这个结果最接近,后期还要继续调参才可以

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值