赞
踩
最近在学习机器学习,想要把学习到的知识运用到实践中,找了很多地方终决定使用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]) # 获取测试集的id passenger_id = test["PassengerId"]
我们简单的打印一个数据详情和一部分数据,检查数据的基本情况。我们可以看到这组数据一共有12个特征,其中Age特征缺失263条数据,Fare缺失一条数据,Cabin缺失1014条数据,Embarked缺失2条数据,整个数据集有1309条数据,其中891条是训练集,418条是训练集。
# 特征含义: # PassengerId(ID) # Survived (标志是否存活) # Pclass(客舱等级) # Name(乘客姓名) # Sex(性别) # Age(年龄) # SibSp(兄弟姐妹数/配偶数) # Parch(父母数/子女数) # Ticket(船票编号) # Fare(票价) # Cabin(客舱编号) # Embarked(上船的港口) print(all_data.info()) print(all_data.head()) print(all_data.isnull().sum())
我们接下来的任务需要把数据中缺失的部分用各种方式补全,竟可能的让数据完整统一,看上去更清晰。
首选把性别换位数字
# 把男性设置成 1 女性设置成 0
all_data["Sex"] = all_data["Sex"].apply(lambda x: 0 if x == "female" else 1)
接下来从缺失最多的Cabin下手,我们以重新创建一个特征的方式来补全Cabin。这里使用编号的第一个字母来做为甲板号,缺失的定义为U(unknown),修改完查看每个甲板号的生存率。
# 补全客舱编号特征,此处用Deck来替代客舱编号,编号为空的用unknown U 来替代
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()) # print(all_data["Title"].value_counts()) 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
最后两个根据特征对应的中位数来补全数据
# 补全Fare 与 Embarked
# 查看那一条Fare为空的数据, 根据上船的港口和仓位来查看价格
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)
# 查看那两条Embarked为空的数据
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)
根据相关性矩阵图初步确认了有关特征为 甲板号(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。感谢阅读,祝你好运。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。