边境的悍匪—Kaggle—泰坦尼克号生还预测详细教程
前言
最近在学习机器学习,想要把学习到的知识运用到实践中,找了很多地方终决定使用kaggle上的数据集。文章主要描述第一次加入到kaggle中完整的完成对泰坦尼克号数据挖掘、分析到预测,最后上传到平台获取分数的一个过程。
该项目是博主的第一个kaggle项目,记录下来用作学习和分享,全文有任何疑点欢迎立刻联系并指出我的错误,不喜勿喷。
项目介绍
泰坦尼克号的沉没是历史上最臭名昭著的沉船之一。 1912年4月15日,人们普遍认为“永不沉没”的皇家邮轮“泰坦尼克”号在处女航中撞上冰山后沉没。不幸的是,船上没有足够的救生艇供所有人使用,导致2224名乘客和船员中有1502人死亡。 虽然生存中有一些运气因素,但似乎有些人比其他人更有可能生存下来。 在这个挑战中,我们要求你建立一个预测模型来回答这个问题:“什么样的人更有可能活下来?”使用乘客数据(如姓名、年龄、性别、社会经济阶层等)。
前期准备
图中红框部分就是泰塔尼克号项目,点击进入即可参加。 点击data获取下载分析数据。
加载数据
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestRegressor, RandomForestClassifier
from sklearn.pipeline import Pipeline, make_pipeline
from sklearn.model_selection import GridSearchCV, cross_val_score
from sklearn.feature_selection import SelectKBest
train = pd.read_csv("./data/train.csv")
test = pd.read_csv("./data/test.csv")
all_data = pd.concat([train, test])
passenger_id = test["PassengerId"]
初步分析
我们简单的打印一个数据详情和一部分数据,检查数据的基本情况。我们可以看到这组数据一共有12个特征,其中Age特征缺失263条数据,Fare缺失一条数据,Cabin缺失1014条数据,Embarked缺失2条数据,整个数据集有1309条数据,其中891条是训练集,418条是训练集。
print(all_data.info())
print(all_data.head())
print(all_data.isnull().sum())
数据清洗
我们接下来的任务需要把数据中缺失的部分用各种方式补全,竟可能的让数据完整统一,看上去更清晰。 首选把性别换位数字
all_data["Sex"] = all_data["Sex"].apply(lambda x: 0 if x == "female" else 1)
接下来从缺失最多的Cabin下手,我们以重新创建一个特征的方式来补全Cabin。这里使用编号的第一个字母来做为甲板号,缺失的定义为U(unknown),修改完查看每个甲板号的生存率。
all_data["Deck"] = all_data["Cabin"].apply(lambda x: "U" if x is np.nan else str(x)[0])
sns.barplot(x="Deck", y="Survived", data=all_data)
plt.show()
然后把甲板号改为数字
deck_dict = {"U": 1, "C": 2, "B": 3, "D": 4, "E": 5, "A": 6, "F": 7, "G": 8, "T": 9}
all_data["Deck"] = all_data["Deck"].map(deck_dict)
补全年龄需要根据名字中的特殊标识分类人们的标签,有的是男士有的是妇女有的是士官。完成之后选择年龄(Age),标签(Title),船舱等级(Pclass)性别(Sex)特征来使用RandomForestRegressor训练预测填充缺失值。
all_data["Title"] = all_data["Name"].apply(lambda x: x.split(",")[1].split(".")[0].strip())
title_dict = {}
title_dict.update(dict.fromkeys(['Capt', 'Col', 'Major', 'Dr', 'Rev'], "Officer"))
title_dict.update(dict.fromkeys(['Don', 'Sir', 'the Countess', 'Dona', 'Lady'], "Royalty"))
title_dict.update(dict.fromkeys(['Mme', 'Ms', 'Mrs'], 'Mrs'))
title_dict.update(dict.fromkeys(['Mlle', 'Miss'], 'Miss'))
title_dict.update(dict.fromkeys(['Mr'], 'Mr'))
title_dict.update(dict.fromkeys(['Jonkheer', "Master"], 'Master'))
all_data["Title"] = all_data["Title"].map(title_dict)
replenish_age = all_data[["Age", "Title", "Pclass", "Sex"]]
replenish_age = pd.get_dummies(replenish_age)
unknown_age = replenish_age[replenish_age["Age"].isnull()].values
known_age = replenish_age[replenish_age["Age"].notnull()].values
X = known_age[:, 1:]
y = known_age[:, 0]
rfr = RandomForestRegressor(n_estimators=100, random_state=0, n_jobs=1)
rfr.fit(X, y)
predict_age = rfr.predict(unknown_age[:, 1:])
all_data.loc[(all_data["Age"].isnull()), "Age"] = predict_age
最后两个根据特征对应的中位数来补全数据
print(all_data.loc[(all_data["Fare"].isnull())])
fare = all_data.loc[(all_data["Pclass"] == 3) & (all_data["Embarked"] == "S")].Fare.median()
all_data["Fare"] = all_data["Fare"].fillna(fare)
print(all_data[["PassengerId", "Pclass", "Fare", "Embarked"]].loc[(all_data["Embarked"].isnull())])
print(all_data.groupby(["Pclass", "Embarked"])["Fare"].median())
all_data.loc[(all_data["Embarked"].isnull()), "Embarked"] = "C"
至此,所有数据清理工作完成。
数据分析
sns.heatmap(all_data.corr(), cmap="Blues", vmax=1, annot=True)
plt.show()
根据相关性矩阵图初步确认了有关特征为 甲板号(Deck) 票价(Fare) 性别(Sex) 船舱等级(Pclass),后续就是逐个验证每个特征与存活的关系,会有很多图片这里就不一一呈现。
sns.barplot(x="Deck", y="Survived", data=all_data)
plt.show()
face = sns.FacetGrid(all_data, hue="Survived", aspect=2)
face.map(sns.kdeplot, "Fare", shade=True)
face.set(xlim=(0, 100))
face.add_legend()
plt.xlabel("Fare")
plt.ylabel("density")
plt.show()
sns.barplot(x="Sex", y="Survived", data=all_data)
plt.show()
sns.barplot(x="Pclass", y="Survived", data=all_data)
plt.show()
face = sns.FacetGrid(all_data, hue="Survived", aspect=2)
face.map(sns.kdeplot, "Age", shade=True)
face.set(xlim=(0, train["Age"].max()))
face.add_legend()
plt.xlabel("Age")
plt.ylabel("density")
plt.show()
sns.barplot(x="SibSp", y="Survived", data=all_data)
plt.show()
sns.barplot(x="Parch", y="Survived", data=all_data)
plt.show()
特征转换
在验证了已有特征完成后我还需要生成一些新的特征或者是对原有的特征做一些转换。后续代码主要完成了以下几个事情:
- 验证船票编号相同数和生存率的关系。
- 生成一个姓的特征家庭数量并对家庭数量进行分类。
- 对年龄、票价根据上一步分析的结果进行分类。
特征转换并验证特征与存活的关系
ticket_dict = dict(all_data["Ticket"].value_counts())
all_data["TicketList"] = all_data["Ticket"].apply(lambda x: ticket_dict[x])
print(all_data["TicketList"].value_counts())
sns.barplot(x="TicketList", y="Survived", data=all_data)
plt.show()
all_data["FamilySize"] = all_data["SibSp"] + all_data["Parch"] + 1
print(all_data["FamilySize"].value_counts())
sns.barplot(x="FamilySize", y="Survived", data=all_data)
plt.show()
def family_size_class(num):
if 2 <= num <= 4:
return 1
elif num == 1 or 4 < num <= 8:
return 2
elif num > 8:
return 3
all_data["FamilySizeClass"] = all_data["FamilySize"].apply(family_size_class)
sns.barplot(x="FamilySizeClass", y="Survived", data=all_data)
plt.show()
def fare_class(f):
if f <= 30:
return 1
elif 30 <= f <= 200:
return 2
elif f > 200:
return 3
all_data["FareClass"] = all_data["Fare"].apply(fare_class)
print(all_data["FareClass"].value_counts())
sns.barplot(x="FareClass", y="Survived", data=all_data)
plt.show()
def age_class(a):
if a <= 20:
return 1
elif 20 < a < 35:
return 2
elif a >= 35:
return 3
all_data["AgeClass"] = all_data["Age"].apply(age_class)
print(all_data["AgeClass"].value_counts())
sns.barplot(x="AgeClass", y="Survived", data=all_data)
plt.show()
建模与调优
all_data = all_data[["Survived", "Pclass", "Sex", "Embarked", "Deck", "Title", "TicketList",
"FamilySizeClass", "FareClass", "AgeClass"]]
all_data = pd.get_dummies(all_data)
train = all_data[all_data["Survived"].notnull()].values
test = all_data[all_data["Survived"].isnull()].drop("Survived", axis=1).values
X = train[:, 1:]
y = train[:, 0]
pipe = Pipeline([("select", SelectKBest()),
("classify", RandomForestClassifier(random_state=7, max_features="sqrt"))])
param_test = {"classify__n_estimators": list(range(20, 50, 2)),
"classify__max_depth": list(range(3, 60, 3))}
gsc = GridSearchCV(estimator=pipe, param_grid=param_test, scoring="roc_auc", cv=10)
gsc.fit(X, y)
print(gsc.best_params_, gsc.best_score_)
模型训练与预测
select = SelectKBest()
rfc = RandomForestClassifier(random_state=7, max_features="sqrt", n_estimators=42, max_depth=6, warm_start=True)
pipe = make_pipeline(select, rfc)
pipe.fit(X, y)
cv_score = cross_val_score(pipe, X, y, cv=10)
print(f"CV Score : Mean - {np.mean(cv_score)} | Std - {np.std(cv_score)}")
predictions = pipe.predict(test)
submission = pd.DataFrame({"PassengerId": passenger_id, "Survived": predictions.astype(np.int32)})
submission.to_csv("./data/submission_v0.1.csv", index=False)
总结
最后把生成的csv上传到kaggle即可,泰坦尼克号已经有很多很多的队伍参加,实现的方法大同小异,这个算法最后的结果也并没有达到很完美,还需要很多的改进。如果你有什么好的思路或者是想要和博主一起学习机器学习,可以与我取得联系。邮箱2579779624@qq.com。感谢阅读,祝你好运。
|