Chapter 3 Classification (Titanic数据集)

3.处理Titanic数据集

其目的是根据乘客的年龄、性别、乘客阶层、乘坐地点等属性来预测乘客是否幸存。

首先,登录Kaggle,去Titanic challenge网站下载train.csv和test.csv。保存到datasets/titanic目录。

主要涉及到的内容有:数据处理(对空白数据进行填充:Imputer)、自定义转换器、pipeline的编写以及采用以及SVCRandomForestClassifier进行分类预测

读取并加载数据

import os
TITANIC_PATH = os,path.join("datasets","titanic")
import pandas as pd
def load_titanic_data(filename,titanic_path = TITANIC_PATH):
    csv_path = os.path.join(titanic_path,filename)
    return pd.read_csv(csv_path)

train_data = load_titanic_data("train.csv")
test_data = load_titanic_data("test.csv")

数据已经分成训练集和测试集,然而,测试数据不包含标签:目标是训练最好的模型可以使用训练数据,然后预测测试数据并上传Kaggle看到你的最后得分。

看看训练集的前几行:

train_data.head()

属性有以下含义:

(1)Survived:这是目标,0表示乘客没有幸存,1表示乘客幸存。

(2)Pclass:客运类。

(3)NameSexAge:不言自明

(4)SibSP:泰坦尼克号上的乘客有多少兄弟姐妹和配偶?

(5)Parch:泰坦尼克号上有多少儿童和家长?

(6)Ticket:票id

(7)Fare:已付价格(英镑)

(8)Cabin:旅客的客舱号码

(9)Embarked:乘客上船的地方

让我们得到更多的信息,看看有多少数据丢失:

train_data.info()

好吧,AgeCabinEmbarked有时为空(小于891个非空),尤其是Cabin(77%为空)。现在先不谈Cabin了,只谈其他的。Age属性大约有19%的空值,因此我们需要决定如何处理它们。用中位数年龄替换空值似乎是合理的。

NameTicket属性可能有一些值,但是将它们转换成模型可以使用的有用数字会有些麻烦。所以现在,忽略它们。

看一下数值信息

train_data.describe()

 

(1)哎呀,只有38%的人Survived。这已经接近40%了,所以准确性将是评估我们模型的一个合理的指标。

(2)平均Fare是32.20英镑,似乎不是很贵(但那时候可能是一大笔钱)。

(3)平均Age不到30岁。

检查一下目标是否为0或1:

train_data["Survived"].value_counts()

现在快速看一下所有的分类属性:

train_data["Pclass"].value_counts()

train_data["Sex"].value_counts()

train_data["Embarked"].value_counts()

属性告诉我们旅客所出发的地方:C=Cherbourg,Q=Queenstown,S=Southampton。

现在让我们构建预处理管道。进行转换器的编写,需要继承BaseEstimator以及TransformerMixin两个类,需要实现fit方法以及transform方法

from sklearn.base import BaseEstimator, TransformerMixin
class DataFrameSelector(BaseEstimator, TransformerMixin):
    def __init__(self, attribute_names):
        self.attribute_names = attribute_names
    def fit(self, X, y=None):
        return self
    def transform(self, X):
        return X[self.attribute_names]

构建数值属性的管道,转换器DataFrameSelector用于选择某个属性的数据,通过pipeline将数据传给DataFrameSelector中进行数据的筛选

from sklearn.pipeline import Pipeline 
try:
    from sklearn.impute import SimpleImputer
except ImportError:
    from sklearn.preprocessing import Imputer as SimpleImputer

num_pipeline = Pipeline([
    ("select_numeric",DataFrameSelector(["Age","SibSp","Parch","Fare"])),
    ("imputer",SimpleImputer(strategy = "median"))])
num_pipeline.fit_transform(train_data)

还需要一个估算object分类列(常规的Imputer不起作用,Imputer仅对数字型的数据具有数据填充的功能,但对object型的不起作用)

MostFrequentImputer转换器作用是将出现频率最多的作为填充数据,对空白数据进行填充

class MostFrequentImputer(BaseEstimator, TransformerMixin):
    def fit(self, X, y=None):
        self.most_frequent_ = pd.Series([X[c].value_counts().index[0] for c in X],index = X.columns)         
        return self
    def transform(self, X,y = None):
        return X.fillna(self.most_frequent_)

文本数据在做训练的时候,很多分类器是不支持的,因此,需要进行OneHot编码可以使用OneHotEncoder将每个分类值转换为一个热向量。现在这个类只能处理整数的分类输入,但是在sci - learn 0.20中它也可以处理文本的分类输入。现在从future_encoders中导入它。但是当Scikit-Learn 0.20发布时,你可以从sklearn中导入它。预处理:

try:
    from sklearn.preprocessing import OrdinalEncoder
    from sklearn.preprocessing import OneHotEncoder
except ImportError:
    from future_encoders import OneHotEncoder

现在可以建立管道的类别属性:

cat_pipeline = Pipeline([
    ("select_cat",DataFrameSelector(["Pclass","Sex","Embarked"])),
    ("imputer",MostFrequentImputer()),
    ("cat_encoder",OneHotEncoder(sparse = False)),
])
cat_pipeline.fit_transform(train_data)

最后,将数值管道和分类管道连接起来:

from sklearn.pipeline import ReatureUnion
preprocess_pipeline = FeatureUnion(transformer_list = [
    ("num_pipeline",num_pipeline),
    ("cat_pipeline",cat_pipeline),
])

太酷了!现在有了一个很好的预处理管道,它接收原始数据并输出数值输入特性,可以将其输入到任何我们想要的机器学习模型中。

X_train = preprocess_pipeline.fit_transform(train_data)

别忘了获得标签:

y_train = train_data["Survived"]

准备训练一个分类器。从SVC开始:

from sklearn.svm import SVC
svm_clf = SVC(gamma = "auto")
svm_clf.fit(X_train,y_train)

X_test = preprocess_pipeline.transform(test_data)
y_pred = svm_clf.predict(X_test)
from sklearn.model_selection import cross_val_score
svm_scores = cross_val_score(svm_clf,X_train,y_train,cv = 10)
svm_scores.mean()

好吧,超过73%的正确率,明显好于随机概率,但这不是一个很好的分数。看看Kaggle上的泰坦尼克竞赛的排行榜,你会发现你需要达到80%以上的准确率才能进入前10%的kaggler。有些人达到了100%,但由于你可以很容易地找到泰坦尼克号的遇难者名单,他们的表现中似乎很少涉及机器学习!;-)那么让我们试着建立一个达到80%准确率的模型。

试试随机森林分类器:

from sklearn.ensemble import RandomForestClassifier
forest_clf = RandomForestClassifier(n_estimators = 100,random_state = 42)
forest_scores = cross_val_score(forest_clf,X_train,y_train,cv = 10)
forest_scores.mean()

好太多啦!

对两种方案进行可视化操作

plt.figure(figsize = (8,4))
plt.plot([1] * 10,svm_scores,".")
plt.plot([2] * 10,forest_scores,".")
plt.boxplot([svm_scores,forest_scores],labels = ("SVM","Random Forest"))
plt.ylabel("Accuracy",fontsize = 14)
plt.show()

上图不仅仅看平均精度在10折交叉验证,让我们把每个模型的10个分数画出来,以及一个方框图突出显示上下四分位数,“whiskers”显示分数的范围。注意,boxplot()函数检测离群值(称为“fliers”),并且不将它们包含在whiskers中。具体地说,如果下四分位数是Q1和上四分位数是Q3,那么四分位范围IQR = Q3−Q1(这是盒子的高度),以及任何低于Q1−1.5×IQR的 分数都是一个filter,所以任何得分大于Q3 + 1.5×IQR。

要进一步改进这个结果,您可以:

(1)使用交叉验证和网格搜索比较更多的模型和调整超参数,

(2)做更多的功能工程,例如:

         将SibSp和Parch替换成它们的和,

         试着找出名字中与幸存属性相关的部分(例如,如果名字中包含“女伯爵”,那么幸存的可能性更大),

(3)尝试将数值属性转换为分类属性:例如,不同的年龄组有非常不同的存活率(见下文),因此创建一个年龄桶类别并使用它来代替年龄可能会有所帮助。类似地,为独自旅行的人设立一个特殊类别可能会有用,因为只有30%的人幸存下来。

train_data["AgeBucket"] = train_data["Age"] // 15 * 15
train_data[["AgeBucket","Survived"]].groupby(['AgeBucket']).mean() 

train_data["RelativesOnboard"] = train_data["SibSp"] + train_data["Parch"]
train_data[["RelativesOnboard","Survived"]].groupby(['RelativesOnboard']).mean() 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值