当前位置: 代码迷 >> 综合 >> Kaggle:Titanic问题
  详细解决方案

Kaggle:Titanic问题

热度:103   发布时间:2023-11-14 00:12:22.0

Kaggle:Titanic问题

相关库函数操作

1.pandas的read_csv函数

读取csv文件为DataFrame格式

from pandas import DataFrame
data_train = pd.read_csv("Titanic/train.csv")
data_train

输出结果:会自动将第一行处理为label

这里写图片描述

2.DataFrame.info()函数

DataFrame.info()可以发现一些数据信息,计算特征非空值的个数,以及数据类型·

data_train.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: 66.2+ KB

3.DataFrame.describe()函数

得到数值型数据的一些信息,数值数,平均值,极值,标准差

data_train.describe()

输出结果:

这里写图片描述

4.value_counts()离散型变量计数函数

对于DataFrame里的特定label可以通过value_counts()函数来统计每个种类的个数

data_train.Cabin.value_counts()

输出结果:

C23 C25 C27        4
G6                 4
B96 B98            4
D                  3
C22 C26            3
E101               3
F2                 3
F33                3
B57 B59 B63 B66    2
C68                2
B58 B60            2
E121               2..
E67                2
D35                2
D36                2
C52                2
F4                 2
C125               2
C124               2..
F G63              1
A6                 1
D45                1
D6                 1
D56                1
C101               1
C54                1
D28                1
D37                1
Name: Cabin, Length: 147, dtype: int64

也可以用图表输出

    fig = plt.figure()data_train.Survived.value_counts().plot(kind='bar') #柱状图显示plt.title('Survived situation')plt.show()data_train.Pclass.value_counts().plot(kind='bar')plt.title('Passenger class')plt.show()

输出结果:

这里写图片描述

示例:

femalehigh = data_train.Survived[data_train.Sex == 'female'][data_train.Pclass != 3].value_counts()
femalehigh.plot(kind='bar', label='female highclass', color='#FA2479')
plt.legend(["female/highclass"],loc='best')
plt.show()

输出结果:

这里写图片描述

5.pyplot.subplot2grid()绘制子图函数

控制总的size与当前图的方位

plt.subplot2grid((2,3),(0,0)) #设置2*3的子图,该图位置在(0,0)处
plt.subplot2grid((2,3),(0,1))
plt.subplot2grid((2,3),(0,2))
plt.subplot2grid((2,3),(1,0), colspan=2) #设置所占列宽为2
pil.subplot2grid((2,3),(1,2))
plt.show()

大致形状如下

这里写图片描述

6.连续型变量计数函数

data_train.Age.plot(kind='kde')
plt.show()

输出结果:

这里写图片描述

同样根据DataFrame的特性,可以继续对特定类别计数

输出‘kde’密度曲线图

data_train.Age[data_train.Pclass == 1].plot(kind='kde')   
data_train.Age[data_train.Pclass == 2].plot(kind='kde')
data_train.Age[data_train.Pclass == 3].plot(kind='kde')
plt.xlabel("Age")# plots an axis lable
plt.ylabel("Density") 
plt.title("Passenger's age for class")
plt.legend(('First', 'Second','Third'),loc='best') # sets legend for graph.
plt.show()

输出结果:

这里写图片描述

7.计数两个离散变量个数,并用DataFrame的’构造函数’

Survived_0 = data_train.Pclass[data_train.Survived == 0].value_counts()
#返回的是左名右值的映射
Survived_1 = data_train.Pclass[data_train.Survived == 1].value_counts()
Survived_0
3    372
2     97
1     80
Name: Pclass, dtype: int64
df = pd.DataFrame({
   'Survived':Survived_1,'Unsurvived':Survived_0}) 
#dataFrame图表格式
   Survived  Unsurvived
1       136          80
2        87          97
3       119         372
df.plot(kind = 'bar', stacked = True)
#输出图像stacked属性表示叠置

输出结果:

这里写图片描述

8.DataFrame.groupby()函数

具体每个特征每个属性计数:

g = data_train.groupby(['SibSp','Survived'])
df = pd.DataFrame(g.count())

这里写图片描述

g = data_train.groupby(['SibSp','Survived','Pclass'])
df = pd.DataFrame(g.count()['PassengerId']) #只计数这一个属性
df

这里写图片描述

9.从DataFrame中提取部分数据

sub_df = data_train[['Age','Fare','Parch','SibSp','Pclass']]

输出结果:

           Age      Fare  Parch  SibSp  Pclass
0    22.000000    7.2500      0      1       3
1    38.000000   71.2833      0      1       1
2    26.000000    7.9250      0      0       3
3    35.000000   53.1000      0      1       1
4    35.000000    8.0500      0      0       3
5    23.828953    8.4583      0      0       3
6    54.000000   51.8625      0      0       1
7     2.000000   21.0750      1      3       3
8    27.000000   11.1333      2      0       3
9    14.000000   30.0708      0      1       2
10    4.000000   16.7000      1      1       3
11   58.000000   26.5500      0      0       1
12   20.000000    8.0500      0      0       3
13   39.000000   31.2750      5      1       3
14   14.000000    7.8542      0      0       3
15   55.000000   16.0000      0      0       2
16    2.000000   29.1250      1      4       3
17   32.066493   13.0000      0      0       2
18   31.000000   18.0000      0      1       3
19   29.518205    7.2250      0      0       3
20   35.000000   26.0000      0      0       2

10.DataFrame.as_matrix()

将DataFrame类型转换为matrix类型以便用于模型fit

    known_age = age_df[age_df.Age.notnull()].as_matrix()unknown_age = age_df[age_df.Age.isnull()].as_matrix()y = known_age[:, 0]# y即目标年龄X = known_age[:, 1:]# X即特征属性值`

11.DataFrame.loc用于替换属性值

用法如下

df.loc[ (df.Age.isnull()), 'Age' ] = predictedAges
df.loc[ (df.Cabin.notnull()), 'Cabin' ] = "Yes"
#小括号里的为判断语句,满足条件的属性值被替换,可以list替换list,或者都替换为一个值

12.pandas.get_dummies()归一化函数

DataFrame类型的属性每个特征分出来命名,归一化

prefix即表示命名新属性的前缀

形成一个新的DataFrame

dummies_Cabin = pd.get_dummies(data_train['Cabin'], prefix= 'Cabin')
dummies_Cabin 

输出结果:

这里写图片描述

13.pandas.concat()函数合并DataFrame

把几个DataFrame按照axis=0(index)axis =1(columns)合并形成新的df

df = pd.concat([data_train, dummies_Cabin, dummies_Embarked, dummies_Sex, dummies_Pclass], axis=1)

输出结果:
这里写图片描述

14.DataFrame.drop()删去DataFrame某些属性

参数:

labels : single label or list-like

axis : int or axis name

level : int or level name, default None,For MultiIndex

inplace : bool, default False,If True, do operation inplace and return None

df.drop(['Pclass', 'Name', 'Sex', 'Ticket', 'Cabin', 'Embarked'], axis=1, inplace=True)

15.利用sklearn中的preprocessing对连续型数据特征化scaling

各属性值之间scale差距太大,将对收敛速度造成很大影响!甚至不收敛!

所以我们将一些变化幅度较大的特征化到[-1,1]之内。

scaler.fit(),scaler.fit_transform()两个函数实现

DataFrame添加属性列的操作直接写就好,eg:df[‘Age’]

import sklearn.preprocessing as preprocessing
scaler = preprocessing.StandardScaler()age_scale_param = scaler.fit(df['Age'])
df['Age_scaled'] = scaler.fit_transform(df['Age'], age_scale_param)fare_scale_param = scaler.fit(df['Fare'])
df['Fare_scaled'] = scaler.fit_transform(df['Fare'], fare_scale_param)
df

16.利用filter()与正则表达式,从DataFrame中取出需要的属性值形成新的DataFrame

train_df = df.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')

17.利用as_matrix()将DataFrame转换成可供numpy操作的list(矩阵)类型

train_np = train_df.as_matrix()

并完成切片化操作,形成特征矩阵X,y,用于下一步fit撸模型

y = train_np[:, 0] # y即Survival结果
X = train_np[:, 1:]# X即特征属性值

18.DataFrame的构造函数创建DataFrame

result = pd.DataFrame({
   'PassengerId':data_test['PassengerId'].as_matrix(), 'Survived':predictions.astype(np.int32)})

输出结果:

这里写图片描述

19.DataFrame.to_csv()函数导出csv文件

result.to_csv("Titanic/logistic_regression_predictions.csv", index=False)

index表示文件是否显示index

20.sklearn的cross_validation模块

利用模块中的cross_val_score()对分类器评分

from sklearn import cross_validation
cross_vallidation.cross_val_score(clf, X, y,cv=5) #5折交叉验证
输出结果:
[ 0.81564246 0.81564246 0.78651685 0.78651685 0.81355932]

21.cross_validation分割数据

按照7:3的比例将训练数据分成train数据,cv数据

split_train, split_cv = cross_validation.train_test_split(df, test_size=0.3, random_state=0)

进行预测

1.LogisticRegression

导入数据,相关模块(略去可视化分析过程)

import pandas as pd
import numpy as np
from pandas import Series,DataFramedata_train = pd.read_csv("Titanic/train.csv") #文件读成DataFrame格式

数据预处理

#Step1:缺失值处理
#用scikit-learn中的RandomForest来拟合一下缺失的年龄数据
from sklearn.ensemble import RandomForestRegressor
# 使用 RandomForestClassifier 填补缺失的年龄属性
def set_missing_ages(df):# 把已有的数值型特征取出来丢进Random Forest Regressor中age_df = df[['Age','Fare', 'Parch', 'SibSp', 'Pclass']]# 乘客分成已知年龄和未知年龄两部分known_age = age_df[age_df.Age.notnull()].as_matrix()unknown_age = age_df[age_df.Age.isnull()].as_matrix()y = known_age[:, 0]# y即目标年龄X = known_age[:, 1:]# X即特征属性值rfr = RandomForestRegressor(random_state=0, n_estimators=2000, n_jobs=-1)# fit到RandomForestRegressor之中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 dfdata_train, rfr = set_missing_ages(data_train)
data_train = set_Cabin_type(data_train)#Step2,对类目型特征归一化(因子化)
#使用pandas.get_dummies
dummies_Cabin = pd.get_dummies(data_train['Cabin'], prefix= 'Cabin')
dummies_Embarked = pd.get_dummies(data_train['Embarked'], prefix= 'Embarked')
dummies_Sex = pd.get_dummies(data_train['Sex'], prefix= 'Sex')
dummies_Pclass = pd.get_dummies(data_train['Pclass'], prefix= 'Pclass')
df = pd.concat([data_train, dummies_Cabin, dummies_Embarked, dummies_Sex, dummies_Pclass], axis=1)
df.drop(['Pclass', 'Name', 'Sex', 'Ticket', 'Cabin', 'Embarked'], axis=1, inplace=True)#Step3,对连续型特征数值scaling
#各属性值之间scale差距太大,将对收敛速度造成几万点伤害值!甚至不收敛!
#所以我们将一些变化幅度较大的特征化到[-1,1]之内。
import sklearn.preprocessing as preprocessing
scaler = preprocessing.StandardScaler()
age_scale_param = scaler.fit(df['Age'])
df['Age_scaled'] = scaler.fit_transform(df['Age'], age_scale_param)
fare_scale_param = scaler.fit(df['Fare'])
df['Fare_scaled'] = scaler.fit_transform(df['Fare'], fare_scale_param)

逻辑回归建模

#逻辑回归建模
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 = train_np[:, 0]# y即Survival结果
X = train_np[:, 1:]# X即特征属性值
# fit到RandomForestRegressor之中
clf = linear_model.LogisticRegression(C=1.0, penalty='l1', tol=1e-6)
clf.fit(X, y)

预处理测试数据

data_test = pd.read_csv("Titanic/test.csv")
data_test.loc[ (data_test.Fare.isnull()), 'Fare' ] = 0
# 接着我们对test_data做和train_data中一致的特征变换
# 首先用同样的RandomForestRegressor模型填上丢失的年龄
tmp_df = data_test[['Age','Fare', 'Parch', 'SibSp', 'Pclass']]
null_age = tmp_df[data_test.Age.isnull()].as_matrix()
# 根据特征属性X预测年龄并补上
X = null_age[:, 1:]
predictedAges = rfr.predict(X)
data_test.loc[ (data_test.Age.isnull()), 'Age' ] = predictedAgesdata_test = set_Cabin_type(data_test)
dummies_Cabin = pd.get_dummies(data_test['Cabin'], prefix= 'Cabin')
dummies_Embarked = pd.get_dummies(data_test['Embarked'], prefix= 'Embarked')
dummies_Sex = pd.get_dummies(data_test['Sex'], prefix= 'Sex')
dummies_Pclass = pd.get_dummies(data_test['Pclass'], prefix= 'Pclass')df_test = pd.concat([data_test, dummies_Cabin, dummies_Embarked, dummies_Sex, dummies_Pclass], axis=1)
df_test.drop(['Pclass', 'Name', 'Sex', 'Ticket', 'Cabin', 'Embarked'], axis=1, inplace=True)
df_test['Age_scaled'] = scaler.fit_transform(df_test['Age'], age_scale_param)
df_test['Fare_scaled'] = scaler.fit_transform(df_test['Fare'], fare_scale_param)

导出基础的预测结果

test=df_test.filter(regex='Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')
predictions = clf.predict(test)
result = pd.DataFrame({
   'PassengerId':data_test['PassengerId'].as_matrix(), 'Survived':predictions.astype(np.int32)})
result.to_csv("Titanic/logistic_regression_predictions.csv", index=False)

查看拟合情况,判定一下当前模型所处状态(欠拟合or过拟合)


from sklearn.learning_curve import learning_curve# 用sklearn的learning_curve得到training_score和cv_score,使用matplotlib画出learning curve
def plot_learning_curve(estimator, title, X, y, ylim=None, cv=None, n_jobs=1, train_sizes=np.linspace(.05, 1., 20), verbose=0, plot=True):"""画出data在某模型上的learning curve.参数解释----------estimator : 你用的分类器。title : 表格的标题。X : 输入的feature,numpy类型y : 输入的target vectorylim : tuple格式的(ymin, ymax), 设定图像中纵坐标的最低点和最高点cv : 做cross-validation的时候,数据分成的份数,其中一份作为cv集,其余n-1份作为training(默认为3份)n_jobs : 并行的的任务数(默认1)"""train_sizes, train_scores, test_scores = learning_curve(estimator, X, y, cv=cv, n_jobs=n_jobs, train_sizes=train_sizes, verbose=verbose)train_scores_mean = np.mean(train_scores, axis=1)train_scores_std = np.std(train_scores, axis=1)test_scores_mean = np.mean(test_scores, axis=1)test_scores_std = np.std(test_scores, axis=1)if plot:plt.figure()plt.title(title)if ylim is not None:plt.ylim(*ylim)plt.xlabel('The number of training samples')plt.ylabel('score')plt.gca().invert_yaxis()plt.grid()plt.fill_between(train_sizes, train_scores_mean - train_scores_std, train_scores_mean + train_scores_std, alpha=0.1, color="b")plt.fill_between(train_sizes, test_scores_mean - test_scores_std, test_scores_mean + test_scores_std, alpha=0.1, color="r")plt.plot(train_sizes, train_scores_mean, 'o-', color="b", label='training score')plt.plot(train_sizes, test_scores_mean, 'o-', color="r", label='CV score')plt.legend(loc="best")plt.draw()plt.gca().invert_yaxis()plt.show()midpoint = ((train_scores_mean[-1] + train_scores_std[-1]) + (test_scores_mean[-1] - test_scores_std[-1])) / 2diff = (train_scores_mean[-1] + train_scores_std[-1]) - (test_scores_mean[-1] - test_scores_std[-1])return midpoint, diffplot_learning_curve(clf, 'learning curves', X, y)

这里写图片描述

在实际数据上看,我们得到的learning curve没有理论推导的那么光滑哈,但是可以大致看出来,训练集和交叉验证集上的得分曲线走势还是符合预期的。

目前的曲线看来,我们的model并不处于overfitting的状态(overfitting的表现一般是训练集上得分高,

而交叉验证集上要低很多,中间的gap比较大)。因此我们可以再做些feature engineering的工作,添加一些新产出的特征或者组合特征到模型中。

看看相关程度

pd.DataFrame({
   "columns":list(train_df.columns)[1:], "coef":list(clf.coef_.T)})

上面的系数和最后的结果是一个正相关的关系

我们先看看那些权重绝对值非常大的feature,在我们的模型上:

?Sex属性,如果是female会极大提高最后获救的概率,而male会很大程度拉低这个概率。

?Pclass属性,1等舱乘客最后获救的概率会上升,而乘客等级为3会极大地拉低这个概率。

?有Cabin值会很大程度拉升最后获救概率(这里似乎能看到了一点端倪,事实上从最上面的有无Cabin记录的Survived分布图上看出,即使有Cabin记录的乘客也有一部分遇难了,估计这个属性上我们挖掘还不够)

?Age是一个负相关,意味着在我们的模型里,年龄越小,越有获救的优先权(还得回原数据看看这个是否合理)

?有一个登船港口S会很大程度拉低获救的概率,另外俩港口压根就没啥作用(这个实际上非常奇怪,因为我们从之前的统计图上并没有看到S港口的获救率非常低,所以也许可以考虑把登船港口这个feature去掉试试)。

?船票Fare有小幅度的正相关(并不意味着这个feature作用不大,有可能是我们细化的程度还不够,举个例子,说不定我们得对它离散化,再分至各个乘客等级上?)

怎么样才知道,哪些优化的方法是promising的呢?

进行交叉验证

from sklearn import cross_validation# 简单看看打分情况
clf = linear_model.LogisticRegression(C=1.0, penalty='l1', tol=1e-6)
all_data = df.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')
X = all_data.as_matrix()[:,1:]
y = all_data.as_matrix()[:,0]
print cross_validation.cross_val_score(clf, X, y, cv=5)split_train, split_cv = cross_validation.train_test_split(df, test_size=0.3, random_state=0)
train_df = split_train.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')
# 生成模型
clf = linear_model.LogisticRegression(C=1.0, penalty='l1', tol=1e-6)
clf.fit(train_df.as_matrix()[:,1:], train_df.as_matrix()[:,0])cv_df=split_cv.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')
predictions = clf.predict(cv_df.as_matrix()[:,1:])origin_data_train = pd.read_csv("Titanic/Train.csv")
bad_cases=origin_data_train.loc[origin_data_train['PassengerId'].isin(split_cv[predictions != cv_df.as_matrix()[:,0]]['PassengerId'].values)]
bad_cases

对比bad case,我们仔细看看我们预测错的样本,到底是哪些特征有问题,咱们处理得还不够细?

我们随便列一些可能可以做的优化操作:

?Age属性不使用现在的拟合方式,而是根据名称中的『Mr』『Mrs』『Miss』等的平均值进行填充。

?Age不做成一个连续值属性,而是使用一个步长进行离散化,变成离散的类目feature。

?Cabin再细化一些,对于有记录的Cabin属性,我们将其分为前面的字母部分(我猜是位置和船层之类的信息) 和 后面的数字部分(应该是房间号,有意思的事情是,如果你仔细看看原始数据,你会发现,这个值大的情况下,似乎获救的可能性高一些)。

?Pclass和Sex俩太重要了,我们试着用它们去组出一个组合属性来试试,这也是另外一种程度的细化。

?单加一个Child字段,Age<=12的,设为1,其余为0(你去看看数据,确实小盆友优先程度很高啊)

?如果名字里面有『Mrs』,而Parch>1的,我们猜测她可能是一个母亲,应该获救的概率也会提高,因此可以多加一个Mother字段,此种情况下设为1,其余情况下设为0

?登船港口可以考虑先去掉试试(Q和C本来就没权重,S有点诡异)

?把堂兄弟/兄妹 和 Parch 还有自己 个数加在一起组一个Family_size字段(考虑到大家族可能对最后的结果有影响)

?Name是一个我们一直没有触碰的属性,我们可以做一些简单的处理,比如说男性中带某些字眼的(‘Capt’, ‘Don’, ‘Major’, ‘Sir’)可以统一到一个Title,女性也一样。

大家接着往下挖掘,可能还可以想到更多可以细挖的部分。我这里先列这些了,然后我们可以使用手头上的”train_df”和”cv_df”开始试验这些feature engineering的tricks是否有效了。

分析后优化代码

ata_train = pd.read_csv("Titanic/Train.csv")
data_train['Sex_Pclass'] = data_train.Sex + "_" + data_train.Pclass.map(str)from sklearn.ensemble import RandomForestRegressor### 使用 RandomForestClassifier 填补缺失的年龄属性
def set_missing_ages(df):# 把已有的数值型特征取出来丢进Random Forest Regressor中age_df = df[['Age','Fare', 'Parch', 'SibSp', 'Pclass']]# 乘客分成已知年龄和未知年龄两部分known_age = age_df[age_df.Age.notnull()].as_matrix()unknown_age = age_df[age_df.Age.isnull()].as_matrix()# 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, rfrdef set_Cabin_type(df):df.loc[ (df.Cabin.notnull()), 'Cabin' ] = "Yes"df.loc[ (df.Cabin.isnull()), 'Cabin' ] = "No"return dfdata_train, rfr = set_missing_ages(data_train)
data_train = set_Cabin_type(data_train)dummies_Cabin = pd.get_dummies(data_train['Cabin'], prefix= 'Cabin')
dummies_Embarked = pd.get_dummies(data_train['Embarked'], prefix= 'Embarked')
dummies_Sex = pd.get_dummies(data_train['Sex'], prefix= 'Sex')
dummies_Pclass = pd.get_dummies(data_train['Pclass'], prefix= 'Pclass')
dummies_Sex_Pclass = pd.get_dummies(data_train['Sex_Pclass'], prefix= 'Sex_Pclass')df = pd.concat([data_train, dummies_Cabin, dummies_Embarked, dummies_Sex, dummies_Pclass, dummies_Sex_Pclass], axis=1)
df.drop(['Pclass', 'Name', 'Sex', 'Ticket', 'Cabin', 'Embarked', 'Sex_Pclass'], axis=1, inplace=True)
import sklearn.preprocessing as preprocessing
scaler = preprocessing.StandardScaler()
age_scale_param = scaler.fit(df['Age'])
df['Age_scaled'] = scaler.fit_transform(df['Age'], age_scale_param)
fare_scale_param = scaler.fit(df['Fare'])
df['Fare_scaled'] = scaler.fit_transform(df['Fare'], fare_scale_param)from sklearn import linear_modeltrain_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)data_test = pd.read_csv("Titanic/test.csv")
data_test.loc[ (data_test.Fare.isnull()), 'Fare' ] = 0
data_test['Sex_Pclass'] = data_test.Sex + "_" + data_test.Pclass.map(str)
# 接着我们对test_data做和train_data中一致的特征变换
# 首先用同样的RandomForestRegressor模型填上丢失的年龄
tmp_df = data_test[['Age','Fare', 'Parch', 'SibSp', 'Pclass']]
null_age = tmp_df[data_test.Age.isnull()].as_matrix()
# 根据特征属性X预测年龄并补上
X = null_age[:, 1:]
predictedAges = rfr.predict(X)
data_test.loc[ (data_test.Age.isnull()), 'Age' ] = predictedAgesdata_test = set_Cabin_type(data_test)
dummies_Cabin = pd.get_dummies(data_test['Cabin'], prefix= 'Cabin')
dummies_Embarked = pd.get_dummies(data_test['Embarked'], prefix= 'Embarked')
dummies_Sex = pd.get_dummies(data_test['Sex'], prefix= 'Sex')
dummies_Pclass = pd.get_dummies(data_test['Pclass'], prefix= 'Pclass')
dummies_Sex_Pclass = pd.get_dummies(data_test['Sex_Pclass'], prefix= 'Sex_Pclass')df_test = pd.concat([data_test, dummies_Cabin, dummies_Embarked, dummies_Sex, dummies_Pclass, dummies_Sex_Pclass], axis=1)
df_test.drop(['Pclass', 'Name', 'Sex', 'Ticket', 'Cabin', 'Embarked', 'Sex_Pclass'], axis=1, inplace=True)
df_test['Age_scaled'] = scaler.fit_transform(df_test['Age'], age_scale_param)
df_test['Fare_scaled'] = scaler.fit_transform(df_test['Fare'], fare_scale_param)test=df_test.filter(regex='Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass.*')
predictions = clf.predict(test)
result = pd.DataFrame({
   'PassengerId':data_test['PassengerId'].as_matrix(), 'Survived':predictions.astype(np.int32)})
result.to_csv("logistic_regression_predictions2.csv", index=False)

Bagging算法优化模型

from sklearn.ensemble import BaggingRegressortrain_df = df.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass.*|Mother|Child|Family|Title')
train_np = train_df.as_matrix()# y即Survival结果
y = train_np[:, 0]# X即特征属性值
X = train_np[:, 1:]# fit到BaggingRegressor之中
clf = linear_model.LogisticRegression(C=1.0, penalty='l1', tol=1e-6)
bagging_clf = BaggingRegressor(clf, n_estimators=10, max_samples=0.8, max_features=1.0, bootstrap=True, bootstrap_features=False, n_jobs=-1)
bagging_clf.fit(X, y)test = df_test.filter(regex='Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass.*|Mother|Child|Family|Title')
predictions = bagging_clf.predict(test)
result = pd.DataFrame({
   'PassengerId':data_test['PassengerId'].as_matrix(), 'Survived':predictions.astype(np.int32)})
result.to_csv("Titanic/logistic_regression_predictions2.csv", index=False)

提交效果:

这里写图片描述

问题经验总结

EDA过程中了解属性,以及数据可视化是很重要的

1.数据探索导入数据为DataFrame格式

df.info()

查看数据属性,非空数据个数,数据类型

这里写图片描述

df.describe()

查看连续型数据均值四分位值,中值,标准差

2.对数据的认识非常重要,利用自己的一些先验知识,可以大致确定一下属性的影响程度

3.绘制每个属性与待预测属性的数量条形图,来分析数据相关程度

这里写图片描述

数据预处理

4.处理missing value

如果missing value占总体的比例非常小,那么直接填入平均值或者众数

如果missing value所占比例不算小也不算大,那么可以考虑它跟其他特征的关系,如果关系明显,那么直接根据其他特征填入;也可以建立简单的模型,比如线性回归,随机森林等。

如果missing value所占比例大,那么直接将miss value当做一种特殊的情况,另取一个值填入

如果missing value占总数比例极高,我们可能就直接舍弃了,作为特征加入的话,可能反倒带入noise,影响最后的结果了

5.处理categorical feature

一般就是通过dummy variable的方式解决

如果特征中包含大量需要做dummy variable处理的,那么很可能导致得到一个稀疏的dataframe,这时候最好用下PCA做降维处理。

如果某个特征有好几万个取值,那么用dummy variable就并不现实了,这时候可以用Count-Based Learning.

在kaggle案例中发现,对于类别特征,在模型中加入tf-idf总是有效果的。

还有个方法叫“Leave-one-out” encoding,也可以处理类别特征种类过多的问题,实测效果不错。

6.特征工程

可以说最后结果的好坏,大部分就是由特征工程决定的,剩下部分应该是调参和Ensemble决定。

特征工程的好坏主要是由domain knowledge决定的

feature selection有很多方法,比如backward,forward selection,random forest的feature importance.

7.选择模型

最常用的模型是Ensemble Model,比如 Random Forest,Gradient Boosting。当然在开始的时候,可以用点简单的模型,一方面是可以作为底线threshold,另一方面也可以在最后作为Ensemble Model。

也可以使用xgboost

8.调参

每种模型都有自己最关键的几个参数

sklearn中GridSearchCV可以设置需要比较的几种参数组合,然后用cross validation来选出最优秀的参数组合。

大致用法如下

from sklearn.grid_search import GridSearchCV
from pprint import pprint
clf=RandomForestClassifier(random_state=seed)
parameters = {
   'n_estimators': [300, 500], 'max_features':[4,5,'auto']}
grid_search = GridSearchCV(estimator=clf,param_grid=parameters, cv=10, scoring='accuracy')
print("parameters:")
pprint(parameters)
grid_search.fit(train_x,train_y)
print("Best score: %0.3f" % grid_search.best_score_)
print("Best parameters set:")
best_parameters=grid_search.best_estimator_.get_params()
for param_name in sorted(parameters.keys()):print("\t%s: %r" % (param_name, best_parameters[param_name]))

9.Model ensemble

Model Ensemble有Bagging,Boosting,Stacking,其中Bagging和Boosting都算是Bootstraping的应用。Bootstraping的概念是对样本每次有放回的抽样,抽样K个,一共抽N次。

Bagging:每次从总体样本中随机抽取K个样本来训练模型,重复N次,得到N个模型,然后将各个模型结果合并,分类问题投票方式结合,回归则是取平均值,e.g.Random Forest。

Boosting:一开始给每个样本取同样的权重,然后迭代训练,每次对训练失败的样本调高其权重。最后对多个模型用加权平均来结合,e.g. GBDT。

Bagging与Boosting的比较:在深入理解Bagging和Boosting后发现,bagging其实是用相同的模型来训练随机抽样的数据,这样的结果是各个模型之间的bias差不多,variance也差不多,通过平均,使得variance降低(由算平均方差的公式可知),从而提高ensemble model的表现。而Boosting其实是一种贪心算法,不断降低bias。

Stacking: 训练一个模型来组合其他各个模型。首先先训练多个不同的模型,然后再以之前训练的各个模型的输出为输入来训练一个模型,以得到一个最终的输出。使用过stacking之后,发现其实stacking很像神经网络,通过很多模型的输出,构建中间层,最后用逻辑回归讲中间层训练得到最后的结果。

10.模型系数关联分析

做完baseline模型后,把得到的model系数和feature关联起来看看。

pd.DataFrame({
   "columns":list(train_df.columns)[1:], "coef":list(clf.coef_.T)})

这里写图片描述

这些系数为正的特征,和最后结果是一个正相关,反之为负相关。

11.交叉验证

对模型进行评分

from sklearn import cross_validationclf = linear_model.LogisticRegression(C=1.0, penalty='l1', tol=1e-6)
all_data = df.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*')
X = all_data.as_matrix()[:,1:]
y = all_data.as_matrix()[:,0]
print cross_validation.cross_val_score(clf, X, y, cv=5)

输出每个fold的评分结果

12.交叉验证之后,分析bad case很关键

可能会得到一些结论,来进行下一步数据特征探索

可能的想法:

Age属性不使用现在的拟合方式,而是根据名称中的『Mr』『Mrs』『Miss』等的平均值进行填充。
Age不做成一个连续值属性,而是使用一个步长进行离散化,变成离散的类目feature。
Cabin再细化一些,对于有记录的Cabin属性,我们将其分为前面的字母部分(我猜是位置和船层之类的信息) 和 后面的数字部分(应该是房间号,有意思的事情是,如果你仔细看看原始数据,你会发现,这个值大的情况下,似乎获救的可能性高一些)。
Pclass和Sex俩太重要了,我们试着用它们去组出一个组合属性来试试,这也是另外一种程度的细化。
单加一个Child字段,Age<=12的,设为1,其余为0(你去看看数据,确实小盆友优先程度很高啊)
如果名字里面有『Mrs』,而Parch>1的,我们猜测她可能是一个母亲,应该获救的概率也会提高,因此可以多加一个Mother字段,此种情况下设为1,其余情况下设为0
登船港口可以考虑先去掉试试(Q和C本来就没权重,S有点诡异)
把堂兄弟/兄妹 和 Parch 还有自己 个数加在一起组一个Family_size字段(考虑到大家族可能对最后的结果有影响)
Name是一个我们一直没有触碰的属性,我们可以做一些简单的处理,比如说男性中带某些字眼的(‘Capt’, ‘Don’, ‘Major’, ‘Sir’)可以统一到一个Title,女性也一样。

13.想法验证

使用手头上的”train_df”和”cv_df”开始试验这些feature engineering的tricks是否有效了。

试验的过程比较漫长,也需要有耐心,而且我们经常会面临很尴尬的状况,就是我们灵光一闪,想到一个feature,然后坚信它一定有效,结果试验下来,效果还不如试验之前的结果。恩,需要坚持和耐心,以及不断的挖掘。

我最好的结果是在『Survived~C(Pclass)+C(Title)+C(Sex)+C(Age_bucket)+C(Cabin_num_bucket)Mother+Fare+Family_Size』下取得的

14.learning curves验证模型是否欠拟合或过拟合