泰坦尼克号生存预测(python)
1 探索性分析
对数据进行一个整体的理解
1.1 查看数据都有一些什么特征
将测试集和训练集合并,方便后续的数据处理
import pandas as pd import seaborn as sns import numpy as np import matplotlib.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"] + 1 sns.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 in list(dataset.Ticket): if not i.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 in index_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 not np.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 else 0) dataset["Age_2"] = dataset["Age"].map(lambda x : 1 if x>11 & x<=18 else 0) dataset["Age_3"] = dataset["Age"].map(lambda x : 1 if x>18 & x<=22 else 0) dataset["Age_4"] = dataset["Age"].map(lambda x : 1 if x>22 & x<=27 else 0) dataset["Age_5"] = dataset["Age"].map(lambda x : 1 if x>27 & x<=33 else 0) dataset["Age_6"] = dataset["Age"].map(lambda x : 1 if x>33 & x<=40 else 0) dataset["Age_7"] = dataset["Age"].map(lambda x : 1 if x>40 else 0) 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"] + 1 sns.barplot(x = dataset["Fsize"],y = dataset["Survived"]) dataset['Single'] = dataset['Fsize'].map(lambda s: 1 if s == 1 else 0) dataset['SmallF'] = dataset['Fsize'].map(lambda s: 1 if s == 2 else 0) dataset['MedF'] = dataset['Fsize'].map(lambda s: 1 if 3 <= s <= 4 else 0) dataset['LargeF'] = dataset['Fsize'].map(lambda s: 1 if s >= 5 else 0) #家庭人数为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 else 0) dataset["Fare_2"] = dataset["Fare"].map(lambda x : 1 if x>7.91 and x<=14.454 else 0) dataset["Fare_3"] = dataset["Fare"].map(lambda x : 1 if x>14.454 and x<=99 else 0) dataset["Fare_4"] = dataset["Fare"].map(lambda x : 1 if x>99 and x<=250 else 0) dataset["Fare_5"] = dataset["Fare"].map(lambda x : 1 if x>250 else 0) 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 import cross_val_score from sklearn import linear_model pre_features = [] rest_features = feature[:] best_acc = 0 while len(rest_features)>0: temp_best_i = '' temp_best_acc = 0 for feature_i in rest_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_i print("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 import linear_model from sklearn.neighbors import KNeighborsClassifier from sklearn.svm import SVC from sklearn import tree knn_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 import cross_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等也都未涉及,以及模型是否过拟合或者欠拟合没有进行探讨。最后,需要学习的东西还很多,这已经是个不错的开始。
前途艰辛,仍需砥砺前行!
浙公网安备 33010602011771号