In [1]:
%matplotlib notebook
https://www.kaggle.com/c/titanic
In [3]:
import pandas
titanic = pandas.read_csv("titanic_train.csv")
titanic.head()
Out[3]:
In [4]:
print(titanic.describe())
In [6]:
titanic["Age"] = titanic["Age"].fillna(titanic["Age"].median())
print(titanic.describe())
In [7]:
print(titanic["Sex"].unique())
In [8]:
# 对男女进行编号
titanic.loc[titanic["Sex"] == "male", "Sex"] = 0
titanic.loc[titanic["Sex"] == "female", "Sex"] = 1
除了Sex字段,还有Embarked也不是数值的,我们也需要进行转换
In [9]:
print(titanic["Embarked"].unique())
titanic["Embarked"] = titanic["Embarked"].fillna('S')
titanic.loc[titanic["Embarked"] == "S", "Embarked"] = 0
titanic.loc[titanic["Embarked"] == "C", "Embarked"] = 1
titanic.loc[titanic["Embarked"] == "Q", "Embarked"] = 2
String值得填充我们是用多的填,这里$S$最多,因此用它填。 现在我们数据预处理结束后,可以做二分类看看到底是获救了还是没获救。 最简单的方法是线性回归。
In [13]:
# import the linear regression class
from sklearn.linear_model import LinearRegression
# 交叉验证
from sklearn.cross_validation import KFold
# 选择特征
predictors = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked']
# Initialize our algorithm class
alg = LinearRegression()
# titanic.shape返回[m, n],m是多少个;n_folds做几倍的交叉验证;random_state表示每次切分都是相同的
kf = KFold(titanic.shape[0], n_folds=3, random_state=1)
predictions = []
for train, test in kf:
#先拿到特征
train_predictors = (titanic[predictors].iloc[train, :])
#拿到真实的label
train_target = titanic["Survived"].iloc[train]
#用线性回归应用到数据上
alg.fit(train_predictors, train_target)
#训练完了就可以做预测了
test_predictions = alg.predict(titanic[predictors].iloc[test,:])
predictions.append(test_predictions)
用pandas和sklearn做机器学习还是很简单的,流程就是:处理数据->选择特征->选择算法->多次交叉验证->最后把结果给append进来。 线性回归得到我们的结果是在区间$[0,1]$上的某一个值,我们怎么把区间值转换为类别呢?我们可以认为小于0.5认为是没获救,大于0.5认为获救了。
In [14]:
import numpy as np
# 预测在3个分开的numpy array中。把它合并在一起。
# 我们在axis 0上拼接它们。因为它们只有一个轴。
predictions = np.concatenate(predictions, axis=0)
predictions[predictions > 0.5] = 1
predictions[predictions <= 0.5] = 0
accuracy = sum(predictions[predictions == titanic['Survived']]) / len(predictions)
print(accuracy)
模型准确率是拿预测准确的值除以总的预测结果。我们的准确率是0.78,比全靠蒙的50%高不了多少。我们可以使用更高级的算法。逻辑回归能够直接得出某只的概率。
In [16]:
from sklearn import cross_validation
from sklearn.linear_model import LogisticRegression
alg = LogisticRegression(random_state = 1)
#计算每个交叉验证的准确值
scores = cross_validation.cross_val_score(alg, titanic[predictors], titanic["Survived"], cv=3)
#获取均值
print(scores.mean())
虽然叫逻辑回归,但是我们一般用它做分类。现在的结果还是0.78多。
In [17]:
from sklearn import cross_validation
from sklearn.ensemble import RandomForestClassifier
predictors = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked']
# n_estimators 是我们想构造的树数量
# min_samples_split 节点进行分裂,最小的节点数量
# min_samples_leaf 最小叶子节点个数
alg = RandomForestClassifier(random_state=1, n_estimators=10, min_samples_split=2, min_samples_leaf=1)
kf = cross_validation.KFold(titanic.shape[0], n_folds=3, random_state=1)
scores = cross_validation.cross_val_score(alg, titanic[predictors], titanic['Survived'], cv=kf)
print(scores.mean())
我们看到样本只有0.78的数量,这是因为我们的随机森林个数偏少的原因。我们可以增加决策树来试试,并且决策树也更深一些。
In [18]:
alg = RandomForestClassifier(random_state=1, n_estimators=50, min_samples_split=4, min_samples_leaf=2)
kf = cross_validation.KFold(titanic.shape[0], n_folds=3, random_state=1)
scores = cross_validation.cross_val_score(alg, titanic[predictors], titanic['Survived'], cv=kf)
print(scores.mean())
现在我们看到决策树的准确率到了0.81了。这也说明参数的调节也非常重要。
我们知道,一个机器学习的项目不可能特征这么明显,我们需要做的第一件事情就是提取特征。现在我们能不能再提取一些别的项目。比如我们来看家庭成员数量,数量越多说不定逃生几率越大。
然后我们看看名字长短是不是也有关系。
In [20]:
# 生成家庭列
titanic["FamilySize"] = titanic["SibSp"] + titanic["Parch"]
# apply方法用来产生新的series
titanic["NameLength"] = titanic["Name"].apply(lambda x: len(x))
还有就是名字当中含有的一些职业属性,也可以提取出来作为一种特征。
In [25]:
import re
# 从名字中获取title
def get_title(name):
title_search = re.search('([A-Za-z]+)\.', name)
if title_search:
return title_search.group(1)
return ""
titles = titanic["Name"].apply(get_title)
print(pandas.value_counts(titles))
title_mapping = {"Mr": 1, "Miss": 2, "Mrs":3, "Master": 4, "Dr": 5, "Rev": 6, "Major": 7, "Mlle": 8, "Col": 9, "Capt": 10, "Ms": 11,
"Don": 12, "Sir": 13, "Lady": 14, "Johkheer": 15, "Countess": 16, "Mme": 17, "Jonkheer": 18}
for k, v in title_mapping.items():
titles[titles==k] = v
print(pandas.value_counts(titles))
titanic["Title"] = titles
In [ ]:
Feature importance。每个特征的作用有多大,我们需要关注一下。我们有891个数据,我们用随机森林有放回采样,那肯定还有很多没有采到的数据,假如我们做了800次有放回采样,其中有300个没有采过。我们叫这800个数据为in bag数据(袋内数据),300个叫out of bag数据(代外数据)。那么我们就能拿这300个数据做测试数据了。 比如Age列,我们对这个列什么也不做,用来跑随机模型,看看错误率$error_1$。然后我们给这个列值加上一些噪音数据,同时保证其他列加上噪音值,然后再获得错误率$error_2$,如果$error_2$明显上升,表示Age列是非常重要的,如果没发生多大变化,表示Age列对最终结果没有多大影响。
In [26]:
import numpy as np
from sklearn.feature_selection import SelectKBest, f_classif
import matplotlib.pyplot as plt
predictors = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked', 'FamilySize', 'Title', 'NameLength']
# 执行特征选择
selector = SelectKBest(f_classif, k=5)
selector.fit(titanic[predictors], titanic["Survived"])
# 获得每个特征的原始p-Value,然后转换成值
scores = -np.log10(selector.pvalues_)
# 画出分值
plt.bar(range(len(predictors)), scores)
plt.xticks(range(len(predictors)), predictors, rotation='vertical')
plt.show()
# 选出4个最佳特征
predictors = ['Pclass', 'Sex', 'Fare', 'Title']
alg = RandomForestClassifier(random_state=1, n_estimators=50, min_samples_split=8, min_samples_leaf=4)
我们使用多个算法同时做。
In [29]:
from sklearn.ensemble import GradientBoostingClassifier
import numpy as np
algorithms = [
[GradientBoostingClassifier(random_state=1, n_estimators=25, max_depth=3), ['Pclass', 'Sex', 'Fare', 'FamilySize', 'Title', 'Age', 'Embarked']],
[LogisticRegression(random_state=1), ['Pclass', 'Sex', 'Fare', 'FamilySize', 'Title', 'Age', 'Embarked']]
]
kf = KFold(titanic.shape[0], n_folds=3, random_state=1)
predictions = []
for train, test in kf:
train_target = titanic['Survived'].iloc[train]
full_test_predictions = []
for alg, predictors in algorithms:
alg.fit(titanic[predictors].iloc[train,:], train_target)
# .astype(float) 用来转换DataFrame为浮点类型
test_predictions = alg.predict_proba(titanic[predictors].iloc[test, :].astype(float))[:, 1]
full_test_predictions.append(test_predictions)
# 两种算法平均,也可以加上权重
test_predictions = (full_test_predictions[0] + full_test_predictions[1]) / 2
test_predictions[test_predictions <= .5] = 0
test_predictions[test_predictions > .5] = 1
predictions.append(test_predictions)
predictions = np.concatenate(predictions, axis=0)
accuracy = sum(predictions[predictions == titanic["Survived"]]) / len(predictions)
print(accuracy)
In [ ]: