美文网首页
kaggle之泰坦尼克之灾

kaggle之泰坦尼克之灾

作者: 道末端 | 来源:发表于2020-04-13 01:28 被阅读0次

项目介绍

基于kaggle提供的泰坦尼克之灾数据,使用python与sklearn机器学习模块,预测乘客的存活状况

kaggle竞赛之泰坦尼克之灾作为数据挖掘入门的第一个实战作品,第一次提交15000名开外,目前最好成绩1811,排名10%。
第一提交:


kaggle rank.png

目前最好成绩:


1811.png

数据处理大概流程

  1. 数据清洗(Data Cleaning)
  2. 探索性可视化(Exploratory Visualization)
  3. 特征工程(Feature Engineering)
  4. 基本建模&评估(Basic Modeling& Evaluation)
  5. 参数调整(Hyperparameters Tuning)
  6. 集成方法(EnsembleMethods)
    本文章将4、6步骤合成一个

1、数据清洗(Data Cleaning)

import seaborn as sns
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import warnings
warnings.filterwarnings('ignore') # 去掉警告
plt.rc('font', family='SimHei', size=13)  #用于解决中文显示不了的问题

train_df = pd.read_csv('./train.csv')
test_df = pd.read_csv('./test.csv')

整体观看数据

image.png
# 查看是否有缺失值、异常值与数据类型,为后面数据预处理做准备
train_df.info()
test_df.info()
image.png

训练集的age、Cabin、Embarked有空值
测试集的age、fare、Cabin有空值

单因子分析

train_df.describe()
image.png
  • 训练集实际乘客人数(891)是总样本泰坦尼克号(2,224,这个数值kaggle官网获得)上的40%。
  • Survived是一个具有0或1值的分类特征, 约38%的样本存活
  • SibS中位数为0说明一半以上的乘客是没有堂兄弟/妹或配偶陪同的
  • Parch的四分位数表明>75的乘客没有和父母或孩子一起出行
  • Fare平均票值mean为32,四分位数的上边缘为31,说明75%左右的乘客票价不高
  • Age >75的乘客年龄在38岁之内
  • Pclass 大多数乘客乘坐的是二、三等舱
train_df.describe(include=['O']) #参数O代表只列出object类型的列
image.png
  • Survived 是存活情况,为预测标记特征;剩下的10个是原始特征数据。
  • PassengerId 是数据唯一序号,无意义;
  • age数据为714条,总样本有891条,缺失接近20%,不考虑直接使用众数、中位数进行替代,使用随机森林拟合缺失的数据
  • Ticket unique 为681,离散程度太高,考虑直接drop掉(后面发现这个同一个Ticket 是同一个船舱,不能drop)
  • Cabin 缺失太严重,不具有参考意义,考虑直接drop掉
  • Embarked 使用中位数填充
  • Names 在整个数据集中是唯一的(count = unique = 891),由于Names可能含有社会地位等信息,需要做特征衍生处理

数据预处理

# 标注
Y_train = train_df["Survived"]
X_train = train_df.drop(["Cabin"], axis=1)

X_test = test_df.drop(["Cabin"], axis=1)

# 对缺失的Embarked Fare以众数来填补
X_train.loc[(X_train.Embarked.isnull()),"Embarked"] = X_train["Embarked"].mode().values
X_test.loc[(X_test.Fare.isnull()),"Fare"] = X_test["Fare"].mode().values

X_train.info()
X_test.info()
# 使用随机森林拟合age缺失的数据 

#RandomForest在原始数据中做不同采样,建立多颗DecisionTree,再进行average等来降低过拟合现象,提高结果的机器学习算法

from sklearn.ensemble import RandomForestRegressor
def set_missing_ages(df):
#     print(df)
    # 把已有的数值型特征取出来丢进Random Forest Regressor中
    age_df = df[['Age','Fare', 'Parch', 'SibSp', 'Pclass']]
    # 乘客分成已知年龄和未知年龄两部分
    known_age = age_df[age_df.Age.notnull()].values
    unknown_age = age_df[age_df.Age.isnull()].values
  
    
    # y即目标年龄
    y = known_age[:, 0]

    # X即特征属性值
    X = known_age[:, 1:]
    # fit到RandomForestRegressor之中 n_estimators 森林中决策树的个数 n_jobs = -1 使用所有核
    rfr = RandomForestRegressor(random_state=0, n_estimators=2000, n_jobs=-1)
    rfr.fit(X, y)
    
    # 用得到的模型进行未知年龄结果预测
    predictedAges = rfr.predict(unknown_age[:, 1:])
    df.loc[(df.Age.isnull()), 'Age' ] = predictedAges 
    return df, rfr
X_train, rfr1 = set_missing_ages(X_train)  
X_test, rfr2 = set_missing_ages(X_test)  

2、探索性可视化

f, [ax1,ax2,ax3] = plt.subplots(1,3,figsize=(20,5))
# sns.countplot(x='Sex', hue='Survived', data=X_train, ax=ax1)

sns.barplot(x="Pclass",y="Survived",data=X_train, ax=ax1)
ax1.set_title("Pclass vs Survived")
sns.barplot(x="Sex",y="Survived",data=X_train, ax=ax2)
ax2.set_title('Sex vs Survived')
sns.violinplot("Sex","Age", hue="Survived", data=X_train,split=True, ax=ax3)
ax3.set_title('Sex and Age vs Survived')
ax3.set_yticks(range(0,110,10))

f, [ax1,ax2] = plt.subplots(1,2,figsize=(20,5))
sns.violinplot("Pclass","Age", hue="Survived", data=X_train,split=True, ax=ax1)
ax1.set_title('Pclass and Age vs Survived')
ax1.set_yticks(range(0,110,10))

sns.barplot(x="SibSp",y="Survived",data=X_train, ax=ax2)
ax2.set_title('SibSp vs Survived')

f, [ax1,ax2] = plt.subplots(1,2,figsize=(20,5))
sns.barplot(x="Parch",y="Survived",data=X_train, ax= ax1)
ax1.set_title('Parch vs Survived')

sns.boxplot(x="Pclass",y="Fare",data=X_train, ax= ax2)
ax2.set_title('Parch vs Survived')

plt.show()
plt.savefig('grid.jpg')![grid.jpg]
image.png
image.png
  • 优先救女性与小孩
  • 船舱等级越高 存活率越高
  • 20-40区间的存活率最高
  • 一等舱中年的存活率明显比2、3等仓的存活率高
  • 有1或2个 堂兄弟/妹或配偶的存活率较高高
  • 有1至3个父母或小孩个数的存活率较高
  • 一等舱的票价都比较高 二等舱次之 三等舱最便宜
# 探索船舱等级的男女存活率
X_train[['Sex','Pclass','Survived']].groupby(['Pclass','Sex']).mean().plot.bar()
image.png

不难看出 基本符合女性优先的原则, 但是不同船舱等级的男女存活率是有区别的 1等仓的男性率几乎是2、3等仓的2倍

# 探索Embarked与存活的关系
sns.countplot('Embarked',hue='Survived',data=X_train)
plt.title('Embarked and Survived')
plt.savefig('探索Embarked与存活的关系.jpg')
plt.show()
image.png

s港口的存活数最多 样本数也是最多的

# kde分布
f,ax = plt.subplots(figsize=(10,5))
sns.kdeplot(X_train.loc[(X_train['Survived'] == 0),'Age'] , color='gray',shade=True,label='not survived')
sns.kdeplot(X_train.loc[(X_train['Survived'] == 1),'Age'] , color='g',shade=True, label='survived')
plt.title('Age特征分布 - Surviver V.S. Not Survivors', fontsize = 15)
plt.xlabel("Age", fontsize = 15)
plt.ylabel('Frequency', fontsize = 15)
plt.savefig('Age特征分布.jpg')
plt.show()
image.png

Survived与Not Survived特征分布的主要区别在 0 ~15左右。
小于15岁以下的乘客(也就是孩子)获救率相对于较高,而大于15岁的乘客分布无明显区别

Ticket_Count = dict(train_df['Ticket'].value_counts())
train_df['TicketGroup'] = train_df['Ticket'].apply(lambda x:Ticket_Count[x])
sns.barplot(x='TicketGroup', y='Survived', data=train_df)
plt.savefig('共票号的乘客幸存率.jpg')
plt.show()
image.png

与2至4人共票号的乘客幸存率较高

train_df['FamilySize']=train_df['SibSp']+train_df['Parch']+1
sns.barplot(x="FamilySize", y="Survived", data=train_df)
plt.savefig('家庭人数幸存率.jpg')
plt.show()
image.png

家庭人数为2到4的乘客幸存率较高

结论

  • 基本上是女人和儿童优先,一等舱是社会上社会财富比较多的人所在的舱,
  • 一等级的舱乘客年龄相对要比其他两个等级的舱乘客年龄都要大,符合实际,有钱人一般年龄偏大,掌握社会财富较多,低位也相对较高
  • 孤身一人与人数较多的小团队的存活率较低,猜测若是有配偶小孩等,有人会选择让另外的人活下去;人数较多会导致生存机会出让困难

3、 特征工程

特征衍生

import re
label = X_train["Survived"]
X_train = X_train.drop("Survived",axis=1)

# 根据称呼来建立一个新特征
def get_title(name):
    title_search = re.search('([A-Za-z]+)\.', name)

    if title_search:
        return title_search.group(1)
    return ""


def get_titles(df):
    
    titles = df["Name"].apply(get_title)

    # 将称号转换成数值表示
    title_mapping = {"Mr": 1, "Miss": 2, "Mrs": 3, "Master": 4, "Dr": 5, "Rev": 6, "Major": 7, "Col": 8, "Mlle": 9,
                     "Mme": 10, "Don": 11, "Lady": 12, "Countess": 13, "Jonkheer": 14, "Sir": 15, "Capt": 16, "Ms": 17,
                     "Dona": 18
                     }

    for k, v in title_mapping.items():
        titles[titles == k] = v
        
    return titles

def Ticket_Label(s):
    if (s >= 2) & (s <= 4):
        return 2
    elif ((s > 4) & (s <= 8)) | (s == 1):
        return 1
    elif (s > 8):
        return 0

def Fam_label(s):
    if (s >= 2) & (s <= 4):
        return 2
    elif ((s > 4) & (s <= 7)) | (s == 1):
        return 1
    elif (s > 7):
        return 0

# 训练集    
# 添加Title特征
titles = get_titles(X_train)
# print(pd.value_counts(titles))
X_train["Title"] = titles
X_train["FamilySize"] = X_train["SibSp"] + X_train["Parch"] + 1
X_train['FamilyLabel']=X_train['FamilySize'].apply(Fam_label)

X_train["NameLength"] = X_train["Name"].apply(lambda x: len(x))

Ticket_Count = dict(X_train['Ticket'].value_counts())
X_train['TicketGroup'] = X_train['Ticket'].apply(lambda x:Ticket_Count[x])    
X_train['TicketGroup'] = X_train['TicketGroup'].apply(Ticket_Label)    

# 测试集
# 添加Title特征
titles = get_titles(X_test)
# print(pd.value_counts(titles))
X_test["Title"] = titles 
X_test["FamilySize"] = X_test["SibSp"] + X_test["Parch"] + 1
X_test['FamilyLabel']=X_test['FamilySize'].apply(Fam_label)

X_test["NameLength"] = X_test["Name"].apply(lambda x: len(x))   

Ticket_Count = dict(X_test['Ticket'].value_counts())
X_test['TicketGroup'] = X_test['Ticket'].apply(lambda x:Ticket_Count[x])    
X_test['TicketGroup'] = X_test['TicketGroup'].apply(Ticket_Label)    
    
X_train = X_train.drop(["Name","Ticket"], axis=1)    
X_test = X_test.drop(["Name","Ticket"], axis=1)
    
X_test.head()

image.png

特征变换

from sklearn.feature_selection import SelectKBest, f_classif    
from sklearn.preprocessing import MinMaxScaler, StandardScaler
from sklearn.preprocessing import LabelEncoder, OneHotEncoder
# Pclass Sex Embarked 离散化 ont hot编码 或 标签化
# Age SibSp Parch Fare 标准化 或归一化(若使用逻辑回归)梯度下降算法 参数之间差距太大会导致步长过长 会严重影响收敛速度 甚至不收敛
# age    : Age        --- False: MinMaxScaler True:StandardScaler
# ss     : SibSp      --- False: MinMaxScaler True:StandardScaler
# ph     : Parch      --- False: MinMaxScaler True:StandardScaler
# fare   : Fare       --- False: MinMaxScaler True:StandardScaler
# pc     : Pclass     --- False: LabelEncoder True:OneHotEncoder
# sex    : Sex        --- False: LabelEncoder True:OneHotEncoder
# ed     : Embarked   --- False: LabelEncoder True:OneHotEncoder

def data_preprocessing(df,age=False,ss=False,ph=False,fare=False,pc=True,sex=True,ed=True):
    scaler_lst = [age,ss,ph,fare]
    column_lst = ["Age","SibSp","Parch","Fare"]
    for i in range(len(scaler_lst)):
        if not scaler_lst[i]:
            # fit_transform reshape(-1,1)是任意行数1列
            df[column_lst[i]+"_scaled"] = StandardScaler().fit_transform(df[column_lst[i]].values.reshape(-1,1)).reshape(1, -1)[0]
        else:
            df[column_lst[i]+"_scaled"] = MinMaxScaler().fit_transform(df[column_lst[i]].values.reshape(-1,1)).reshape(1, -1)[0]
    scaler_lst = [pc,sex,ed]
    column_lst = ["Pclass","Sex","Embarked"]  
    for i in range(len(scaler_lst)):
        if not scaler_lst[i]:
            print("LabelEncoder")
            df[column_lst[i]] = LabelEncoder().fit_transform(df[column_lst[i]])
            # 标签化后进行归一化或者标准化处理
            df[column_lst[i]] = MinMaxScaler().fit_transform(df[column_lst[i]].values.reshape(-1,1)).reshape(1, -1)[0]
        else:
            df =  df.join(pd.get_dummies(df[column_lst[i]],prefix= column_lst[i]))
            df = df.drop(column_lst[i], axis=1)
    return df

X_train = data_preprocessing(X_train)
X_test = data_preprocessing(X_test)
selector = SelectKBest(f_classif, k=5) # 方差分析,计算方差分析(ANOVA)的F值 (组间均方 / 组内均方),选取前5个特征
selector.fit(X_train, label)

scores = -np.log10(selector.pvalues_)

xticks = X_train.columns.values

# 画图看各个特征的重要程度
plt.bar(range(len(xticks)), scores)
plt.xticks(range(len(xticks)), xticks, rotation='vertical')
plt.show()
plt.savefig('SelectKBest.jpg')
plt.show()
image.png

查看特征相关性

5、参数调整

略过

6、 集成方法

  rf_est = RandomForestClassifier(n_estimators = 100, criterion = 'gini', max_features = 'sqrt',
                                     max_depth = 3, min_samples_split = 4, min_samples_leaf = 2,
                                     n_jobs = -1, random_state = 42, verbose = 1)
gbm_est = GradientBoostingClassifier(n_estimators=400, learning_rate=0.0008, loss='exponential',
                                              min_samples_split=3, min_samples_leaf=2, max_features='sqrt',
                                              max_depth=3, random_state=42, verbose=1)
et_est = ensemble.ExtraTreesClassifier(n_estimators=100, max_features='sqrt', max_depth=35, n_jobs=50,
                                       criterion='entropy', random_state=42, verbose=1)

vote_est1 = [
    ('rf', rf_est),('gbm', gbm_est),('et', et_est)
]

vote_clf = ensemble.VotingClassifier(estimators = vote_est1,
                                       voting = 'soft', 
                                       weights = [3,5,2],
                                       n_jobs = 50)
vote_clf.fit(X_train,Y_train)
predict = vote_clf.predict(X_test)

集成随机森林、GBDT、极限树最后用VotingClassifier进行投票

submission = pd.DataFrame({'PassengerId':test_df['PassengerId'],
                           'Survived':predict})
submission.to_csv('submission_result.csv',index=False,sep=',')

导出csv文件上传到kaggle获得排名

个人心得体会

第一次完整的跑完一个流程才知道为什么有人说"只要特征足够好,模型随便跑"。由于经验不足,第一次提交的时候特征工程没做好,基础模型只使用随机森林的情况下,第一次提交排名15000+,评分0.75119(榜首为1)。

后面看了诸多大神的blog之后发现,虽然集成方法的模型各种各样,但是有一点基本都差不多,都用了很多精力在特征工程上面。

不过特征工程做起来也需要对业务场景熟练才行,像ticket特征,一开始不知道持有同票号的人是在同一个船舱的。还有name这个特征,一开始也没想到,名字其实是和性别是有关系的,而性别和存活率的相关性是相当高的。

在做这个泰坦尼克之灾的时候,将之前在数据挖掘中一些不太明白的点,慢慢的理解了。比如特征工程的重要性,特征衍生的重要性等。

最后有点感慨的是,数据挖掘确实比做IT好玩的多,好久都没体验过这种为了提高一点点努力而翻文档奋斗的感觉了,上一次有这种感觉还是做音视频编解码的时候。

希望最后能找到一份数据分析的工作 ╮(╯3╰)╭。

相关文章

  • kaggle之泰坦尼克之灾

    项目介绍 基于kaggle提供的泰坦尼克之灾数据,使用python与sklearn机器学习模块,预测乘客的存活状况...

  • 泰坦尼克之灾_Kaggle

    小白根据前人经验尝试对泰坦尼克之灾进行预测分析 一、确认目标 预测乘客是否能在泰坦尼克之灾中幸存下来。 二、数据探...

  • Machine learning:Titanic数据分析(一)导

    下一节:特征关系分析 一、导览 泰坦尼克之灾数据集本文译自kaggle上的处理泰坦尼克号数据集的这篇Noteboo...

  • Kaggle:泰坦尼克之灾 (一)

    刚刚开始接触Kaggle,根据官网的推荐先拿titanic竞赛练练手,先撸一个baseline出来再慢慢优化。 前...

  • kaggle—泰坦尼克之灾1

    泰坦尼克之灾是kaggle的一个入门案例,本文是我关于这个比赛的一些记录 1、jupyter notebook的安...

  • kaggle——泰坦尼克之灾1

    泰坦尼克之灾是kaggle的一个入门案例,以下是这个比赛的一些记录: 1、jupyter notebook的安装 ...

  • kaggle——泰坦尼克之灾2

    之前已经写过一篇关于这个比赛的简书,简单的描述了比赛的大致流程,参考简书。在这之后又看了rank3的kernal,...

  • kaggle——泰坦尼克之灾2

    关于比赛的基本操作描述,参考简书。学习了比赛中排行第三(rank3)的源码kernal,参考链接,对比起来内容更加...

  • kaggle——泰坦尼克之灾3

    之前有写过两篇关于Titanic比赛的简书,这几天上kaggle-Titanic的kernels在MostVost...

  • (二) Kaggle 泰坦尼克之灾

    竞赛背景 泰坦尼克号的沉没是历史上最臭名昭著的海难之一。 1912年4月15日,在她的处女航中,被广泛认为的“沉没...

网友评论

      本文标题:kaggle之泰坦尼克之灾

      本文链接:https://www.haomeiwen.com/subject/lycwmhtx.html