1 探索性分析
对数据进行一个整体的理解
1.1 查看数据都有一些什么特征
将测试集和训练集合并,方便后续的数据处理
importpandas as pdimportseaborn as snsimportnumpy as npimportmatplotlib.pyplot as plt%matplotlib inline
train= pd.read_csv('G:\\titanic\\train.csv')
test= pd.read_csv('G:\\titanic\\test.csv')
testId= test["PassengerId"]
dataset= pd.concat(objs = [train,test],axis = 0).reset_index(drop=True)
train_len= len(train)
PassengerId => 乘客ID
Pclass => 乘客等级(1/2/3等舱位)
Name => 乘客姓名
Sex => 性别
Age => 年龄
SibSp => 堂兄弟/妹个数
Parch => 父母与小孩个数
Ticket => 船票信息
Fare => 票价
Cabin => 客舱
Embarked => 登船港口
查看数据集的信息,可以看到age、cabin、enkarked、fare都是存在缺失值的
dataset.info()
1.2 各特征与survived之间的关系
1.2.1 Age
采用训练集数据进行观察分析,可以看到最后存活下来的年龄会整体偏低一些,但是特征不够明显
sns.boxplot(y=train["Age"],x=train["Survived"])
1.2.2 Sex
lady first
获救人数里女性占比更大
fig =plt.figure()
fig.set(alpha=0.2) #设定图表颜色alpha参数
Survived_m= train.Survived[train.Sex == 'male'].value_counts()
Survived_f= train.Survived[train.Sex == 'female'].value_counts()
df=pd.DataFrame({'male':Survived_m, 'female':Survived_f})
df.plot(kind='bar', stacked=True)
plt.xlabel("survived")
plt.ylabel("number")
1.2.3 Pclass
船舱等级为1的获救几率更大
sns.barplot(x=train["Pclass"],y=train["Survived"])
1.2.4 SibSp/Parch
灾难来临家庭人数多的获救几率高还是低?
将兄弟姐妹和父母合并得到家庭总人数
可以看到家庭人数在2-4人之间获救可能性更高些,单身一人缺少帮助,家庭人数多的需要照顾更多家人
train["Fsize"] = train["SibSp"] + train["Parch"] + 1sns.barplot(x= train["Fsize"],y = train["Survived"])
1.2.5 Fare
票价高的获救几率比较大,但是不明显。
同时可以看到Fare中存在一个异常值,需要将其剔除
sns.boxplot(y=train["Fare"],x=train["Survived"])
train["Fare"]=train["Fare"].replace(train["Fare"].max(),train["Fare"].median())
1.2.6 Name
乘客名字中带有相应的称呼,称呼可能代表这他的社会地位
其中Mr Miss Mrs Master 所占数量最多,其他比较少的都统一替换为rare
name_list = [i.split(",")[1].split(".")[0].strip() for i in dataset["Name"]]#按指定字符将字符串切片为列表
dataset["Title"] =pd.DataFrame(name_list)#g = sns.countplot(dataset["Title"])#g = plt.setp(g.get_xticklabels(),rotation=45)
dataset["Title"] = dataset["Title"].replace(['Lady', 'the Countess','Countess','Capt','Col','Don', 'Major', 'Rev', 'Sir', 'Jonkheer', 'Dona'],"rare")#dataset["Title"] = dataset["Title"].map({"rare":0,"Master":1, "Miss":2, "Ms" : 2 , "Mme":2, "Mlle":2, "Mrs":2, "Mr":3,"Dr":3})
sns.barplot(x = dataset["Title"],y = dataset["Survived"])
dataset["Title"].unique()
dataset.drop(labels= ["Name"],axis = 1 ,inplace =True)#将name列丢掉
dataset = pd.get_dummies(dataset,columns = ["Title"])#将title列给分列
1.2.7 Cabin
客舱号代表着所处船上的位置,可能不同的客舱位置跑到救援船上的几率有一定的相关性
缺失的客舱可能是没有客舱,统一替换为X
有客舱的整体来说比没有客舱的获救几率更高些
#Cabin
dataset["Cabin"]=pd.DataFrame([i[0] if not pd.isnull(i) else "X" for i in dataset["Cabin"]])#可以考虑加入客舱号字母后面的数字,可能有些数字里逃生口较近
dataset.Cabin.unique()
sns.barplot(x=dataset["Cabin"],y=dataset["Survived"])
dataset= pd.get_dummies(dataset,columns = ["Cabin"])
1.2.8 Embarked
从图中看出登船港口不同之间的获救几率差别不大,客人登船后基本都会到自己的客舱,猜测是不同的登船港口会分有无客舱或者船票类别登船,但是对是否获救影响不大
sns.barplot(x=train["Embarked"],y=train["Survived"])
1.2.9 Ticket
#Ticket
Ticket =[]for i inlist(dataset.Ticket):if noti.isdigit():
Ticket.append(i.replace(".","").replace("/","").strip().split(" ")[0])else:
Ticket.append("X")#按船票的字母和数字将船票区分
dataset["Ticket"] =Ticket
dataset.Ticket.unique()
dataset= pd.get_dummies(dataset,columns = ["Ticket"],prefix = "T")#prefix将Ticket改名
2 特征工程
2.1 Age Sex
年龄会跟父母和兄弟姐妹有很大关系,因此将SibSp,Parch,Pclass一样的平均值做为部分填充
其余填充剩余值的平均值,并将sex转化为0 1 数值型
将年龄分段
#Age填充#获取age缺失值的索引列表
index_nan_age = list(dataset["Age"][dataset.Age.isnull()].index)#print(index_nan_age)
for i inindex_nan_age:
age_med= dataset["Age"].median()
age_smed= dataset["Age"][((dataset["SibSp"] == dataset.iloc[i]["SibSp"])&(dataset["Parch"] == dataset.iloc[i]["Parch"])&(dataset["Pclass"] == dataset.iloc[i]["Pclass"]))].median()#取家人乘客等级相同的age平均值进行填充,其余用总平均值填充
if notnp.isnan(age_smed):
dataset["Age"].iloc[i] =age_smed#这里flag 为什么一定要用[]
else:
dataset["Age"].iloc[i] =age_med#dataset.info()
#Sex数值转换
dataset["Sex"] = dataset["Sex"].replace("male",1)
dataset["Sex"] = dataset["Sex"].replace("female",0)
dataset["Age"]=dataset["Age"].astype(int)
dataset["Age_1"] = dataset["Age"].map(lambda x : 1 if x<=11 else0)
dataset["Age_2"] = dataset["Age"].map(lambda x : 1 if x>11 & x<=18 else0)
dataset["Age_3"] = dataset["Age"].map(lambda x : 1 if x>18 & x<=22 else0)
dataset["Age_4"] = dataset["Age"].map(lambda x : 1 if x>22 & x<=27 else0)
dataset["Age_5"] = dataset["Age"].map(lambda x : 1 if x>27 & x<=33 else0)
dataset["Age_6"] = dataset["Age"].map(lambda x : 1 if x>33 & x<=40 else0)
dataset["Age_7"] = dataset["Age"].map(lambda x : 1 if x>40 else0)
dataset.drop(labels= ["Age"],axis = 1,inplace = True)
2.2 Name
取名字的称呼分别对其分类,以便能将其作为特征代入模型
#name处理#dataset["Name"].sample(10)
name_list = [i.split(",")[1].split(".")[0].strip() for i in dataset["Name"]]#按指定字符将字符串切片为列表
dataset["Title"] =pd.DataFrame(name_list)
g= sns.countplot(dataset["Title"])
g= plt.setp(g.get_xticklabels(),rotation=45)
dataset["Title"] = dataset["Title"].replace(['Lady', 'the Countess','Countess','Capt','Col','Don', 'Major', 'Rev', 'Sir', 'Jonkheer', 'Dona'],"rare")#dataset["Title"] = dataset["Title"].map({"rare":0,"Master":1, "Miss":2, "Ms" : 2 , "Mme":2, "Mlle":2, "Mrs":2, "Mr":3,"Dr":3})
sns.barplot(x = dataset["Title"],y = dataset["Survived"])
dataset["Title"].unique()
dataset.drop(labels= ["Name"],axis = 1 ,inplace =True)#将name列丢掉
dataset = pd.get_dummies(dataset,columns = ["Title"])#将title列给分列
2.3 SibSp Parch
按家庭人数将其分为4个特征
#家人
dataset["Fsize"] = dataset["SibSp"] + dataset["Parch"] + 1sns.barplot(x= dataset["Fsize"],y = dataset["Survived"])
dataset['Single'] = dataset['Fsize'].map(lambda s: 1 if s == 1 else0)
dataset['SmallF'] = dataset['Fsize'].map(lambda s: 1 if s == 2 else0)
dataset['MedF'] = dataset['Fsize'].map(lambda s: 1 if 3 <= s <= 4 else0)
dataset['LargeF'] = dataset['Fsize'].map(lambda s: 1 if s >= 5 else0)#家庭人数为2-3人存活几率更大些
dataset = pd.get_dummies(dataset, columns = ["Fsize"])
2.4
同样将票价分为多个类别
#Fare
dataset["Fare"]=dataset['Fare'].replace(np.nan,dataset["Fare"].median())#dataset["Fare"].describe()
dataset["Fare_1"] = dataset["Fare"].map(lambda x : 1 if x<=7.91 else0)
dataset["Fare_2"] = dataset["Fare"].map(lambda x : 1 if x>7.91 and x<=14.454 else0)
dataset["Fare_3"] = dataset["Fare"].map(lambda x : 1 if x>14.454 and x<=99 else0)
dataset["Fare_4"] = dataset["Fare"].map(lambda x : 1 if x>99 and x<=250 else0)
dataset["Fare_5"] = dataset["Fare"].map(lambda x : 1 if x>250 else0)
dataset.drop(labels= ["Fare"],axis = 1,inplace = True)
3 特征选择
通过逐个测试特征存在与否,观察某个特征有无对准确率的影响,进而筛选出表现好的特征
#特征工程
train =dataset[:train_len]
test=dataset[train_len:]
test.drop(labels=["Survived"],axis = 1,inplace=True)
feature=train.columns.values.tolist()
feature.remove("Survived")
x=np.array(train[feature])
y= np.array(train["Survived"])
from sklearn.model_selection importcross_val_scorefrom sklearn importlinear_model
pre_features=[]
rest_features=feature[:]
best_acc=0while len(rest_features)>0:
temp_best_i= ''temp_best_acc=0for feature_i inrest_features:
temp_features= pre_features +[feature_i,]
x=train[temp_features]
scores= cross_val_score(rf,x,y,cv=5 , scoring='accuracy')
acc=np.mean(scores)if acc >temp_best_acc:
temp_best_acc=acc
temp_best_i=feature_iprint("select",temp_best_i,"acc:",temp_best_acc)if temp_best_acc >best_acc:
best_acc=temp_best_acc
pre_features+=[temp_best_i,]
rest_features.remove(temp_best_i)else:break
#print("best feature set: ",selected_features,"acc: ",best_acc)
print(pre_features)
4 模型融合-投票法
采用多个算法模型进行投票,少数服从多数
from sklearn importlinear_modelfrom sklearn.neighbors importKNeighborsClassifierfrom sklearn.svm importSVCfrom sklearn importtreeknn_est = KNeighborsClassifier(n_neighbors = 2)svm_est = SVC(kernel='linear', C=0.025)dt_est = tree.DecisionTreeClassifier(max_depth=8)ab_est = ensemble.AdaBoostClassifier(n_estimators=500, learning_rate=0.1)rf_est = ensemble.RandomForestClassifier(n_estimators=500, warm_start=True,max_features='sqrt',max_depth=6,min_samples_split=3,min_samples_leaf=2, n_jobs=-1, verbose=0)gbm_est = ensemble.GradientBoostingClassifier(n_estimators=500, learning_rate=0.008,min_samples_split=3, min_samples_leaf=2,max_depth=5, verbose=0)et_est = ensemble.ExtraTreesClassifier(n_estimators=500, n_jobs=-1, max_depth=8, min_samples_leaf=2, verbose=0)lm_set = linear_model.LogisticRegression()voting_est = ensemble.VotingClassifier(estimators = [('rf', rf_est),('gbm', gbm_est),('et', et_est),("lm",lm_set),("dt",dt_est),("ab",ab_est),("knn",knn_est),("svm",svm_est)],voting = 'hard',n_jobs = 50)voting_est.fit(x,y)
from sklearn.model_selection importcross_val_score
scores= cross_val_score(voting_est,x,y,cv = 5,scoring = "accuracy")print(np.mean(scores))
5 预测
预测测试集
voting_est = voting_est.fit(train[pre_features],train["Survived"])
predict_data=voting_est.predict(test[pre_features])
submission= pd.DataFrame({"PassengerId":testId,"Survived":predict_data})
submission.to_csv("G:\\titanic\\voting_est.csv",index = False)
6 总结
最后训练出来的模型在训练集上进行交叉检验准确率平均为85%,但是用来实际预测测试集时准确率仅有76%。回顾整个项目解决流程,特征工程很重要,发掘选择正确的特征才是提高准确度的基础。训练模型是为精髓,自己对这部分知之甚少,本次项目模型调参未涉及,模型融合用了最简单的投票法,stacking、blending等也都未涉及,以及模型是否过拟合或者欠拟合没有进行探讨。最后,需要学习的东西还很多,这已经是个不错的开始。
前途艰辛,仍需砥砺前行!