kaggle--泰坦尼克号宇宙飞船🗺🛰
目的:为了帮助救援人员并找回失踪的乘客,面临的挑战是使用从宇宙飞船损坏的计算机系统中恢复的记录来预测哪些乘客被异常运送。
1.题目
在这场比赛中,你的任务是预测在泰坦尼克号宇宙飞船与时空异常相撞期间,乘客是否被运送到另一个维度,传送回1000年前同名的时空。为了帮助你做出这些预测,你会得到一组从船上损坏的计算机系统中恢复的个人记录。
2.计算机中恢复的个人记录
train.csv - 大约三分之二(~8700)乘客的个人记录,用作训练数据。
test.csv - 剩余三分之一(~4300)乘客的个人记录,用作测试数据。您的任务是预测此集合中乘客的值 Transported
。
PassengerId
- 每位乘客的唯一 ID。每个 ID 的形式都表示 gggg_pp
其中gggg
乘客与一个团体一起旅行, pp
并且是他们在团体中的号码。小组中的人通常是家庭成员,但并非总是如此。
HomePlanet
- 乘客离开的星球,通常是他们的永久居住星球。
CryoSleep
- 指示乘客是否选择在航行期间进入假死状态。处于冷冻睡眠状态的乘客被限制在他们的客舱内。
Cabin
- 乘客所住的舱号。采用 deck/num/side
的形式为 ,其中 side
可以是 P
左舷或 S
右舷。
Destination
- 乘客将要前往的星球。
Age
- 乘客的年龄。
VIP
- 旅客在航程中是否支付了特殊VIP服务费用。
RoomService
, FoodCourt
, , ShoppingMall
Spa
, VRDeck
乘客在泰坦尼克号宇宙飞船的许多豪华设施中的每一个都收取的金额。
Name
- 乘客的名字和姓氏。
开始分析吧,看看我们能够拯救多少人!😎
3.数据预处理
首先导入数据,观察一下数据长什么样子
python
import pandas as pd
df_train = pd.read_csv(r'C:\Users\19313\Desktop\泰坦尼克号宇宙飞船\input_data\train.csv')
df_test = pd.read_csv(r'C:\Users\19313\Desktop\泰坦尼克号宇宙飞船\input_data\test.csv')
print(df_train.info())
ok,通过图片我们可以得到数据特征列以及其对应的数据类型,训练数据有8693行14列。(同样可以知道测试数据为4277行13列)
数据中似乎存在一些缺失值,先留在这里,稍后处理。
再来看几列详细的数据,全面了解一下真实的数据集
python
print(df_train.head())
挺正常的,没什么问题,就是正常的数据。
再看看数据的各项计数指标
python
print(df_train.describe())
通过这个表我们可以知道各个特征的各项指标,具体数据如图所示。
可以知道各项指标的数值差距还是比较大的。
3.1缺失值处理
3.1.1缺失值观察
python
print(df_train.isnull().sum().sort_values(ascending=False))
print(df_test.isnull().sum().sort_values(ascending=False))
训练集的缺失情况:
测试集的缺失情况:
这样看起来缺失值还是很多的,文不如图,画出训练集和测试集的缺失值热力图出来感受一下。
python
plt.figure(figsize = (15, 6))
sns.heatmap(df_train.isnull());
plt.show()
plt.figure(figsize = (15, 6))
sns.heatmap(df_train.isnull());
plt.show()
由图可以观察到,数据缺失的比较稀疏,想不于几千的原始数据,缺失的数据只占一小部分,数据没有达到不可补全需要丢失的地步,接下来我们就来填补缺失数据吧。
3.1.2缺失值补全
缺失值的填充方式会对模型性能产生重大影响。因为特征列的各数据不同,对各数据采取适合各个特征列的值进行补全。
主要运用Excel表查看一下个特征列有哪些数据。
对于年龄,我们通常采用的是平均值进行补全,
对于VIP缺失值,我们默认就是"False",
对于HomePlanet,数据除去空白值还存在Earth,Europa以及Mars。Mars数据个数最少,为了避免数据因个数存在较大差异,将HomePlanet的缺失值全部填充为"Mars"
对于Destination,同上原因,我们将缺失值全部填充为"PS0 J318.5-22"
对于CryoSleep,是否假死休眠,我们默认为填充为"Flase"
对于Cabin,由于船舱数类型过多,随机随机一个船舱补充,选择"A/0/P"
对于其余数据如'RoomService','FoodCourt','ShoppingMall','Spa','VRDeck'全部填充为0。
python
df_train[['RoomService','FoodCourt','ShoppingMall','Spa','VRDeck']] = df_train[['RoomService','FoodCourt','ShoppingMall','Spa','VRDeck']].fillna(0)
df_test[['RoomService','FoodCourt','ShoppingMall','Spa','VRDeck']] = df_test[['RoomService','FoodCourt','ShoppingMall','Spa','VRDeck']].fillna(0)
df_train['Age'] =df_train['Age'].fillna(df_train['Age'].median())
df_test['Age'] =df_test['Age'].fillna(df_test['Age'].median())
df_train['VIP'] =df_train['VIP'].fillna(False)
df_test['VIP'] =df_test['VIP'].fillna(False)
df_train['HomePlanet'] =df_train['HomePlanet'].fillna('Mars')
df_test['HomePlanet'] =df_test['HomePlanet'].fillna('Mars')
df_train['Destination']=df_train['Destination'].fillna("PSO J318.5-22")
df_test['Destination']=df_test['Destination'].fillna("PSO J318.5-22")
df_train['CryoSleep'] =df_train['CryoSleep'].fillna(False)
df_test['CryoSleep'] =df_test['CryoSleep'].fillna(False)
df_train['Cabin'] =df_train['Cabin'].fillna('T/0/P')
df_test['Cabin'] =df_test['Cabin'].fillna('T/0/P')
然后观察一下是否现在还存在缺失值
python
print(df_train.isnull().sum().sort_values(ascending=False))
print(df_test.isnull().sum().sort_values(ascending=False))
说明一下,因为PassengerId是唯一的ID,所以name名字这列特征不是那么重要,完全可以由PassengerID代替她(还有一个原因name与我们之前处理的泰坦尼克沉船不一样,这里的name并没有携带头衔以表示身份,名字仅仅代表某人的代称),故不必处理它。其他的特征列都不存在缺失值啦,缺失值处理完毕。
为了将PassengerID作为唯一值,且不产生数据歧义,将name删除.
python
df_train.drop(['Name'],axis=1,inplace=True)
df_test.drop(['Name'],axis=1,inplace=True)
print(df_train.columns)
print(df_test.columns)
训练集和测试集中都没有"name"列了,"PassengerId"是唯一的key.
4.探索和可视化EDA📊
4.1热力图可视化📈
接下来想看看数据之间的关系,比如通过热力图查看。
首先,先将数据中的string数据类型转换为数值类型。
python
df_train['HomePlanet'] = df_train['HomePlanet'].astype('category').cat.codes
df_train['CryoSleep'] = df_train['CryoSleep'].astype('category').cat.codes
df_train['Cabin'] = df_train['Cabin'].astype('category').cat.codes
df_train['Destination'] = df_train['Destination'].astype('category').cat.codes
df_train['VIP'] = df_train['VIP'].astype('category').cat.codes
df_train['Transported'] = df_train['Transported'].astype('category').cat.codes
此段代码就是将数据中的分类数据类型的数据转换成整数数据类型,比较冗余,还可以通过其他方式实现,自定义一个函数,将分类变量转化为整数编码,然后用apply()函数将自定义函数应用在数据的每一列上。
python
def encode_categorical_column(column):
return column.astype('category').cat.codes
df_train[['HomePlanet', 'CryoSleep', 'Cabin', 'Destination', 'VIP', 'Transported']] = df_train[['HomePlanet', 'CryoSleep', 'Cabin', 'Destination', 'VIP', 'Transported']].apply(encode_categorical_column)
然后,就使用seaborn库的heatmap()函数将数据的协方差通过热力图,表示出来
python
plt.figure(figsize=(15,18))
sns.heatmap(df_train.corr(),cmap='RdBu_r' ,annot=True)
plt.show()
其中数字大于0就是数据的协方差为正相关,反之为负相关。通过观察热力图,似乎看不出数据之间的正相关,似乎负相关的数据列更多。我们继续再通过其他方法,挖掘数据间的相关性。
4.2 Transported可视化探索
4.2.1 Transported存活率
仔细研究一下"Transported"列乘客是否被传送到另一个维度,即乘客是否存活,如果显示"True"即被传送到另外一个维度,传送回1000年前同名的时空,需要救援人员找回失踪的乘客,我需要使用从宇宙飞船损坏的计算机系统中恢复的记录来预测哪些乘客被异常运送,我要改变历史!
被传送和未被传送的乘客差不多,这意味着,两个人之间,有一个人被传送了。
继续研究一个"Transported"与其他特征列存在的关系。
4.2.2 HomePlanet和Transported之间的关系
探索永久居中星球与被传送之间是否存在关系。
python
sns.countplot(x=df_train.HomePlanet,hue=df_train.Transported)
plt.show()
居住在""Europa","Mars"星球上的乘客,被传送的人数大于未被传送的人数,而居住在"Earth"上的乘客,正好相反,这说明永久居住星球对是否被传送存在一定的影响。
4.2.3 CryoSleep和Transported之间的关系
探索永久居中星球与被传送之间是否存在关系。
python
sns.countplot(x=df_train.CryoSleep,hue=df_train.Transported)
plt.show()
通过柱状图可以看出当乘客被冷冻时,被传送的概率远远大于未被冷冻的乘客的概率。
这说明永久冷冻假死状态对是否被传送存在较大的影响。
☠太空旅行,不要冷冻!
4.2.4 VIP和Transported之间的关系
探索永久居中星球与被传送之间是否存在关系。
python
sns.countplot(x=df_train.VIP,hue=df_train.Transported)
plt.show()
VIP乘客很少,被传送的乘客小于未被传送的乘客,而非VIP乘客,被传送的乘客大于未被传送的乘客。
故说明永久VIP对是否被传送存在一定的影响。
4.2.4 Age和Transported之间的关系
探索年龄与被传送之间是否存在关系。
python
sns.countplot(x=df_train.Age,hue=df_train.Transported)
plt.show()
年龄分布太广泛了,图显示不明显,但是还是可以隐约看出来,年龄较小和较大的乘客,橘色的柱状图高于蓝色的柱状图,即更容易被传送,而年龄在青年和中年的乘客,正好相反。
这说明年龄和是否被传送之间存在一定的关系。
4.2.5 Destination和Transported之间的关系
探索传送目的地与被传送之间是否存在关系。
python
sns.countplot(x=df_train.Destination,hue=df_train.Transported)
plt.show()
TRAPPIST-le星球距离地球大约40光年,PSO J318.5-22星球距离地球大约80光年,55 Cancri e星球距离地球大约41光年。
距离地球大约40光年的TRAPPIST-le星球未被传送的乘客大于被传送的乘客,而距离地球大约41光年的55 Cancri e星球被传送的乘客大于未被传送的乘客。
这说明目的地星球和是否被传送之间存在一定的关系。
4.2.6 Cabin和Transported之间的关系
探索传送船舱与被传送之间是否存在关系。
Cabin
- 乘客所住的舱号。采用 deck/num/side
的形式为 ,其中 side
可以是 P
左舷或 S
右舷。
因为船舱的数据是具体的,即几乎每个人拥有一个船舱的具体休息仓,如果进行数据分析,会因数据过于分散而得不到较好的结果,但是又发现船上分为几个甲板分区区域,于是先将数据进行分离,分为deck、num、side三部分。
python
df_train[['Deck','Num','Side']] = df_train.Cabin.str.split('/',expand=True)
df_test[['Deck','Num','Side']] = df_test.Cabin.str.split('/',expand=True)
print(df_train.Deck.value_counts())
船舱的甲板具体分为A,B,C,D,E,F,G,T等8个区,每个区具体人数如下:
4.2.6.1 Deck和Transported之间的关系
看看各个分区与被传送之间是否存在关系。在得到结果之间,盲猜一下,我觉得应该是存在较大关系的。
python
sns.countplot(x=df_train.Deck,hue=df_train.Transported)
plt.show()
根据图观察得到,住在B、C、G甲板的乘客,被传送的乘客大于未被传送的乘客,对B、C区尤其明显。住在F、E、D区的乘客,未被传送的乘客大于被传送的乘客,对F、E区尤其明显。
之前我们填补的cabin数据,选择的T区的乘客,并未观察有明显异常的表现,这也比较符合我们的预测,填补值尽量不要影响原始数据的分布。
这说明在飞船的甲板上和是否被传送之间存在一定的关系。
4.2.6.2 Num和Transported之间的关系
继续观察一下甲板的数量,其实我觉得应该没有关系,数据应该很多,也不分析不出来,但是既然分析了,就一个变量也不要放过还是分析一下。
python
sns.countplot(x=df_train.Num,hue=df_train.Transported)
plt.show()
数据很多,但是还是依稀得出,数量少的船舱的橘色柱状图高于蓝色,即数量少的船舱的被传送的乘客大于未被传送的乘客。
我暂时不能得出船舱的数量与是否被传送之间存在关系。
4.2.6.3 Side和Transported之间的关系
继续观察一下船舱的左右舷是否与被传送之间存在关系。
python
sns.countplot(x=df_train.Side,hue=df_train.Transported)
plt.show()
通过图形,很明显可以看出处于P(左旋)比S(右舷)的乘客更不容易被传送。
4.3 数值型变量之间的数值分布关系
首先看一下'HomePlanet', 'CryoSleep', 'Destination', 'Age','VIP'5个变量的详细情况,数据比较集中,做出来的图可能不是那么好看。
python
print(df_train[['HomePlanet', 'CryoSleep', 'Destination', 'Age','VIP']].describe())
python
numerical_df_train = df_train[['HomePlanet', 'CryoSleep', 'Destination', 'Age','VIP','Transported']]
sns.pairplot(data=numerical_df_train, hue='Transported')
plt.show()
还是数据分布的比较集中了,图片有点杂乱,不过,没有关系,集中的数据才好进行模型预测。
5 数学建模
5.1 为什么要使用决策树模型
决策树模型在预测泰坦尼克号宇宙飞船与时空异常相撞期间乘客是否被运送到另一个维度方面表现出色,该模型具备可解释性,能够处理数据中的非线性关系,并对不相关的特征具有鲁棒性。
此外,决策树还适应混合数据类型,使其适用于个人记录属性。其简单性使得实现和可视化变得轻松,同时可以通过集成技术进一步提升其准确性。
5.2 数据预测的前期准备
首先将数据分为训练和测试数据的特征集合,标签集合,以及对XGBoost的参数使用网格法进行调参。
python
y_train = df_train['Transported']
X_train = df_train.drop(['Transported','PassengerId'],axis=1)
X_val = df_test.drop(['PassengerId'],axis=1)
# 网格搜索参数设置
param_grid = {
'eta': [0.01, 0.1], # Reduced to two common learning rates
'n_estimators': [32, 128, 120, 100], # Reduced to two values
'max_depth': [3, 6, 8, 16], # Reduced depth choices
'reg_lambda': [1, 10],
'subsample': [0.5, 1.0], # Only take extreme values
'min_child_weight': [1, 10], # Only take extreme values
'colsample_bytree': [0.5, 1.0], # Only take extreme values
'objective': ['binary:logistic']
}
5.3 XGBoost进行数据预测🌳
使用XGBClassifier分类器对"Transported"进行模型训练,打印出最佳交叉验证分数。
python
model = XGBClassifier()
kfold = KFold(n_splits=5, shuffle=True, random_state=42)
grid_search = GridSearchCV(model, param_grid, cv=kfold, scoring='accuracy', verbose=1, n_jobs=-1,return_train_score=True)
grid_search.fit(X_train, y_train)
print("Best parameters found: ", grid_search.best_params_)
# # 最佳交叉验证分数
print("Best cross-validation score: {:.2f}".format(grid_search.best_score_))
train_scores_mean = grid_search.cv_results_["mean_train_score"]
test_scores_mean = grid_search.cv_results_["mean_test_score"]
使用最佳参数进行预测。
最佳参数为:
Best parameters found: {'colsample_bytree': 0.5, 'eta': 0.1, 'max_depth': 6, 'min_child_weight': 10, 'n_estimators': 100, 'objective': 'binary:logistic', 'reg_lambda': 10, 'subsample': 0.5}
python
best_model = grid_search.best_estimator_
predictions = best_model.predict(X_val)
print(predictions)
提交预测的数据
python
submission_df = pd.DataFrame({
'PassengerId': df_test['PassengerId'],
'Transported': predictions
})
predict_file_path =r'C:\Users\19313\Desktop\泰坦尼克号宇宙飞船\out_data/predict.csv'
submission_df.to_csv(predict_file_path, index=False)
5 提交得分啦🥰
这次一次救了4277*0.69417=2972人,太棒啦!