titanic数据机器学习_round_2

文章详细描述了如何在Titanic数据集中进行特征处理,包括数据探索、清洗(如填充缺失值)、特征工程(如one-hotencoding和标准化)以及模型构建(如LogisticRegression、RandomForest、DecisionTree和KNN)。作者强调了特征理解和处理在机器学习中的重要性。
摘要由CSDN通过智能技术生成

前言

延续round_1的想法,我认为机器学习中特征处理的学习价值远高于模型。因此round_2是在round_1的基础上,重新梳理了特征处理的流程,在数据探索中增加了对特征的解释,对各个特征的清洗方式加入了个人对背景的理解,增加特征归一化处理。

目录

Exploratory data analysis

1.1 data describe

1.2 data cleaning

1.3 correlation

Data Engineering

2.1 transform and encode features

Modeling

3.1 min_max_scaler

3.2 benchmark-LogisticRegression

3.3 RandomForest/DecisionTree/KNN

Summary

Exploratory data analysis

1.1 data describe

#这一步关注min/max和data type
train.describe()
test.describe()

train dataset特征的各个特性

test dataset特征的各个特性 

初步观察发现,Age区间是[0.17,80];SibSp区间是[0,8];Parch区间是[0,9];Fare区间是[0,513)
test/train的数量比是418:891,约为1:2。

先查看连续型数值特征Fare和Age,出于考虑到后续可能想使用年龄分组和金额分组进行归类,先把类分好。

#查看连续数值age,fare
#age和survived关系,对age分层[0,15)[15,30)[30,45)[45,60)[60,100)
#先自动划分区间查看分布情况找界限,参考https://blog.csdn.net/weixin_42060598/article/details/109642986
ag=pd.cut(x=train['Age'],bins=5)
ag
#再自定义划分区间并加进train dataframe
ag=[0,15,30,45,60,100]
train['Age_group']=pd.cut(x=train['Age'],bins=ag)
train['Age_group']

#同理对Fare划分区间
fg=pd.cut(train['Fare'],bins=5)
fg
fg=[0,100,200,300,400,600]
train['Fare_group']=pd.cut(train['Fare'],bins=fg)
train['Fare_group']

再查看类别型特征:Pclass, Sex, SibSp, Parch, Embarked

#查看categorical data:Pclass,Sex,SibSp,Parch,Embarked
print(train['Pclass'].value_counts(),'\n')
print(train['Survived'].value_counts(),'\n')
print(train['Embarked'].value_counts(),'\n')

通过查看类别特征发现,Pclass分为1,2,3三个等级,其中3等人数最多;
Survived分为0,1两种,0是not survived, 1是Survived;
Embarked分为S,C,Q三种,s=Southampton c=Cherbourg Q=Queenstown。

然后查看获救率

#查看获救率和未获救率
#value_counts()[1]是指取类别为1的值
surv=train['Survived'].value_counts()[1]
print(surv)
not_surv=train['Survived'].value_counts()[0]
#df.shape可以最轻松获取数据集总数[m,n]m行n列,[0]就是取行数
surv_rate=surv/train.shape[0]*100
not_surv_rate=not_surv/train.shape[0]*100
#在''中加入多个{},可以在format函数中加入多个特征
print('the survived rate is {:.2f}% and not survived rate {:.2f}%'
      .format(surv_rate,not_surv_rate))
#发现survived rate为38.38%
The survived rate is 38.38% and not survived rate 61.62%。说明乘客平均生还率仅38.38%

使用工具可视化展示类别型特征的生还情况。

#使用crosstab查看categorical data:Pclass,Sex,SibSp,Parch,Embarked
#crosstab主要有index,columns,values,aggfunc四个参数,分别代表行,列,值,和值的计算方式
a=pd.crosstab(index=train['Survived'],columns=[train['Pclass']])
b=pd.crosstab(index=train['Survived'],columns=[train['Sex']])
c=pd.crosstab(index=train['Survived'],columns=[train['SibSp']])
d=pd.crosstab(index=train['Survived'],columns=[train['Parch']])
e=pd.crosstab(index=train['Survived'],columns=[train['Embarked']])
f=pd.crosstab(index=train['Survived'],columns=[train['Age_group']])
g=pd.crosstab(index=train['Survived'],columns=[train['Fare_group']])

#subplots可以绘制n*n的图,subplots_adjust可以调整图间距
fig,axes=plt.subplots(2,4,figsize=(12,6))
plt.subplots_adjust(hspace=0.3,wspace=0.2)
a.plot(kind='bar',ax=axes[0,0])
b.plot(kind='bar',ax=axes[0,1])
c.plot(kind='bar',ax=axes[0,2])
d.plot(kind='bar',ax=axes[0,3])
e.plot(kind='bar',ax=axes[1,0])
f.plot(kind='bar',ax=axes[1,1])
g.plot(kind='bar',ax=axes[1,2])
axes[0,0].set_title('class_surv_rate')
axes[0,1].set_title('gender_surv_rate')
axes[0,2].set_title('Siblings_surv_rate')
axes[0,3].set_title('parch_surv_rate')
axes[1,0].set_title('embarked_surv_rate')
axes[1,1].set_title('Age_surv_rate')
axes[1,2].set_title('Fare_surv_rate')

 根据各特征的获救分布,发现class-1、2比3的生还率高,female的生还率比male高;
有sib的比没有的高,parch影响不大,c处embarked的比q和s的生还率更高;
age_group中0-15岁低龄人生还率高,fare_group中200以上的生还率较高。

 1.2 Data Cleaning

通过info()函数发现train dataset的Age,Cabin,Embarked存在缺失值,test dataset中Age,Cabin存在缺失值。

print(train.info())
print('\n')
print(test.info())

train和test dataset的特征和值

通过isnull().sum()函数查找缺失值数量是多少。

print(train.isnull().sum(),'\n')
print(test.isnull().sum())

train和test dataset的缺失值

根据数据集解释,Embarked是登船地,应该与Fare+Pclass相关,考虑对应填充;
Age由于船难发生时,船员优先考虑妇女儿童登上救生艇且救生艇数量严重不足,即生还率应该是儿童>成年女性>成年男性,外加高等级舱室离甲板更近,因此Age应该与Sex+Pclass相关,由同Sex+Pclass的中位数填充;
由于上述操作需要很多Pclass+Fare的参照,其中Fare是连续数值,考虑根据Pclass对Fare取中位备用。

#根据Pclass对Fare取median
class_fare=train.groupby('Pclass')['Fare'].median()
class_fare

 

Age missing value fill

#首先获取Pclass+Sex对应的age中位数
age_fill=train.groupby(['Pclass','Sex'])['Age'].median()
print(age_fill)
#这段超级6,我之前的想法是写for循环+if判断,贴入不同数值,但是又繁琐又会报错, apply(lambda x:x.fillna(x.median()))超级强
#lambda函数直接将数据按Pclass、sex将人分类然后填充age中位数
#参考https://www.kaggle.com/code/gunesevitan/titanic-advanced-feature-engineering-tutorial
train['Age']=train.groupby(['Pclass','Sex'])['Age'].apply(lambda x:x.fillna(x.median()))
train.info()

通过groupby两个类别特征,对age进行简短填充循环,非常Pythonic。一行代码实现了根据Pclass和Gender进行分类后对各类别的Age求中位数,再填充至对应类别的Age缺失值中!

本来打算处理Cabin的,但是缺失数量太大,根据Pclass等特征填充后很容易导致与这些特征的相关性很高造成多重共线性。所以还是选择不处理。

Embarked missing Value fill

由于embarked缺失值为2条,可以通过查看对应Pclass+fare等信息推测

#查看缺失信息所在列
train[train['Embarked'].isnull()]
#找Pclass==1,Sex是female的embarked地点都是哪里
train['embark_du']=np.where((train['Pclass']==1)&(train['Sex']=='female'),1,0)
pd.pivot_table(train,index='Embarked',values='embark_du',aggfunc=np.sum)
#发现s最多,48条,选择S
train['Embarked']=train['Embarked'].fillna('S')

把PassengerId,Name,Cabin,Cabin_new,Embark_du,Ticket,Age_group和Fare_group drop掉,方便后续做correlation热力图

#把PassengerId,Name,Cabin,Cabin_new,Embark_du,Ticket,Age_group和Fare_group drop掉
train=train.drop(['PassengerId','Name','Cabin','Cabin_new','Ticket',
                  'Age_group','Fare_group','embark_du'],axis=1)

对test dataset做类似处理

#用pclass和sex分层,再用各分层中age的中位数进行填充
test['Age']=test.groupby(['Pclass','Sex'])['Age'].apply(lambda x:x.fillna(x.median()))
#用pclass、Sex分层,再用各分层中fare的中位数进行填充
test['Fare']=test.groupby(['Pclass','Sex'])['Fare'].apply(lambda x:x.fillna(x.median()))

#drop掉test数据集中PassengerId,Name,Ticket,Cabin
test=test.drop(['PassengerId','Name','Ticket','Cabin'],axis=1)

1.3 Correlation

#部分相关性为负,比如Pclass,等级越高,数字越小,生还率越高。
#所以查看与Survived相关性时看绝对值。
abs(train.corr()['Survived']).sort_values(ascending=False)

再查看所有特征的相关性 

train.corr().sort_values(by='Survived',ascending=False)
#发现和生还率相关性最高的连续型数值特征是Pclass,其次是Fare,但总体都不高
#特征之间相关性最高的是Fare和Pclass,绝对值为0.55,较低

将相关性可视化展示

#绘制heatmap可视化展示
plt.figure(figsize=(7,6))
sns.heatmap(data=abs(train.corr()),cmap='GnBu',annot=True)
plt.title('train_data_correlation')

 

Data Engineer 

2.1 Transform and encode features

 将categorical数值和categorical字符串数据改为one-hot encoding

#包括:Pclass,Sex,Embarked
train_enco=pd.get_dummies(train,columns=['Pclass','Sex','Embarked'])
test_enco=pd.get_dummies(test,columns=['Pclass','Sex','Embarked'])

再次查看特征转换后所有变量查看相关性

abs(train_enco.corr())['Survived'].sort_values(ascending=False)

 发现gender、Pclass、Fare与Survived的相关性最高,分别约为0.54,0.30,0.25。

Modeling

首先调取要用到的包

#使用Logistic Regression作为benchmark
#选择decision tree, knn, random forest进行训练
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import GridSearchCV,cross_val_score
from sklearn.ensemble import RandomForestClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score
#模型默认的model.score()函数都是R^2

先定义x_train, y_train, x_test,后续用到。

x_train=train_enco.drop(columns='Survived',axis=1)
y_train=train_enco['Survived']
x_test=test_enco

3.1 min_max_scaler

查看train_enco特征的标准差,考虑是否需要scaler

train_enco.describe().sort_values(by='std',axis=1,ascending=False)

 

 发现Fare和Age的min-max和标准差均大于其他变量,容易影响模型响应速度,考虑使用min_max_scaler无量纲化。

from sklearn.preprocessing import MinMaxScaler
scaler=MinMaxScaler(feature_range=(0,1))
scale_model=scaler.fit(x_train)
x_train=scale_model.transform(x_train)

 通过min-max-scaler之后,特征的标准差均降至1以下。

3.2 benchmark-LogisticRegression

#benchmark--LR
LRmodel=LogisticRegression()
LRmodel.fit(x_train,y_train)
LRpredict=LRmodel.predict(x_test)
print(LRmodel.score(x_train,y_train))

Logistic Regression模型最终R^2得分是0.8080808080808081

3.3 RandomForest/DecisionTree/KNN

RandomForest 

#Random Forest
RFmodel=RandomForestClassifier()
RFmodel.fit(x_train,y_train)
RFpredict=RFmodel.predict(x_test)
print(RFmodel.score(x_train,y_train))

随机森林模型最终R^2得分0.9809203142536476

Decision Tree

#使用GridSearchCV进行循环,找出最优超参数,最后使用best_params_和best_score_获取结果
max_depth=np.arange(1,11)
min_samples_leaf=np.arange(1,11)
min_samples_split=np.arange(1,11)
parameters={'max_depth':max_depth,
            'min_samples_leaf':min_samples_leaf,
            'min_samples_split':min_samples_split}
DT_result=GridSearchCV(estimator=DecisionTreeClassifier(),
                       param_grid=parameters,cv=10)
DT_result.fit(x_train,y_train)
print(DT_result.best_params_)
print('\n')
print(DT_result.best_score_)

经过网格搜索和交叉验证,最终decision tree的R^2得分是0.84。

其中超参的最优解如下{'max_depth': 9, 'min_samples_leaf': 6, 'min_samples_split': 8}

KNN

#KNN
#依旧使用GridSearchCV,找出最优超参数
k=np.arange(1,21)
weights=['distance']
parameters={'n_neighbors':k,'weights':weights}
KNN_result=GridSearchCV(estimator=KNeighborsClassifier(),
                        param_grid=parameters,cv=10)
KNN_result.fit(x_train,y_train)
print(KNN_result.best_params_)
print('\n')

经过网格搜索和交叉验证,最终KNN的最佳k值是13。

代入模型计算得分

KNNmodel=KNeighborsClassifier(n_neighbors=13,weights='distance')
KNNmodel.fit(x_train,y_train)
KNNmodel.score(x_train,y_train)

KNN模型最终R^2得分是0.9809203142536476。

总结

通过round_1和round_2的尝试,学习了机器学习的基本特征处理方法,数据解释,数据清洗并不是简单使用median,mean,ffill,bfill就可以的,真实场景缺失值的填充需要结合应用场景和背景,发现在特征转换的时候one-hot encoding是如此好用。对min-max-scaler建立一个初步认知。代入模型那一刻,已经是最后一步了,前期特征处理和模型训练的时间和消耗精力比值我觉得大概在50:1左右,而且由于特征处理需要结合业务背景,所以我认为gpt或者大模型当前还无法替代数据分析师的工作,更多是丰富一些跑模型的手段。

titanic数据集就暂时告一段落,还有很多地方可以挖掘,如果后续有新的想法,会继续更新~

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值