回忆杀
先来一波回忆杀,沉迷于男女主的颜值自拔,当年的小李子的确是魅力四射啊!还记得那句经典的台词You jump,I jump,代表着永恒的爱情啊
先收住,等会再舔屏,我们今天不讲《泰坦尼克号》,主要讲泰坦尼克号。电影是根据真实故事改编而来的,1912年泰坦尼克号作为当时世界上最豪华、最庞大的客运轮船,在处女航中途与一座冰山相撞,大部分人不幸身亡,少部分人存活下来。因此本文想利用机器学习中的重要算法之一决策树(Decision Tree),根据乘客的数据(性别、年龄等)对其能否存活进行预测。如果在真实背景下,预测Jack和Rose能否存活幸存。
决策树是啥?
决策树都不知道?本仙女可没空和你吹牛。所谓决策树,其实是一种十分常用的分类方法。是一种监管学习方法,也就是给你一堆样本,每个样本都有一组属性和一个类别,这些类别是我们是已知的,也就是本案例中是否存活。通过决策树进行训练,那么就能够对新出现的乘客给出正确的分类啦。
决策树是在已知各种情况发生概率的基础上,通过构成决策树来求取净现值的期望值大于等于零的概率,评价项目风险,判断其可行性的决策分析方法。在机器学习中,决策树是一个预测模型,他代表的是对象属性与对象值之间的一种映射关系。Entropy = 系统的凌乱程度,使用算法ID3, C4.5和C5.0生成树算法使用熵。
给一个通俗易懂的例子吧,比如某银行要判断一个新客户是否可以贷款,根据历史客户数据生成决策树是酱紫的。那么我们先从根节点房产出发,如果该客户有房产,则可以贷款,若没有房产,再看其是否车辆,若有车辆,则可以贷款,若没有再看其年收入,以此类推,总能判断是否可以贷款。一眼就能看出这个图很像一棵树呢,所以就叫决策树啦。
泰坦尼克号数据集
首先来介绍下泰坦尼克号数据集吧,共收集到1309个样本,乘客的12个特征,其中Survived是因变量,是个二分类变量(存活和死亡),其他11个特征作为因变量,是不是刚好符合决策树算法的要求。
变量(变量解释)
Survived 存活情况(存活1,死亡0)
PassengerId 乘客编号
Pclass 船舱等级(1,2,3)
Name 乘客姓名
Sex 乘客性别
Age 乘客年龄
SibSp 同乘的兄弟姐妹/配偶数
Parch 同乘的父母/小孩数
Ticket 船票编号(字符串)
Fare 船票价格(0-500)
Cabin 乘客所在船舱
Embarked 乘客登船港口(S,C,Q)
其中891个作为训练集,418个作为测试集。需要的包和导入数据的Python代码都在这里啦。Padas中的concat函数就是把训练集和测试集合并起来了,方便后面统一分析。
用Python自带的函数sample抽5个样本悄悄看一下数据,index是抽取的样本所在的行数。其实我们拿到的数据是有缺失的,比如Cabin船舱。
Part3
下面就是特征工程了,来看看乘客的存活情况是否与单个因变量有关
性别sex
先看一下性别和存活情况的关系,女性的存活率为74.20%,男性的存活率仅为18.89%,可见女性确实是受保护的,存活率远远高于男性。女性优先在危难关头得到淋漓尽致的展现了。
船舱等级class
下面来看一下船舱等级的存活率图,等级一、二、三的存活率分别为 62.97%、47.28%、24.24%。代码只需要把上面代码的“Sex”换成“Pclass”,so easy这里就不附上了。
年龄age
再来看一下年龄和存活率的情况吧
可以看出,在6岁左右,存活(1)远远大于死亡(0),说明儿童相对保护的比较好,大多数是存活下来的。25岁左右的青年存活率要低于死亡率,更多年轻人会把生存机会留给老人和孩子。在60到80岁之间,存活率比较比死亡率要低,老人相对体力不够,存活比较困难。
Fare
数据处理
我们先来瞄一眼缺失值情况,数据集共包含1309条记录,其中训练集891条,测试集418条。
可以看到特征Age,Cabin,Embarked,Fare以及Ticket不全,包含缺失值,需要进行缺失值填补,其中Survived是否生存,就是我们研究的因变量,所以测试集全为缺失。
年龄Age
首先来看年龄,通过前面推文我们已经知道年龄是重要特征,因此其缺失值的能否正确填补对模型有较大影响。通过进一步分析可以发现,登船者的年龄与其同乘的兄弟姐妹/配偶数(SibSp)、同乘的小孩数(Parch)有一定关联。因此我们这里用与缺失年龄的乘客有一样SibSp、Parch的乘客的平均年龄来填补缺失值,如果这个值为空,那就用所有乘客的平均年龄来填补。
index_NaN_age = list(dataset["Age"][dataset["Age"].isnull()].index)
for i in index_NaN_age :
age_med = dataset["Age"].median()
age_pred = dataset["Age"][((dataset['SibSp'] == dataset.iloc[i]["SibSp"]) & (dataset['Parch'] == dataset.iloc[i]["Parch"]) & (dataset['Pclass'] == dataset.iloc[i]["Pclass"]))].median()
if not np.isnan(age_pred) :
dataset['Age'].iloc[i] = age_pred
else :
dataset['Age'].iloc[i] = age_med
船票价格Fare
接下来看船票价格与乘客存活率的关系,violin图的代码只需在箱线图基础上加参数kind=’violin’
可以看出,存活下来的的乘客(Survived=1)其船票价格在100附近的相对较多,整体来说,未存活的乘客船票价格相对较低。
dataset["Fare"] = dataset["Fare"].fillna(dataset["Fare"].median())
dataset["Fare"] = dataset["Fare"].map(lambda i: np.log(i) if i > 0 else 0)
船票价格的缺失值只有3个,我们就可以简单粗暴的用中位数进行填补啦。但需注意的是,极少数乘客的票价相当高,因此我们这里对大于0的票价取对数,缩小差距,更好建模。
登船港口Embarked
下面我们来看登船港口的存活率,并加入的性别做对比。总共有S、C、Q三个港口,可以看出,无论是男性还是女性,各个港口的存活率是有较大差距的。明显看出港口C的存活率最高。
因为登船港口仅有两个缺失,我们就直接用众数填补了。先用value_counts()函数对频数进行统计,可以看出三个港口登船人数最多的为S,也就是众数,就直接用S进行填补啦~
dataset[“Embarked”].value_counts()
dataset[“Embarked”] = dataset[“Embarked”].fillna(“S”)
名字Name
泰坦尼克号数据集的Name特征比较特殊,我们先看一下其前五行:
一眼看上去名字特征似乎没什么用处,但其实这里包含了一个重要特征:乘客的头衔,即Mr、Mrs这样的title,不同的头代表的身份地位不同,与其存活率应该有较大关系。这里我们首先把头衔信息提取出来,并命名为Title,作为数据集的新列。其中split函数就是按指定的字符对字符串进行分割。这里首先利用‘,’将姓和名分开,再用‘.’将头衔提出来。
dataset_title = [i.split(",")[1].split(".")[0].strip() for i in dataset["Name"]]#strip()去除首尾的空白
dataset["Title"] = pd.Series(dataset_title)
dataset["Title"].head()
看一下提取的效果,一下就把我们想要的头衔提取出来了~
but由于头衔种类特别多,且很多头衔出现的频数很少,频数统计如图。
接下来用replace函数对出现次数少且相近的头衔进行替换。最后再将头衔Ms、Miss 、Mme、Mlle、Mrs归为一类,其他头衔单独归为一类。
dataset["Title"] = dataset["Title"].replace(['Lady', 'the Countess','Countess','Capt', 'Col','Don', 'Dr', 'Major', 'Rev', 'Sir', 'Jonkheer', 'Dona'], 'Rare')
dataset["Title"] = dataset["Title"].map({"Master":0, "Miss":1, "Ms" : 1 , "Mme":1, "Mlle":1, "Mrs":1, "Mr":2, "Rare":3})
dataset["Title"] = dataset["Title"].astype(int)
家庭成员数
下面我们关注同乘的兄弟姐妹/配偶数(SibSp)与同乘的小孩数(Parch),因为这两个特征其实反映了乘客的同行的家庭成员数,因此我们新创建一个特征家庭成员数Fsize =SibSp + SibSp+1。创建之后再用lambda函数赋值为0和1。
dataset.drop(labels = ["Name"], axis = 1, inplace = True)
# Create a family size descriptor from SibSp and Parch
dataset["Fsize"] = dataset["SibSp"] + dataset["Parch"] + 1
# Create new feature of family size
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)
分类变量编码
最后再将所有分类变量进行编码
dataset = pd.get_dummies(dataset, columns = ["Title"])
dataset = pd.get_dummies(dataset, columns = ["Embarked"], prefix="Em")
模型与预测
处理好的数据就可以进行建模和预测了(爱学习的小同学不要着急,后续会继续讲数据的具体整理过程),sklearn包里面有许多分类算法,我们导出里面的DecisionTreeClass就可以啦。代码附上,其中Y_train,X_train分别为训练集的因变量和自变量,X_test为测试集的因变量。
clf = tree.DecisionTreeClassifier(random_state=2)
train = dataset[:len(train)]
test = dataset[891:]
train["Survived"] = train["Survived"].astype(int)
Y_train = train["Survived"]
X_train = train.drop(labels = ["Survived"],axis = 1)
X_train = X_train.drop(labels = ["Ticket"],axis = 1)
X_test = test.drop(labels = ["Ticket"],axis = 1)
X_train.head()
clf.fit(X_train,Y_train)
y_pred = clf.predict(X_test)
X_train.isnull()
`
最后回归电影《泰坦尼克号》,将男女主的数据录入测试集,有部分信息无法得知,用样本数据进行填补。最后预测得到Jack死亡,Rose生存,与电影的结局是一致的。有爱学习的小朋友可能会说决策树预测的不准,那么后续推文会再介绍其它机器学习的算法,再进行预测,乖乖等着吧。
最后的最后,学习太辛苦了,再来欣赏一下男女主的神仙颜值吧。
The End 下面是全部代码
#data analysis libraries
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt