Kaggle机器学习入门课程 泰坦尼克号数据科学解决方案中文翻译

泰坦尼克号数据科学解决方案

该笔记本引导我们了解在Kaggle等网站上解决数据科学竞赛的典型工作流程。

有几个优秀的notebooks可以研究数据科学竞赛的参赛作品。然而,许多notebooks会跳过一些关于解决方案开发的解释,因为这些笔记本是为专家开发的,面向专家。该笔记本的目标是遵循逐步工作流程,解释每一步以及我们在解决方案开发过程中所做每个决策的理由。

Workflow stages

工作流程阶段

竞赛解决方案的工作流程经历七个阶段,这些阶段在《数据科学解决方案》一书中有所描述。

  1. 问题定义。
  2. 获取训练和测试数据。
  3. 整理、准备、清理数据。
  4. 分析、识别模式、探索数据。
  5. 建模、预测并解决问题。
  6. 可视化、报告并展示问题解决步骤和最终解决方案。
  7. 提供或提交结果。

该工作流程指示了每个阶段可能如何相互衔接的一般顺序。然而,也存在一些例外的使用案例。

  1. 我们可以结合多个工作流程阶段。我们可以通过可视化数据进行分析。
  2. 在指示的阶段之前执行一个阶段。我们可以在整理数据之前和之后分析数据。
  3. 在我们的工作流程中多次执行一个阶段。可视化阶段可以多次使用。
  4. 完全省略一个阶段。我们可能不需要提供阶段来将数据集产品化或服务化,以便参加竞赛。

Question and problem definition

问题和问题定义

Kaggle等竞赛网站定义要解决的问题或要提出的问题,同时提供用于训练数据科学模型的数据集,并根据测试数据集测试模型结果。泰坦尼克号生存竞赛的问题或问题定义在Kaggle上描述如下。

根据训练样本集中的乘客生存与否的信息,我们的模型能否根据不包含生存信息的测试数据集确定这些乘客是否生存。

我们还可能希望对我们问题的领域进行一些初步了解。这在Kaggle竞赛描述页面上有所描述。以下是需要注意的要点。

  1. 1912年4月15日,泰坦尼克号在首航中与冰山相撞,沉没,导致2224名乘客和船员中有1502人遇难。生存率为32%。
  2. 导致这次船难造成如此多生命损失的原因之一是没有足够的救生艇供乘客和船员使用。
  3. 虽然生存下来的因素中有一定的运气成分,但某些群体的生存几率比其他群体高,例如女性、儿童和上层阶级。

Workflow goals

工作流程目标

数据科学解决方案工作流程解决七个主要目标。

  1. 分类。我们可能希望对样本进行分类或归类。我们也可能希望了解不同类别与我们解决目标之间的影响或相关性。
  2. 相关性。可以根据训练数据集中可用的特征来解决问题。数据集中的哪些特征对我们的解决目标有显著贡献?从统计学的角度看,特征与解决目标之间是否存在相关性?当特征值发生变化时,解决状态是否也会变化,反之亦然?这可以针对给定数据集中的数值特征和分类特征进行测试。我们还可能希望确定生存以外的特征之间的相关性,以便实现后续目标和工作流程阶段。相关某些特征可能有助于创建、完成或修正特征。
  3. 转换。对于建模阶段,需要准备数据。根据模型算法的选择,可能需要将所有特征转换为数值等效值。例如,将文本分类值转换为数值值。
  4. 补全。数据准备还可能需要我们估计特征中的任何缺失值。当没有缺失值时,模型算法的表现可能会更好。
  5. 纠正。我们还可能分析给定的训练数据集,寻找特征中的错误或可能不准确的值,并尝试纠正这些值或排除包含错误的样本。检测样本或特征中的异常值是一种方法。如果某个特征对分析没有贡献或可能显著扭曲结果,我们也可以完全丢弃该特征。
  6. 创建。我们能否基于现有特征或特征集创建新特征,使得新特征符合相关性、转换、完整性目标。
  7. 图表。如何根据数据的性质和解决目标选择合适的可视化图表和图形。
# data analysis and wrangling
import pandas as pd
import numpy as np
import random as rnd

# visualization
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline

# machine learning
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC, LinearSVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.linear_model import Perceptron
from sklearn.linear_model import SGDClassifier
from sklearn.tree import DecisionTreeClassifier

Dataset Description

数据集描述

Overview

The data has been split into two groups:

  • training set (train.csv)
  • test set (test.csv)

训练集应用于构建你的机器学习模型。对于训练集,我们提供每位乘客的结果(也称为“真实值”)。你的模型将基于乘客的性别和舱位等“特征”。你还可以使用特征工程来创建新特征。

测试集应用于查看你的模型在未见数据上的表现。对于测试集,我们不提供每位乘客的真实值。你的任务是预测这些结果。对于测试集中的每位乘客,使用你训练的模型预测他们是否在泰坦尼克号沉没中幸存。

Data Dictionary

VariableDefinitionKey
survivalSurvival0 = No, 1 = Yes
pclassTicket class1 = 1st, 2 = 2nd, 3 = 3rd
sexSex
AgeAge in years
sibsp# of siblings / spouses aboard the Titanic
parch# of parents / children aboard the Titanic
ticketTicket number
farePassenger fare
cabinCabin number
embarkedPort of EmbarkationC = Cherbourg, Q = Queenstown, S = Southampton

Variable Notes

pclass: 社会经济地位(SES)的代理
1st = Upper
2nd = Middle
3rd = Lower

age: 如果年龄小于1,则为小数。如果年龄是估算的,则形式为xx.5

sibsp: 数据集以这种方式定义家庭关系...
Sibling = brother, sister, stepbrother, stepsister
兄弟姐妹 = 兄弟、姐妹、同父异母的兄弟、同母异父的姐妹
Spouse = husband, wife (mistresses and fiancés were ignored)
配偶 = 丈夫、妻子(情妇和未婚夫被忽略)
parch: 数据集以这种方式定义家庭关系...
Parent = mother, father
Child = daughter, son, stepdaughter, stepson

一些孩子仅与保姆一起旅行,因此他们的parch=0。

Acquire data¶

获取数据¶

Python Pandas包帮助我们处理数据集。我们首先将训练和测试数据集导入Pandas DataFrame中。我们还将这些数据集合并,以便对两个数据集一起进行某些操作。

train_df = pd.read_csv('../input/train.csv')
test_df = pd.read_csv('../input/test.csv')
combine = [train_df, test_df]

Copy

Analyze by describing data

通过描述数据进行分析

Pandas还帮助描述数据集,回答我们项目早期的以下问题。
数据集中有哪些特征可用?
注意特征名称,以便直接操作或分析这些特征。这些特征名称在Kaggle数据页面上有描述。

print(train_df.columns.values)

Copy

['PassengerId' 'Survived' 'Pclass' 'Name' 'Sex' 'Age' 'SibSp' 'Parch'
'Ticket' 'Fare' 'Cabin' 'Embarked']

哪些特征是分类特征?
这些值将样本分类为相似样本的集合。分类特征中的值可以是名义的、顺序的、比率的或区间的?这有助于我们选择适当的可视化图表。
Categorical: Survived, Sex, and Embarked. Ordinal: Pclass.
分类特征:是否生存、性别和登船港口。顺序特征:舱位。

哪些特征是数值特征?

这些值在样本之间变化。数值特征中的值可以是离散的、连续的或时间序列的?这有助于我们选择适当的可视化图表。
Continous: Age, Fare. Discrete: SibSp, Parch.
连续特征:年龄、票价。离散特征:兄弟姐妹数量、父母数量。

# preview the data
train_df.head()

Copy

PassengerIdSurvivedPclassNameSexAgeSibSpParchTicketFareCabinEmbarked
0103Braund, Mr. Owen Harrismale22.010A/5 211717.2500NaNS
1211Cumings, Mrs. John Bradley (Florence Briggs Th...female38.010PC 1759971.2833C85C
2313Heikkinen, Miss. Lainafemale26.000STON/O2. 31012827.9250NaNS
3411Futrelle, Mrs. Jacques Heath (Lily May Peel)female35.01011380353.1000C123S
4503Allen, Mr. William Henrymale35.0003734508.0500NaNS

哪些特征是混合数据类型?

同一特征内的数值和字母数字数据。这些是需要纠正的目标。

票号是数字和字母数字数据类型的混合。舱位是字母数字的。

哪些特征可能包含错误或拼写错误?
对于大型数据集,这更难以审查,但从较小的数据集中查看一些样本可能会直接告诉我们哪些特征可能需要纠正。
姓名特征可能包含错误或拼写错误,因为描述姓名的方式有很多种,包括使用称谓、圆括号和用于替代或简短名称的引号。

train_df.tail()

Copy

PassengerIdSurvivedPclassNameSexAgeSibSpParchTicketFareCabinEmbarked
88688702Montvila, Rev. Juozasmale27.00021153613.00NaNS
88788811Graham, Miss. Margaret Edithfemale19.00011205330.00B42S
88888903Johnston, Miss. Catherine Helen "Carrie"femaleNaN12W./C. 660723.45NaNS
88989011Behr, Mr. Karl Howellmale26.00011136930.00C148C
89089103Dooley, Mr. Patrickmale32.0003703767.75NaNQ

哪些特征包含空白、空值或空值?
这些需要纠正:

Cabin > Age > Embarked features contain a number of null values in that order for the training dataset.
舱位 > 年龄 > 登船港口特征在训练数据集中按此顺序包含多个空值。
Cabin > Age are incomplete in case of test dataset.
舱位 > 年龄在测试数据集中是不完整的。

各种特征的数据类型是什么?

七个特征是整数或浮点数。在测试数据集中有六个。
五个特征是字符串(对象)。

train_df.info()
print('_'*40)
test_df.info()

Copy

<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.6+ KB


<class 'pandas.core.frame.DataFrame'>
RangeIndex: 418 entries, 0 to 417
Data columns (total 11 columns):
PassengerId 418 non-null int64
Pclass 418 non-null int64
Name 418 non-null object
Sex 418 non-null object
Age 332 non-null float64
SibSp 418 non-null int64
Parch 418 non-null int64
Ticket 418 non-null object
Fare 417 non-null float64
Cabin 91 non-null object
Embarked 418 non-null object
dtypes: float64(2), int64(4), object(5)
memory usage: 36.0+ KB

数值特征值在样本中的分布是什么?

这有助于我们确定训练数据集在实际问题领域中的代表性等早期见解。

  • 总样本为891个,占泰坦尼克号上实际乘客人数(2,224)的40%。
  • 生存是一个具有0或1值的分类特征。
  • 大约38%的样本幸存,代表实际生存率为32%。
  • 大多数乘客(> 75%)没有与父母或孩子一起旅行。
  • 近30%的乘客在船上有兄弟姐妹和/或配偶。
  • 票价差异显著,少数乘客(<1%)支付高达512美元。
  • 在65-80岁年龄范围内有少数老年乘客(<1%)。
train_df.describe()
# Review survived rate using `percentiles=[.61, .62]` knowing our problem description mentions 38% survival rate.
# Review Parch distribution using `percentiles=[.75, .8]`
# SibSp distribution `[.68, .69]`
# Age and Fare `[.1, .2, .3, .4, .5, .6, .7, .8, .9, .99]`

Copy

PassengerIdSurvivedPclassAgeSibSpParchFare
count891.000000891.000000891.000000714.000000891.000000891.000000891.000000
mean446.0000000.3838382.30864229.6991180.5230080.38159432.204208
std257.3538420.4865920.83607114.5264971.1027430.80605749.693429
min1.0000000.0000001.0000000.4200000.0000000.0000000.000000
25%223.5000000.0000002.00000020.1250000.0000000.0000007.910400
50%446.0000000.0000003.00000028.0000000.0000000.00000014.454200
75%668.5000001.0000003.00000038.0000001.0000000.00000031.000000
max891.0000001.0000003.00000080.0000008.0000006.000000512.329200

分类特征的分布是什么?

  • 姓名在数据集中是唯一的(计数=唯一=891)。
  • 性别变量有两个可能的值,其中男性占65%(最高=男性,频率=577/计数=891)。
  • 舱位值在样本中有多个重复。或者说,多个乘客共享一个舱位。
  • 登船港口有三个可能的值。大多数乘客使用的港口是S(最高=S)。
  • 票号特征有高达22%的重复值比例(唯一=681)。
train_df.describe(include=['O'])

Copy

NameSexTicketCabinEmbarked
count891891891204889
unique89126811473
topPanula, Master. Juha Niilomale1601C23 C25 C27S
freq157774644

Assumtions based on data analysis

基于数据分析的假设

我们根据目前所做的数据分析得出了以下假设。在采取适当行动之前,我们可能会进一步验证这些假设。

Correlating.
关联性。

我们想知道每个特征与是否生存之间的相关性有多好。我们希望在项目早期进行这项工作,并将这些快速的相关性与项目后期建模的相关性进行匹配。

Completing.
补全。

  • 我们可能想要补全年龄特征,因为它与生存肯定相关。
  • 我们可能想要补全登船港口特征,因为它也可能与生存或其他重要特征相关。

Correcting.
纠正。

  • 票号特征可能会从我们的分析中删除,因为它包含高比例的重复值(22%),并且票号与生存之间可能没有相关性。
  • 舱位特征可能会被删除,因为在训练和测试数据集中都高度不完整或包含许多空值。
  • 乘客ID可能会从训练数据集中删除,因为它对生存没有贡献。
  • 姓名特征相对不标准,可能不会直接对生存产生贡献,因此可能会被删除。

Creating.
创建。

  • 我们可能想要基于父母数量和兄弟姐妹数量创建一个名为家庭的新特征,以获取在船上家庭成员的总数。
  • 我们可能想要基于父母数量和兄弟姐妹数量创建一个名为家庭的新特征,以获取在船上家庭成员的总数。
  • 我们可能想要为年龄段创建新特征。这将一个连续的数值特征转换为一个顺序分类特征。
  • 如果有助于我们的分析,我们还可能想要创建票价范围特征。

Classifying.
分类。

我们还可能根据之前提到的问题描述增加我们的假设。

  1. 女性(性别=女性)更有可能幸存。
  2. 儿童(年龄<?)更有可能幸存。
  3. 上层乘客(舱位=1)更有可能幸存。

Analyze by pivoting features¶

通过透视特征进行分析

为了确认我们的一些观察和假设,我们可以通过将特征相互透视来快速分析特征间的相关性。在这个阶段,我们只能对没有空值的特征进行此操作。这样做也只适用于分类特征(性别)、顺序特征(舱位)或离散特征(兄弟姐妹数量、父母数量)。

  1. 舱位 我们观察到舱位=1与生存之间有显著的相关性(>0.5)(分类#3)。我们决定将这个特征纳入我们的模型。
  2. 性别 我们确认在问题定义期间的观察,即性别=女性的生存率非常高,达到74%(分类#1)。
  3. 兄弟姐妹数量和父母数量 这些特征在某些值上没有相关性。最好从这些单独特征中推导出一个特征或一组特征(创建#1)。
train_df[['Pclass', 'Survived']].groupby(['Pclass'], as_index=False).mean().sort_values(by='Survived', ascending=False)

Copy

PclassSurvived
010.629630
120.472826
230.242363
train_df[["Sex", "Survived"]].groupby(['Sex'], as_index=False).mean().sort_values(by='Survived', ascending=False)

Copy

SexSurvived
0female0.742038
1male0.188908
train_df[["SibSp", "Survived"]].groupby(['SibSp'], as_index=False).mean().sort_values(by='Survived', ascending=False)

Copy

SibSpSurvived
110.535885
220.464286
000.345395
330.250000
440.166667
550.000000
train_df[["Parch", "Survived"]].groupby(['Parch'], as_index=False).mean().sort_values(by='Survived', ascending=False)

Copy

ParchSurvived
330.600000
110.550847
220.500000
000.343658
550.200000
440.000000

Analyze by visualizing data

通过可视化数据进行分析

现在我们可以继续使用可视化来确认我们的一些假设,以分析数据。

Correlating numerical features

关联数值特征

让我们首先理解数值特征与我们的解决目标(生存)之间的相关性。

直方图对于分析连续数值变量(如年龄)非常有用,通过分组或范围可以帮助识别有用的模式。直方图可以通过自动定义的区间或相等范围的带来指示样本的分布。这有助于我们回答与特定区间相关的问题(婴儿的生存率是否更高?)

请注意,直方图可视化中的x轴表示样本或乘客的数量。

Observations.
观察结果。

  1. 婴儿(年龄<=4)具有较高的生存率。
  2. 最年长的乘客(年龄=80)幸存。
  3. 大量15-25岁的人没有幸存。
  4. 大多数乘客的年龄在15-35岁之间。

Decisions.
决策。

这个简单的分析确认了我们的假设,为后续工作流程阶段的决策提供了支持。

  1. 我们应该在模型训练中考虑年龄(我们的假设分类#2)。
  2. 补全年龄特征的空值(补全#1)。
  3. 我们应该对年龄组进行分组(创建#3)。
g = sns.FacetGrid(train_df, col='Survived')
g.map(plt.hist, 'Age', bins=20)

Copy

Correlating numerical and ordinal features¶

关联数值特征和顺序特征

我们可以结合多个特征,通过单个图表识别相关性。这可以与具有数值的数值特征和分类特征一起完成。

Observations.
观察结果。

  • 舱位=3的乘客最多,但大多数没有幸存。这确认了我们的分类假设#2。
  • 舱位=2和舱位=3的婴儿乘客大多数幸存。进一步验证了我们的分类假设#2。
  • 舱位=1的大多数乘客幸存。这确认了我们的分类假设#3。
  • 舱位在乘客的年龄分布上有所不同。

Decisions.
决策。

在模型训练中考虑舱位。

# grid = sns.FacetGrid(train_df, col='Pclass', hue='Survived')
grid = sns.FacetGrid(train_df, col='Survived', row='Pclass', size=2.2, aspect=1.6)
grid.map(plt.hist, 'Age', alpha=.5, bins=20)
grid.add_legend();

Copy

Correlating categorical features

关联分类特征

现在我们可以将分类特征与我们的解决目标进行关联。

Observations.
观察结果。

  • 女性乘客的生存率远高于男性。这确认了分类假设(#1)。
  • 在Embarked=C中,男性的生存率更高,这是一个例外。这可能是舱位和登船港口之间的相关性,进而是舱位和生存之间的相关性,而不一定是登船港口和生存之间的直接相关性。
  • 在C和Q港口中,男性在舱位=3的生存率高于舱位=2。补全(#2)。
  • 不同登船港口的舱位=3和男性乘客的生存率有所不同。关联(#1)。

Decisions.

将性别特征添加到模型训练中。
补全并添加登船港口特征到模型训练中。

# grid = sns.FacetGrid(train_df, col='Embarked')
grid = sns.FacetGrid(train_df, row='Embarked', size=2.2, aspect=1.6)
grid.map(sns.pointplot, 'Pclass', 'Survived', 'Sex', palette='deep')
grid.add_legend()

Copy

关联分类特征和数值特征

我们可能还想将分类特征(非数值值)和数值特征进行关联。我们可以考虑将登船港口(分类非数值)、性别(分类非数值)、票价(数值连续)与生存(分类数值)进行关联。

观察结果。

  • 支付更高票价的乘客生存率更高。这确认了我们创建票价范围的假设(#4)。
  • 登船港口与生存率相关。这确认了关联(#1)和补全(#2)。

决策。

考虑对票价特征进行分组。

# grid = sns.FacetGrid(train_df, col='Embarked', hue='Survived', palette={0: 'k', 1: 'w'})
grid = sns.FacetGrid(train_df, row='Embarked', col='Survived', size=2.2, aspect=1.6)
grid.map(sns.barplot, 'Sex', 'Fare', alpha=.5, ci=None)
grid.add_legend()

Copy

整理数据

我们已经收集了关于我们的数据集和解决方案要求的若干假设和决策。到目前为止,我们没有改变任何特征或数值来得出这些结论。现在让我们执行我们的决策和假设,以纠正、创建和完成目标。

通过删除特征进行纠正

这是一个很好的起始目标。通过删除特征,我们处理的数据点更少。这加快了我们的笔记本运行速度,并简化了分析。

根据我们的假设和决策,我们希望删除舱位(纠正#2)和票号(纠正#1)特征。

请注意,在适用的情况下,我们对训练和测试数据集同时进行操作,以保持一致性。

print("Before", train_df.shape, test_df.shape, combine[0].shape, combine[1].shape)

train_df = train_df.drop(['Ticket', 'Cabin'], axis=1)
test_df = test_df.drop(['Ticket', 'Cabin'], axis=1)
combine = [train_df, test_df]

"After", train_df.shape, test_df.shape, combine[0].shape, combine[1].shape

Copy

Before (891, 12) (418, 11) (891, 12) (418, 11)
('After', (891, 10), (418, 9), (891, 10), (418, 9))

从现有特征中创建新特征

我们想分析姓名特征是否可以被工程化以提取称谓,并测试称谓与生存之间的相关性,然后再删除姓名和乘客ID特征。
在以下代码中,我们使用正则表达式提取称谓特征。正则表达式模式(\w+.) 匹配姓名特征中以点字符结尾的第一个单词。expand=False标志返回一个DataFrame。

观察结果。

当我们绘制称谓、年龄和生存时,我们注意到以下观察结果。

  •  大多数称谓准确地划分了年龄组。例如:Master称谓的平均年龄为5岁。
  •  称谓年龄组之间的生存率略有不同。
  •  某些称谓大多数幸存(Mme、Lady、Sir)或没有幸存(Don、Rev、Jonkheer)。

决策。

我们决定保留新的称谓特征用于模型训练。

for dataset in combine:
    dataset['Title'] = dataset.Name.str.extract(' ([A-Za-z]+)\.', expand=False)

pd.crosstab(train_df['Title'], train_df['Sex'])

Copy

Sexfemalemale
Title
Capt01
Col02
Countess10
Don01
Dr16
Jonkheer01
Lady10
Major02
Master040
Miss1820
Mlle20
Mme10
Mr0517
Mrs1250
Ms10
Rev06
Sir01

我们可以用更常见的名称替换许多称谓,或者将它们归类为稀有(Rare)。

for dataset in combine:
    dataset['Title'] = dataset['Title'].replace(['Lady', 'Countess','Capt', 'Col',\
 	'Don', 'Dr', 'Major', 'Rev', 'Sir', 'Jonkheer', 'Dona'], 'Rare')

    dataset['Title'] = dataset['Title'].replace('Mlle', 'Miss')
    dataset['Title'] = dataset['Title'].replace('Ms', 'Miss')
    dataset['Title'] = dataset['Title'].replace('Mme', 'Mrs')
    
train_df[['Title', 'Survived']].groupby(['Title'], as_index=False).mean()

Copy

TitleSurvived
0Master0.575000
1Miss0.702703
2Mr0.156673
3Mrs0.793651
4Rare0.347826

我们可以将分类称谓转换为序数。

title_mapping = {"Mr": 1, "Miss": 2, "Mrs": 3, "Master": 4, "Rare": 5}
for dataset in combine:
    dataset['Title'] = dataset['Title'].map(title_mapping)
    dataset['Title'] = dataset['Title'].fillna(0)

train_df.head()

Copy

PassengerIdSurvivedPclassNameSexAgeSibSpParchFareEmbarkedTitle
0103Braund, Mr. Owen Harrismale22.0107.2500S1
1211Cumings, Mrs. John Bradley (Florence Briggs Th...female38.01071.2833C3
2313Heikkinen, Miss. Lainafemale26.0007.9250S2
3411Futrelle, Mrs. Jacques Heath (Lily May Peel)female35.01053.1000S3

现在我们可以安全地从训练和测试数据集中删除姓名特征。我们在训练数据集中也不需要乘客ID特征。

train_df = train_df.drop(['Name', 'PassengerId'], axis=1)
test_df = test_df.drop(['Name'], axis=1)
combine = [train_df, test_df]
train_df.shape, test_df.shape

Copy

((891, 9), (418, 9))

转换分类特征

现在我们可以将包含字符串的特征转换为数值。这是大多数模型算法所要求的。这样做也将帮助我们实现特征补全的目标。

让我们开始将性别特征转换为一个新特征,称为性别,其中女性=1,男性=0。

for dataset in combine:
    dataset['Sex'] = dataset['Sex'].map( {'female': 1, 'male': 0} ).astype(int)

train_df.head()

Copy

SurvivedPclassSexAgeSibSpParchFareEmbarkedTitle
003022.0107.2500S1
111138.01071.2833C3
213126.0007.9250S2
311135.01053.1000S3
403035.0008.0500S1

补全数值连续特征

现在我们应该开始估计和补全缺失或空值的特征。我们将首先对年龄特征进行处理。

我们可以考虑三种方法来补全数值连续特征。

  1. 一种简单的方法是生成介于均值和标准差之间的随机数。
  2. 更准确的猜测缺失值的方法是使用其他相关特征。在我们的案例中,我们注意到年龄、性别和舱位之间的相关性。使用不同舱位和性别特征组合的年龄中位数来猜测年龄值。因此,Pclass=1和性别=0、Pclass=1和性别=1等的年龄中位数...
  3. 结合方法1和方法2。因此,不是基于中位数来猜测年龄值,而是基于舱位和性别组合的均值和标准差之间的随机数。

方法1和方法3将为我们的模型引入随机噪声。多次执行的结果可能会有所不同。我们将更倾向于使用方法2。

# grid = sns.FacetGrid(train_df, col='Pclass', hue='Gender')
grid = sns.FacetGrid(train_df, row='Pclass', col='Sex', size=2.2, aspect=1.6)
grid.map(plt.hist, 'Age', alpha=.5, bins=20)
grid.add_legend()

Copy

让我们开始准备一个空数组,以容纳基于舱位和性别组合猜测的年龄值。

guess_ages = np.zeros((2,3))
guess_ages

Copy

array([[0., 0., 0.],
[0., 0., 0.]])

现在我们遍历性别(0或1)和舱位(1、2、3),计算六种组合的猜测年龄值。

for dataset in combine:
    for i in range(0, 2):
        for j in range(0, 3):
            guess_df = dataset[(dataset['Sex'] == i) & \
                                  (dataset['Pclass'] == j+1)]['Age'].dropna()

            # age_mean = guess_df.mean()
            # age_std = guess_df.std()
            # age_guess = rnd.uniform(age_mean - age_std, age_mean + age_std)

            age_guess = guess_df.median()

            # Convert random age float to nearest .5 age
            guess_ages[i,j] = int( age_guess/0.5 + 0.5 ) * 0.5
            
    for i in range(0, 2):
        for j in range(0, 3):
            dataset.loc[ (dataset.Age.isnull()) & (dataset.Sex == i) & (dataset.Pclass == j+1),\
                    'Age'] = guess_ages[i,j]

    dataset['Age'] = dataset['Age'].astype(int)

train_df.head()

Copy

SurvivedPclassSexAgeSibSpParchFareEmbarkedTitle
003022107.2500S1
1111381071.2833C3
213126007.9250S2
3111351053.1000S3
403035008.0500S1

让我们创建年龄区间并确定与生存的相关性。

train_df['AgeBand'] = pd.cut(train_df['Age'], 5)
train_df[['AgeBand', 'Survived']].groupby(['AgeBand'], as_index=False).mean().sort_values(by='AgeBand', ascending=True)

Copy

AgeBandSurvived
0(-0.08, 16.0]0.550000
1(16.0, 32.0]0.337374
2(32.0, 48.0]0.412037
3(48.0, 64.0]0.434783
4(64.0, 80.0]0.090909

让我们根据这些区间用序数替换年龄。

for dataset in combine:    
    dataset.loc[ dataset['Age'] <= 16, 'Age'] = 0
    dataset.loc[(dataset['Age'] > 16) & (dataset['Age'] <= 32), 'Age'] = 1
    dataset.loc[(dataset['Age'] > 32) & (dataset['Age'] <= 48), 'Age'] = 2
    dataset.loc[(dataset['Age'] > 48) & (dataset['Age'] <= 64), 'Age'] = 3
    dataset.loc[ dataset['Age'] > 64, 'Age']
train_df.head()

Copy

SurvivedPclassSexAgeSibSpParchFareEmbarkedTitleAgeBand
00301107.2500S1(16.0, 32.0]
111121071.2833C3(32.0, 48.0]
21311007.9250S2(16.0, 32.0]
311121053.1000S3(32.0, 48.0]
40302008.0500S1(32.0, 48.0]

删除AgeBand

train_df = train_df.drop(['AgeBand'], axis=1)
combine = [train_df, test_df]
train_df.head()

Copy

SurvivedPclassSexAgeSibSpParchFareEmbarkedTitle
00301107.2500S1
111121071.2833C3
21311007.9250S2
311121053.1000S3
40302008.0500S1

创建新特征组合现有特征

我们可以创建一个新的特征家庭规模,它结合了Parch和SibSp。这将使我们能够从数据集中删除Parch和SibSp。

for dataset in combine:
    dataset['FamilySize'] = dataset['SibSp'] + dataset['Parch'] + 1

train_df[['FamilySize', 'Survived']].groupby(['FamilySize'], as_index=False).mean().sort_values(by='Survived', ascending=False)

Copy

FamilySizeSurvived
340.724138
230.578431
120.552795
670.333333
010.303538
450.200000
560.136364
780.000000

我们可以创建另一个特征叫做IsAlone

for dataset in combine:
    dataset['IsAlone'] = 0
    dataset.loc[dataset['FamilySize'] == 1, 'IsAlone'] = 1

train_df[['IsAlone', 'Survived']].groupby(['IsAlone'], as_index=False).mean()

Copy

IsAloneSurvived
000.505650
110.303538

让我们删除Parch、SibSp和家庭规模特征,以支持IsAlone

train_df = train_df.drop(['Parch', 'SibSp', 'FamilySize'], axis=1)
test_df = test_df.drop(['Parch', 'SibSp', 'FamilySize'], axis=1)
combine = [train_df, test_df]

train_df.head()

Copy

SurvivedPclassSexAgeFareEmbarkedTitleIsAlone
003017.2500S10
1111271.2833C30
213117.9250S21
3111253.1000S30
403028.0500S11

我们还可以创建一个结合舱位和年龄的人工特征

for dataset in combine:
    dataset['Age*Class'] = dataset.Age * dataset.Pclass

train_df.loc[:, ['Age*Class', 'Age', 'Pclass']].head(10)

Copy

Age*ClassAgePclass
0313
1221
2313
3221
4623
5313
6331
7003
8313

补全分类特征

登船港口特征的值为S、Q、C,取决于登船港口。我们的训练数据集中有两个缺失值。我们简单地用最常见的值填充这些缺失值。

freq_port = train_df.Embarked.dropna().mode()[0]
freq_port

Copy

'S'

for dataset in combine:
    dataset['Embarked'] = dataset['Embarked'].fillna(freq_port)
    
train_df[['Embarked', 'Survived']].groupby(['Embarked'], as_index=False).mean().sort_values(by='Survived', ascending=False)

Copy

EmbarkedSurvived
0C0.553571
1Q0.389610
2S0.339009

将分类特征转换为数值

我们现在可以通过创建一个新的数值港口特征来转换EmbarkedFill特征。

for dataset in combine:
    dataset['Embarked'] = dataset['Embarked'].map( {'S': 0, 'C': 1, 'Q': 2} ).astype(int)

train_df.head()

Copy

SurvivedPclassSexAgeFareEmbarkedTitleIsAloneAge*Class
003017.25000103
1111271.28331302
213117.92500213
3111253.10000302
403028.05000116

快速补全和转换数值特征

我们现在可以使用众数来补全测试数据集中单个缺失的票价特征,以获得该特征中出现最频繁的值。我们只需一行代码即可完成这一操作。

请注意,我们并没有创建中间的新特征或进行任何进一步的相关性分析来猜测缺失特征,因为我们只是在替换一个值。补全目标实现了模型算法在非空值上操作的要求。

我们也可能希望将票价四舍五入到小数点后两位,因为它代表货币。

test_df['Fare'].fillna(test_df['Fare'].dropna().median(), inplace=True)
test_df.head()

Copy

PassengerIdPclassSexAgeFareEmbarkedTitleIsAloneAge*Class
08923027.82922116
18933127.00000306
28942039.68752116
38953018.66250113
489631112.28750303

创建费用区间FareBand

train_df['FareBand'] = pd.qcut(train_df['Fare'], 4)
train_df[['FareBand', 'Survived']].groupby(['FareBand'], as_index=False).mean().sort_values(by='FareBand', ascending=True)

Copy

FareBandSurvived
0(-0.001, 7.91]0.197309
1(7.91, 14.454]0.303571
2(14.454, 31.0]0.454955
3(31.0, 512.329]0.581081

根据FareBand将票价特征转换为序数值。

for dataset in combine:
    dataset.loc[ dataset['Fare'] <= 7.91, 'Fare'] = 0
    dataset.loc[(dataset['Fare'] > 7.91) & (dataset['Fare'] <= 14.454), 'Fare'] = 1
    dataset.loc[(dataset['Fare'] > 14.454) & (dataset['Fare'] <= 31), 'Fare']   = 2
    dataset.loc[ dataset['Fare'] > 31, 'Fare'] = 3
    dataset['Fare'] = dataset['Fare'].astype(int)

train_df = train_df.drop(['FareBand'], axis=1)
combine = [train_df, test_df]
    
train_df.head(9)

Copy

SurvivedPclassSexAgeFareEmbarkedTitleIsAloneAge*Class
0030100103
1111231302
2131110213
3111230302
4030210116
5030112113
6010330113
7030020400
8131110303
test_df.head(10)

Copy

PassengerIdPclassSexAgeFareEmbarkedTitleIsAloneAge*Class
089230202116
189331200306
289420312116
389530110113
489631110303
589730010110
689831102213
789920120102
890031101313
990130120103

模型、预测和解决

现在我们准备好训练一个模型并预测所需的解决方案。有60多个预测建模算法可供选择。我们必须理解问题的类型和解决方案的要求,以缩小到几个可以评估的模型。我们的问题是分类和回归问题。我们想要识别输出(生存与否)与其他变量或特征(性别、年龄、港口等)之间的关系。我们还在进行一种称为监督学习的机器学习类别,因为我们正在使用给定的数据集来训练我们的模型。基于这两个标准——监督学习加上分类和回归,我们可以将模型选择缩小到几个。这些包括:

  1. 逻辑回归
  2. KNN或k近邻
  3. 支持向量机
  4. 朴素贝叶斯分类器
  5. 决策树
  6. 随机森林
  7. 感知器
  8. 人工神经网络
  9. RVM或相关向量机
X_train = train_df.drop("Survived", axis=1)
Y_train = train_df["Survived"]
X_test  = test_df.drop("PassengerId", axis=1).copy()
X_train.shape, Y_train.shape, X_test.shape

Copy

((891, 8), (891,), (418, 8))

逻辑回归是工作流程中早期运行的有用模型。逻辑回归通过使用逻辑函数(即累积逻辑分布)来估计概率,从而测量分类因变量(特征)与一个或多个自变量(特征)之间的关系。

请注意模型基于我们的训练数据集生成的置信分数。

# Logistic Regression

logreg = LogisticRegression()
logreg.fit(X_train, Y_train)
Y_pred = logreg.predict(X_test)
acc_log = round(logreg.score(X_train, Y_train) * 100, 2)
acc_log

Copy

80.36

logreg.score 方法接受两个参数:

  1. 第一个参数:特征数据集(如 X_test 或 X_train),表示要评估的样本的输入特征。

  2. 第二个参数:真实标签数据集(如 Y_test 或 Y_train),表示对应的真实输出或目标值。

通过这两个参数,logreg.score 方法可以计算模型在给定特征数据集上的准确率,即模型正确预测的样本数与总样本数的比例。

例如:

  • logreg.score(X_train, Y_train):评估模型在训练集上的准确率。
  • logreg.score(X_test, Y_test):评估模型在测试集上的准确率。

我们可以使用逻辑回归来验证我们在特征创建和实现目标方面的假设和决策。这可以通过计算决策函数中各特征的系数来完成。

正系数会增加响应的对数几率(从而增加概率),而负系数会减少响应的对数几率(从而降低概率)。

性别的系数是最高的正系数,这意味着随着性别值的增加(男性:0 到女性:1),生存概率为 1 的可能性增加最多。相反,随着舱位等级(Pclass)的增加,生存概率为 1 的可能性减少最多。

因此,年龄与舱位等级的乘积(Age*Class)是一个很好的人工特征,因为它与生存的负相关性是第二高的。而称谓(Title)则是第二高的正相关性特征。

coeff_df = pd.DataFrame(train_df.columns.delete(0))
coeff_df.columns = ['Feature']
coeff_df["Correlation"] = pd.Series(logreg.coef_[0])

coeff_df.sort_values(by='Correlation', ascending=False)

Copy

FeatureCorrelation
1Sex2.201527
5Title0.398234
2Age0.287163
4Embarked0.261762
6IsAlone0.129140
3Fare-0.085150
7Age*Class-0.311200

logreg.coef_[0] 的作用是获取逻辑回归模型中第一个类别的特征系数。具体来说:

  1. 特征系数:在逻辑回归中,每个特征都有一个对应的系数,这些系数表示该特征对预测结果的影响程度。正系数表示特征与目标变量之间的正相关关系,负系数则表示负相关关系。

  2. 访问方式logreg.coef_ 是一个数组,其中包含所有特征的系数。如果是二分类问题,logreg.coef_ 通常会有两个元素,分别对应两个类别的系数。使用 [0] 访问第一个元素,通常表示第一个类别(例如,生存与否中的“生存”类别)。

  3. 解释模型:通过查看 logreg.coef_[0],可以了解每个特征对模型决策的贡献,从而帮助解释模型的行为和结果。

总之,logreg.coef_[0] 是分析特征重要性和理解模型决策过程的重要工具。

接下来,我们使用支持向量机进行建模,支持向量机是一种监督学习模型,配有分析用于分类和回归分析的数据的学习算法。给定一组训练样本,每个样本标记为属于两个类别之一,SVM训练算法构建一个模型,将新的测试样本分配到一个类别或另一个类别,使其成为一种非概率性二元线性分类器。

请注意,该模型生成的置信度评分高于逻辑回归模型。

# Support Vector Machines

svc = SVC()
svc.fit(X_train, Y_train)
Y_pred = svc.predict(X_test)
acc_svc = round(svc.score(X_train, Y_train) * 100, 2)
acc_svc

Copy

83.84

在模式识别中,k-最近邻算法(简称k-NN)是一种用于分类和回归的非参数方法。样本通过其邻居的多数投票进行分类,样本被分配到其k个最近邻中最常见的类别(k是一个正整数,通常较小)。如果k = 1,则对象简单地被分配到那个单一最近邻的类别。

knn = KNeighborsClassifier(n_neighbors = 3)
knn.fit(X_train, Y_train)
Y_pred = knn.predict(X_test)
acc_knn = round(knn.score(X_train, Y_train) * 100, 2)
acc_knn

Copy

84.74

在机器学习中,朴素贝叶斯分类器是一类基于应用贝叶斯定理并对特征之间的强(朴素)独立性假设的简单概率分类器。朴素贝叶斯分类器具有很高的可扩展性,所需的参数数量与学习问题中的变量(特征)数量成线性关系。参考维基百科。

该模型生成的置信度评分在迄今为止评估的模型中是最低的。

# Gaussian Naive Bayes

gaussian = GaussianNB()
gaussian.fit(X_train, Y_train)
Y_pred = gaussian.predict(X_test)
acc_gaussian = round(gaussian.score(X_train, Y_train) * 100, 2)
acc_gaussian

Copy

72.28

感知器是一种用于监督学习的二元分类器算法(可以决定一个输入(用数字向量表示)是否属于某个特定类别的函数)。它是一种线性分类器,即一种基于将一组权重与特征向量结合的线性预测函数进行预测的分类算法。该算法允许在线学习,因为它一次处理训练集中的一个元素。参考维基百科。

# Perceptron

perceptron = Perceptron()
perceptron.fit(X_train, Y_train)
Y_pred = perceptron.predict(X_test)
acc_perceptron = round(perceptron.score(X_train, Y_train) * 100, 2)
acc_perceptron

Copy

78.0

Linear SVC 指的是线性支持向量分类器(Linear Support Vector Classifier),它是支持向量机(SVM)的一种,用于进行线性可分的分类任务。在这种模型中,数据集样本被分成两类,并且通过一个超平面进行划分。线性SVC的目标是找到一个能够最大化分类间隔的决策边界(即超平面),从而提高模型对样本数据分类的准确性和泛化能力。

线性SVC的优点包括处理高维数据的能力、较好的分类性能和灵活性。由于它只考虑线性划分,因此在处理较大规模的数据集时,训练速度相对较快。此外,线性SVC可以通过调整正则化参数 (C) 来控制模型的复杂度,避免过拟合或欠拟合。通常,它使用凸优化技术来求解最佳超平面。

在实际应用中,线性SVC被广泛用于文本分类、图像识别、以及其他需要快速高效进行线性分类的场合。例如,在文本分类中,由于词袋模型通常是高维稀疏矩阵,线性SVC表现良好。

# Linear SVC

linear_svc = LinearSVC()
linear_svc.fit(X_train, Y_train)
Y_pred = linear_svc.predict(X_test)
acc_linear_svc = round(linear_svc.score(X_train, Y_train) * 100, 2)
acc_linear_svc

Copy

79.12

随机梯度下降(Stochastic Gradient Descent,SGD)是一种优化算法,通常用于训练机器学习模型,尤其是神经网络。与标准梯度下降算法不同,SGD在每次迭代时随机选取一个样本(或一个小批量样本)来更新模型参数,而不是使用整个数据集计算梯度。这种方法有几个明显的优势:

  1. 计算效率高:由于每次只使用一个样本或一个小批量样本,SGD的更新步骤比批量梯度下降快得多,尤其适合大型数据集。

  2. 更快的收敛速度:虽然SGD的路径比批量梯度下降更加崎岖(因为每次更新引入的噪声导致梯度方向波动),但在大多数情况下,它可以加速收敛并帮助跳出局部最小值。

  3. 内存使用减少:因为不需要同时加载整个数据集进行计算,这对处理特别大的数据集时很有帮助。

然而,SGD也有一些挑战,比如收敛过程中波动较大、不易精确找到最优点。为此,引入了诸如动量(Momentum)、学习率调节(如RMSprop和Adam)等技巧,帮助改进SGD的性能和稳定性。通过这些改进,SGD成为了深度学习中的一个核心工具,被广泛用于各种复杂模型的训练。

# Stochastic Gradient Descent

sgd = SGDClassifier()
sgd.fit(X_train, Y_train)
Y_pred = sgd.predict(X_test)
acc_sgd = round(sgd.score(X_train, Y_train) * 100, 2)
acc_sgd

Copy

78.56

该模型使用决策树作为预测模型,将特征(树枝)映射到关于目标值的结论(树叶)。目标变量可以取有限值集的树模型称为分类树;在这些树结构中,叶子表示类别标签,树枝表示导致这些类别标签的特征的结合。目标变量可以取连续值(通常是实数)的决策树称为回归树。参考维基百科。

# Decision Tree

decision_tree = DecisionTreeClassifier()
decision_tree.fit(X_train, Y_train)
Y_pred = decision_tree.predict(X_test)
acc_decision_tree = round(decision_tree.score(X_train, Y_train) * 100, 2)
acc_decision_tree

Copy

86.76

下一个模型随机森林是最受欢迎的模型之一。随机森林或随机决策森林是一种用于分类、回归和其他任务的集成学习方法,通过在训练时构建大量决策树(n_estimators=100)并输出类别的众数(分类)或个别树的平均预测(回归)来操作。参考维基百科。

该模型的置信度评分在迄今为止评估的模型中是最高的。

# Random Forest

random_forest = RandomForestClassifier(n_estimators=100)
random_forest.fit(X_train, Y_train)
Y_pred = random_forest.predict(X_test)
random_forest.score(X_train, Y_train)
acc_random_forest = round(random_forest.score(X_train, Y_train) * 100, 2)
acc_random_forest

Copy

86.76

模型评估¶

现在我们可以对所有模型的评估进行排名,以选择最适合我们问题的模型。尽管决策树和随机森林得分相同,但我们选择使用随机森林,因为它可以纠正决策树对训练集过拟合的习惯。

models = pd.DataFrame({
    'Model': ['Support Vector Machines', 'KNN', 'Logistic Regression', 
              'Random Forest', 'Naive Bayes', 'Perceptron', 
              'Stochastic Gradient Decent', 'Linear SVC', 
              'Decision Tree'],
    'Score': [acc_svc, acc_knn, acc_log, 
              acc_random_forest, acc_gaussian, acc_perceptron, 
              acc_sgd, acc_linear_svc, acc_decision_tree]})
models.sort_values(by='Score', ascending=False)

Copy

ModelScore
3Random Forest86.76
8Decision Tree86.76
1KNN84.74
0Support Vector Machines83.84
2Logistic Regression80.36
7Linear SVC79.12
6Stochastic Gradient Decent78.56
5Perceptron78.00
submission = pd.DataFrame({
        "PassengerId": test_df["PassengerId"],
        "Survived": Y_pred
    })
# submission.to_csv('../output/submission.csv', index=False)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值