本文共 13281 字,大约阅读时间需要 44 分钟。
4、 特征工程
特征工程主要是对一些不适合直接参与建模的特征进行各种处理,通过已有数据构建一些新特征,对特征进行哑变量转换等等。
4.1 对Name进行处理
由于名字一般都比较杂乱,似乎对模型预测没有任何作用。但是通过对Name进行观察发现,在姓名里包含了一些身份信息,性别信息,我们可以粗略看一下。
#对Name进行处理#查看Nameprint(dataset["Name"].head())
结果如下:
0 Braund, Mr. Owen Harris1 Cumings, Mrs. John Bradley (Florence Briggs Th...2 Heikkinen, Miss. Laina3 Futrelle, Mrs. Jacques Heath (Lily May Peel)4 Allen, Mr. William HenryName: Name, dtype: object
从上面可以看到名字中间部分有Mr、Mrs这些能代表性别也能一定程度上反映身份的信息,更具体的可以查看整个数据集,这里直接提取出中间部分,并进行统计。
#下面直接提取名字中间部分dataset_title=[i.split(",")[1].split(".")[0].strip() for i in dataset["Name"]]dataset["Title"]=pd.Series(dataset_title)#查看详情print(dataset["Title"].describe())print(dataset["Title"].unique())
结果如下
count 1299unique 18top Mrfreq 753Name: Title, dtype: object['Mr' 'Mrs' 'Miss' 'Master' 'Don' 'Rev' 'Dr' 'Mme' 'Ms' 'Major' 'Lady' 'Sir' 'Mlle' 'Col' 'Capt' 'the Countess' 'Jonkheer' 'Dona']
以下是从百度查询到的相关解释:
Mr.= mister,先生Mrs.= mistress,太太/夫人Miss,复数为misses,对未婚妇女用,Ms.或Mz,美国近来用来称呼婚姻状态不明的妇女Madame简写是Mme.,复数是mesdames(简写是Mme)Mlle,小姐Lady, 女士,指成年女子,有些人尤其是长者认为这样说比较礼貌Dona,是西班牙语对女子的称谓,相当于英语的 LadyMaster,佣人对未成年男少主人的称呼,相当于汉语的"少爷"。Mr. Mister的略字,相当于汉语中的"先生",是对男性一般的称呼,区别于有头衔的人们,如Doctor, Professor,Colonel等Don,n. <西> (置于男士名字前的尊称)先生,堂jonkheer是贵族St.= saint,圣人Rev.= reverend,用于基督教的牧师,如the Rev. Mr.SmithDr.= doctor,医生/博士Colonel,上校major,意思有少校人意思The Countless,女伯爵 西>
所以我们可以将上面定义的Title进行分类:
第一类:’Mr’ 、’Don’ 第二类:’Mrs’ 、’Miss’ 、’Mme’ 、’Ms’ 、’Lady’、 ‘Dona’ 、’Mlle’ 第三类: ‘Sir’ 、’Major’、 ‘Col’ 、’Capt’ 第四类:’Master’ 、’Jonkheer’、 ‘the Countess’ 第五类:’Rev’ 、’Dr’#将title合并为几个组dataset["Title"]=dataset["Title"].replace(['Mr','Don'],'Mr')dataset["Title"]=dataset["Title"].replace(['Mrs','Miss','Mme','Ms','Lady','Dona','Mlle'],'Ms')dataset["Title"]=dataset["Title"].replace(['Sir','Major','Col','Capt'],'Major')dataset["Title"]=dataset["Title"].replace(['Master','Jonkheer','the Countess'],'Jonkheer')dataset["Title"]=dataset["Title"].replace(['Rev','Dr'],'Rev')
查看各组的幸存率
#我们查看各组的幸存率情况:g=sns.barplot(data=dataset[:train_len],x="Title",y="Survived")g.set_ylabel("Survival Probability")plt.show()
结果如下:
从图上可以看到男女的差别与之前的分析是一致的,同时贵族和军人相对其他男性也有较高的幸存率,但是神职人员和医生的生存率并没有高出很多,医生的幸存率有点不合逻辑因而重新分组。 结果如下: 从图上看到神职人员没有幸存的,而医生的幸存率较普通男性仍然高出很多。#下面将姓名数值化dataset["Title"]=dataset["Title"].map({ 'Mr':0,'Ms':1,'Major':2,'Jonkheer':3,'Rev':4,'Dr':5})dataset["Title"]=dataset["Title"].astype(int)#将Title哑变量化dataset=pd.get_dummies(dataset,columns=["Title"],prefix="TL")# 去掉name这一特征dataset.drop(labels = ["Name"], axis = 1, inplace = True)print(dataset.info())
4.2 对Cabin进行处理
#对Cabin进行处理#先查看Cabin的情况print(dataset["Cabin"].describe())
结果如下:
count 292unique 186top G6freq 5Name: Cabin, dtype: object
从上可以看到Cabin中有186个不同的取值,直接统计不太可能也没必要。
下面再查看缺失值的情况print(dataset["Cabin"].isnull().sum())
结果如右:1007
缺失值和存在真实值的样本比例是1007:292,通常这种情况可以直接将该特征去掉(而且船舱与票价是有相关性的);这里我们并没有去掉,主要考虑到船舱本身可能能反映乘客的身份和逃生的硬件实施和便利性,而普通人群中没有特殊身份的人更可能缺少船舱信息,用字母X来表示缺失,不失为一种恰当的处理方法。首字母相同的船舱情况相近,船舱信息直接用首字母来代替也是合理的。#将船舱信息进行替换dataset["Cabin"]=pd.Series([i[0] if not pd.isnull(i) else 'X' for i in dataset['Cabin']])#再来查看一下船舱信息print(dataset["Cabin"].describe())print(dataset["Cabin"].isnull().sum())
可以看到如下结果:
count 1299unique 9top Xfreq 1007Name: Cabin, dtype: object0
现在只有Cabin只有9个值,而缺失值为0。
#查看不同船舱的幸存率g=sns.barplot(data=dataset[:train_len],x="Cabin",y="Survived")g.set_ylabel("Survival Probability")plt.show()
结果如下:
从图上可以看到不同船舱的幸存率是有差异的,除了T(全部遇难),其他有船舱信息的幸存率均要高于没有船舱信息的乘客,与预期一致。#利用哑变量将Cabin信息数值化dataset=pd.get_dummies(dataset,columns=["Cabin"],prefix="Cabin")#再来查看一下船舱信息print(dataset.info())
结果如下:
RangeIndex: 1299 entries, 0 to 1298Data columns (total 20 columns):Age 1299 non-null float64Embarked 1299 non-null objectFare 1299 non-null float64Parch 1299 non-null int64PassengerId 1299 non-null int64Pclass 1299 non-null int64Sex 1299 non-null int64SibSp 1299 non-null int64Survived 881 non-null float64Ticket 1299 non-null objectTitle 1299 non-null int32Cabin_A 1299 non-null uint8Cabin_B 1299 non-null uint8Cabin_C 1299 non-null uint8Cabin_D 1299 non-null uint8Cabin_E 1299 non-null uint8Cabin_F 1299 non-null uint8Cabin_G 1299 non-null uint8Cabin_T 1299 non-null uint8Cabin_X 1299 non-null uint8dtypes: float64(3), int32(1), int64(5), object(2), uint8(9)memory usage: 118.1+ KBNone
4.3 对Ticket进行处理
通过对原始数据进行查看可以知道,Ticket中包含的信息也是比较杂乱的,需要做一定处理。船票主要是分为两种,有字母前缀和没有字母前缀的,有字母前缀的应该是一些较特殊的票,其中就有类似于现在的VIP这种票,也可能有一些比较差的票。正如之前的分析中讲到,将船票这一特征纳入到模型中主要是考虑船票本身能反映票价和位置等信息从而与生存率相关联;而相同的字母前缀具有相同的性质,因而这里将字母前缀来代替船票信息。如果是纯数字,则归为同一类“X”。
#对Ticket进行处理Ticket=[]for i in list(dataset["Ticket"]): if not i.isdigit(): Ticket.append(i.replace(".","").replace("/","").strip().split(' ')[0]) else: Ticket.append("X")dataset["Ticket"]=Ticket#查看替换后的情况print(dataset["Ticket"].describe())
结果如下:
count 1299unique 37top Xfreq 954Name: Ticket, dtype: object
#查看不同船票的生存率g=sns.barplot(data=dataset,x="Ticket",y="Survived")g.set_ylabel("Survival Probability")plt.show()
结果如下:
从图上可以看到不同船票之间的差别。#利用哑变量将Ticket数值化dataset=pd.get_dummies(dataset,columns=["Ticket"],prefix="T")
4.4 对Embarked进行处理
#将Embarked哑变量化dataset = pd.get_dummies(dataset, columns = ["Embarked"], prefix="Em")
4.4 对Pclass进行处理
#将Pclass哑变量化dataset["Pclass"] = dataset["Pclass"].astype("category")dataset = pd.get_dummies(dataset, columns = ["Pclass"],prefix="Pc")
4.5 去除PassengerId
dataset.drop(labels = ["PassengerId"], axis = 1, inplace = True)
4.6 查看处理好的数据
#查看最终数据#print(dataset.head())print(dataset.info())
结果如下:
RangeIndex: 1299 entries, 0 to 1298Data columns (total 64 columns):Age 1299 non-null float64Fare 1299 non-null float64Parch 1299 non-null int64Sex 1299 non-null int64SibSp 1299 non-null int64Survived 881 non-null float64TL_0 1299 non-null uint8TL_1 1299 non-null uint8TL_2 1299 non-null uint8TL_3 1299 non-null uint8TL_4 1299 non-null uint8TL_5 1299 non-null uint8Cabin_A 1299 non-null uint8Cabin_B 1299 non-null uint8Cabin_C 1299 non-null uint8Cabin_D 1299 non-null uint8Cabin_E 1299 non-null uint8Cabin_F 1299 non-null uint8Cabin_G 1299 non-null uint8Cabin_T 1299 non-null uint8Cabin_X 1299 non-null uint8T_A 1299 non-null uint8T_A4 1299 non-null uint8T_A5 1299 non-null uint8T_AQ3 1299 non-null uint8T_AQ4 1299 non-null uint8T_AS 1299 non-null uint8T_C 1299 non-null uint8T_CA 1299 non-null uint8T_CASOTON 1299 non-null uint8T_FC 1299 non-null uint8T_FCC 1299 non-null uint8T_Fa 1299 non-null uint8T_LINE 1299 non-null uint8T_LP 1299 non-null uint8T_PC 1299 non-null uint8T_PP 1299 non-null uint8T_PPP 1299 non-null uint8T_SC 1299 non-null uint8T_SCA3 1299 non-null uint8T_SCA4 1299 non-null uint8T_SCAH 1299 non-null uint8T_SCOW 1299 non-null uint8T_SCPARIS 1299 non-null uint8T_SCParis 1299 non-null uint8T_SOC 1299 non-null uint8T_SOP 1299 non-null uint8T_SOPP 1299 non-null uint8T_SOTONO2 1299 non-null uint8T_SOTONOQ 1299 non-null uint8T_SP 1299 non-null uint8T_STONO 1299 non-null uint8T_STONO2 1299 non-null uint8T_STONOQ 1299 non-null uint8T_SWPP 1299 non-null uint8T_WC 1299 non-null uint8T_WEP 1299 non-null uint8T_X 1299 non-null uint8Em_C 1299 non-null uint8Em_Q 1299 non-null uint8Em_S 1299 non-null uint8Pc_1 1299 non-null uint8Pc_2 1299 non-null uint8Pc_3 1299 non-null uint8dtypes: float64(3), int64(3), uint8(58)memory usage: 134.5 KBNone
5、 数据建模
#重新获取训练数据和测试数据train=dataset[:train_len]train["Survived"]=train["Survived"].astype(int)Y_train=train["Survived"]X_train=train.drop(labels=["Survived"],axis=1)test=dataset[train_len:]test.drop(labels=["Survived"],axis=1,inplace=True)
5.1 利用分类算法进行分类
有时候可能会使用多种算法进行测试,从中选出比较适应数据场景的算法;对新样本的预测也可能是综合多种算法的结果;这里我直接选用随机森林进行数据分类,之后有时间再做更复杂的训练。
使用搜索最佳参数的方式进行训练。# 搜索随机森林的最佳参数 RFC = RandomForestClassifier()## 设置参数网络rf_param_grid = { "max_depth": [None], "max_features": [1, 3, 10], "min_samples_split": [2, 3, 10], "min_samples_leaf": [1, 3, 10], "bootstrap": [False], "n_estimators" :[100,300], "criterion": ["gini"]}gsRFC = GridSearchCV(RFC,param_grid = rf_param_grid, cv=kfold, scoring="accuracy", n_jobs= 1, verbose = 1)gsRFC.fit(X_train,Y_train)RFC_best = gsRFC.best_estimator_print(RFC_best)# 打印最佳得分print(gsRFC.best_score_)
结果如下:
RandomForestClassifier(bootstrap=False, class_weight=None, criterion='gini', max_depth=None, max_features=3, max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, min_samples_leaf=3, min_samples_split=3, min_weight_fraction_leaf=0.0, n_estimators=100, n_jobs=1, oob_score=False, random_state=None, verbose=0, warm_start=False)0.83427922815
从以上结果可以看到,如果使用随机森林作模型,其最佳配置为max_features=3,min_samples_leaf=3,min_samples_split=3,n_estimators=100,最佳得分为0.83427922815
可以使用不同算法进行尝试,优中选优。5.2 评估模型效果
这里只通过学习曲线对比欠拟合和过拟合的情况
欠拟合:简单讲就是模型参数太少,不管数据多少,都无法正确反映出数据真实的结构和规律;欠拟合情况下训练集的效果较差,测试集的效果也差。 在欠拟合情况下,增加样本数量对模型没有什么帮助,因为参数本身不够,模型先天不足; 过拟合:模型参数太多,数据太少,这样模型的参数取值会过分的迎合训练集数据特点,从对训练集的数据拟合的非常好,但测试集数据结构不一定与训练集完全一样,这就导致了此种情况下得到的模型泛化能力很弱,在训练集之外的数据中效果很差; 在过拟合情况下,增加训练样本模型效果会得到提升,因为样本越多,数据结构就越复杂,从而需要更多参数建立模型。# 效果评估#####--------------------------------------------------------------------------------------------------### 效果评估之学习曲线def plot_learning_curve(estimator, title, X, y, ylim=None, cv=None, n_jobs=1, train_sizes=np.linspace(.1, 1.0, 5)): """Generate a simple plot of the test and training learning curve""" plt.figure() plt.title(title) if ylim is not None: plt.ylim(*ylim) plt.xlabel("Training examples") plt.ylabel("Score") train_sizes, train_scores, test_scores = learning_curve( estimator, X, y, cv=cv, n_jobs=n_jobs, train_sizes=train_sizes) train_scores_mean = np.mean(train_scores, axis=1) train_scores_std = np.std(train_scores, axis=1) test_scores_mean = np.mean(test_scores, axis=1) test_scores_std = np.std(test_scores, axis=1) plt.grid() plt.fill_between(train_sizes, train_scores_mean - train_scores_std, train_scores_mean + train_scores_std, alpha=0.1, color="r") plt.fill_between(train_sizes, test_scores_mean - test_scores_std, test_scores_mean + test_scores_std, alpha=0.1, color="g") plt.plot(train_sizes, train_scores_mean, 'o-', color="r", label="Training score") plt.plot(train_sizes, test_scores_mean, 'o-', color="g", label="Cross-validation score") plt.legend(loc="best") return pltg = plot_learning_curve(gsRFC.best_estimator_,"RF mearning curves",X_train,Y_train,cv=kfold)plt.show()
结果如下:
如何查看学习曲线? 首先看训练集的得分和交叉验证的得分会不会随着样本增加而产生大的变化,如果样本增加得分明显提高说明过拟合; 然后看训练集得分和交叉验证得分差别大不大,如果两差别很大,说明模型在不同数据中表现不一样,即模型的泛化能力差; 从上图中可以看到随机森林算法中随着样本的增多,分类效果变好,说明有一定的过拟合,但变化程度不大,说明过拟合能接受;而训练样本中的得分一直都比交叉验证时的高,这个与实际情况相符;交叉验证时得分与训练样本中的得分差别不大,说明模型的泛化能力不差。 效果的查看更需要通过与其他算法对比,才知道哪种算法比较适应该数据场景。这里只有一种算法,只是演示如下查看学习曲线。5.3 查看特征变量在模型中的权重
不算算法权重的意义并不完全相同,在回归算法,权重表示回归系数,在PCA算法中,权重仅反映该特征包含信息的多少。
# 特征变量权重分析#####--------------------------------------------------------------------------------------------------nrows = ncols = 1fig, axes = plt.subplots(nrows = nrows, ncols = ncols, sharex="all", figsize=(15,15))#names_classifiers = [("AdaBoosting", ada_best),("ExtraTrees",ExtC_best),("RandomForest",RFC_best),("GradientBoosting",GBC_best)]names_classifiers = [("RandomForest",RFC_best)]nclassifier = 0for row in range(nrows): for col in range(ncols): name = names_classifiers[nclassifier][0] classifier = names_classifiers[nclassifier][1] indices = np.argsort(classifier.feature_importances_)[::-1][:40] g = sns.barplot(y=X_train.columns[indices][:40],x = classifier.feature_importances_[indices][:40] , orient='h',ax=axes[row][col]) g.set_xlabel("Relative importance",fontsize=12) g.set_ylabel("Features",fontsize=12) g.tick_params(labelsize=9) g.set_title(name + " feature importance") nclassifier += 1plt.show()
结果如下:
从上图可以看到,在随机森林的生成模型中,对预测结果产生最大权重的特征是sex,然后是TL_0和TL_1; 如果对比不同算法,可能会看到在不同算法中,各特征权重并不是完全相同的。还有一个有趣的现象,同样的算法,同样的输入,两次跑的结果中不同特征的权重可能也不一样,像随机森林之类算法也好理解,毕竟在抽取样本和特征组成新数据集的时候本身也存在随机性。5.4 集成预测
利用多种算法建模并对测试样本进行预测
其思想其实跟随机森林的整体思想是一样的,对最后的结果采取少数服从多数的策略。# 集成预测#####--------------------------------------------------------------------------------------------------votingC = VotingClassifier(estimators=[('rfc', RFC_best),('svc', SVMC_best)], voting='soft', n_jobs=4)#里面放的是各算法最佳参数之下的模型votingC = votingC.fit(X_train, Y_train)test_Survived = pd.Series(votingC.predict(test), name="Survived")results = pd.concat([IDtest,test_Survived],axis=1)results.to_csv("ensemble_python_voting.csv",index=False)
对测试集的预测结果保存至当前文件夹的ensemble_python_voting.csv文件中。
至此单机版的数据挖掘整个过程就大致如此了!转载地址:https://blog.csdn.net/qingqing7/article/details/78516797 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!