学习机器学习的小伙伴,在入门的时候可以通过相对赶紧简单和干净的数据入门。 最常用的数据集就是我在之前写的MNIST,波士顿房价,还有今天要给大家介绍的泰坦尼克号生存预测问题。
泰坦尼克号的故事背景大家一定都非常熟悉了,下面我们一起来探索一下相关数据,以及看看如何用机器学习的方法进行分类预测。
本数据也是Kaggle平台的入门数据之一,可以同Kaggle平台下载训练数据和测试数据,这个竞赛项目也是长年开放的,大家也可以将自己的预测结果上传到Kaggle上。
https://www.kaggle.com/c/titanic/data
import pandas as pd
train_data = pd.read_csv("train.csv")
#预览一下数据
train_data.head()
PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
0 1 0 3 Braund, Mr. Owen Harris male 22.0 1 0 A/5 21171 7.2500 NaN S
1 2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 0 PC 17599 71.2833 C85 C
2 3 1 3 Heikkinen, Miss. Laina female 26.0 0 0 STON/O2. 3101282 7.9250 NaN S
3 4 1 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 0 113803 53.1000 C123 S
4 5 0 3 Allen, Mr. William Henry male 35.0 0 0 373450 8.0500 NaN S
print(train_data.shape)
(891, 12)
train_data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
PassengerId 891 non-null int64
Survived 891 non-null int64
Pclass 891 non-null int64
Name 891 non-null object
Sex 891 non-null object
Age 714 non-null float64
SibSp 891 non-null int64
Parch 891 non-null int64
Ticket 891 non-null object
Fare 891 non-null float64
Cabin 204 non-null object
Embarked 889 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 83.7+ KB
#可以看到有一些列 比如Cabin,Age有非常多的缺失值
泰坦尼克号的数据预处理有两点非常值得大家练手,首先就是缺失值处理,可以看到Cabin和Age是有相当多的缺失值的。
缺失值常见处理方式:
处理Cabin缺失值
在决定处理之前不妨先看一下Cabin分布情况。
Cabin = train_data['Cabin']
Cabin.value_counts()
B96 B98 4
C23 C25 C27 4
G6 4
E101 3
D 3
..
F38 1
C50 1
A5 1
C91 1
E63 1
Name: Cabin, Length: 147, dtype: int64
可以看到Cabin的值比较分散,Cabin值基本出现不大于4次,很多都只出现一次。
下面看一下Cabin的有无对Survive与否的影响。
import matplotlib.pyplot as plt
%matplotlib inline
fig = plt.figure()
fig.set(alpha=0.2) # 设定图表颜色alpha参数
Survived_nocabin = train_data.Survived[pd.isnull(train_data.Cabin)].value_counts()
Survived_cabine = train_data.Survived[pd.notnull(train_data.Cabin)].value_counts()
df=pd.DataFrame({u'with Cabin':Survived_cabin, u'without Cabin':Survived_nocabin}).transpose()
df.plot(kind='bar', stacked=True)
plt.title(u"Cabin and Survived Situation")
plt.xlabel(u"with Cabin or not ")
plt.ylabel(u"Number of people")
plt.show()
有Cabin记录的乘客survival 比例稍高, 可以采用缺失值处理的第二种方法,把这个值分成两个特征,有cabin和无cabin。
处理Age缺失值
Age属性有177个乘客没有记录, 可以用方法3或者方法4, 下面我们一起来试一下方法4.
用随机森林拟合一下缺失的年龄数据
### 使用 RandomForestClassifier 填补缺失的年龄属性
def set_missing_ages(df):
# 把已有的数值型特征取出来丢进RandomForestRegressor中
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之中
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
def set_Cabin_type(df):
df.loc[ (df.Cabin.notnull()), 'Cabin' ] = "Yes"
df.loc[ (df.Cabin.isnull()), 'Cabin' ] = "No"
return df
train_data, rfr = set_missing_ages(train_data)
train_data = set_Cabin_type(train_data)
然后再看一下数据概况
train_data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
PassengerId 891 non-null int64
Survived 891 non-null int64
Pclass 891 non-null int64
Name 891 non-null object
Sex 891 non-null object
Age 891 non-null float64
SibSp 891 non-null int64
Parch 891 non-null int64
Ticket 891 non-null object
Fare 891 non-null float64
Cabin 891 non-null object
Embarked 889 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 83.7+ KB
最后一步准备数据特征
# 因为逻辑回归建模时,需要输入的特征都是数值型特征
# 我们先对类目型的特征离散/因子化
# 以Cabin为例,原本一个属性维度,因为其取值可以是['yes','no'],而将其平展开为'Cabin_yes','Cabin_no'两个属性
# 原本Cabin取值为yes的,在此处的'Cabin_yes'下取值为1,在'Cabin_no'下取值为0
# 原本Cabin取值为no的,在此处的'Cabin_yes'下取值为0,在'Cabin_no'下取值为1
# 我们使用pandas的get_dummies来完成这个工作,并拼接在原来的data_train之上,如下所示
dummies_Cabin = pd.get_dummies(train_data['Cabin'], prefix= 'Cabin')
dummies_Embarked = pd.get_dummies(train_data['Embarked'], prefix= 'Embarked')
dummies_Sex = pd.get_dummies(train_data['Sex'], prefix= 'Sex')
dummies_Pclass = pd.get_dummies(train_data['Pclass'], prefix= 'Pclass')
df = pd.concat([train_data, dummies_Cabin, dummies_Embarked, dummies_Sex, dummies_Pclass], axis=1)
df.drop(['Pclass', 'Name', 'Sex', 'Ticket', 'Cabin', 'Embarked'], axis=1, inplace=True)
df
PassengerId Survived Age SibSp Parch Fare Cabin_No Cabin_Yes Embarked_C Embarked_Q Embarked_S Sex_female Sex_male Pclass_1 Pclass_2 Pclass_3
0 1 0 22.00000 1 0 7.2500 1 0 0 0 1 0 1 0 0 1
1 2 1 38.00000 1 0 71.2833 0 1 1 0 0 1 0 1 0 0
2 3 1 26.00000 0 0 7.9250 1 0 0 0 1 1 0 0 0 1
3 4 1 35.00000 1 0 53.1000 0 1 0 0 1 1 0 1 0 0
4 5 0 35.00000 0 0 8.0500 1 0 0 0 1 0 1 0 0 1
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
886 887 0 27.00000 0 0 13.0000 1 0 0 0 1 0 1 0 1 0
887 888 1 19.00000 0 0 30.0000 0 1 0 0 1 1 0 1 0 0
888 889 0 16.19395 1 2 23.4500 1 0 0 0 1 1 0 0 0 1
889 890 1 26.00000 0 0 30.0000 0 1 1 0 0 0 1 1 0 0
890 891 0 32.00000 0 0 7.7500 1 0 0 1 0 0 1 0 0 1
891 rows × 16 columns
看一下协方差
correlation_matrix = df.corr().round(2)
sns.heatmap(data=correlation_matrix,annot=True)
数据预处理
# 接下来我们要接着做一些数据预处理的工作,比如scaling,将一些变化幅度较大的特征化到[-1,1]之内
# 这样可以加速logistic regression的收敛
import sklearn.preprocessing as preprocessing
scaler = preprocessing.StandardScaler()
age_scale_param = scaler.fit(df['Age'].values.reshape(-1,1))
df['Age_scaled'] = scaler.fit_transform(df['Age'].values.reshape(-1,1), age_scale_param)
fare_scale_param = scaler.fit(df['Fare'].values.reshape(-1,1))
df['Fare_scaled'] = scaler.fit_transform(df['Fare'].values.reshape(-1,1), fare_scale_param)
df
# 我们把需要的feature字段取出来,转成numpy格式,使用scikit-learn中的LogisticRegression建模
from sklearn import linear_model
train_df = df.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')
train_np = train_df.as_matrix()
# y即Survival结果
y = train_np[:, 0]
# X即特征属性值
X = train_np[:, 1:]
# fit到RandomForestRegressor之中
clf = linear_model.LogisticRegression(C=1.0, penalty='l1', tol=1e-6)
clf.fit(X, y)
clf