欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Kaggle:Titanic问题

程序员文章站 2024-03-22 09:19:34
...

Kaggle:Titanic问题

相关库函数操作

1.pandas的read_csv函数

读取csv文件为DataFrame格式

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

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

Kaggle:Titanic问题

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()

输出结果:

Kaggle:Titanic问题

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()

输出结果:

Kaggle:Titanic问题

示例:

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()

输出结果:

Kaggle:Titanic问题

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()

大致形状如下

Kaggle:Titanic问题

6.连续型变量计数函数

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

输出结果:

Kaggle:Titanic问题

同样根据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()

输出结果:

Kaggle:Titanic问题

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属性表示叠置

输出结果:

Kaggle:Titanic问题

8.DataFrame.groupby()函数

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

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

Kaggle:Titanic问题

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

Kaggle:Titanic问题

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 

输出结果:

Kaggle:Titanic问题

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)

输出结果:
Kaggle:Titanic问题

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)})

输出结果:

Kaggle:Titanic问题

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,DataFrame

data_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 df

data_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' ] = predictedAges

data_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 vector
    ylim : 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])) / 2
    diff = (train_scores_mean[-1] + train_scores_std[-1]) - (test_scores_mean[-1] - test_scores_std[-1])
    return midpoint, diff

plot_learning_curve(clf, 'learning curves', X, y)

Kaggle:Titanic问题

在实际数据上看,我们得到的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, rfr

def set_Cabin_type(df):
    df.loc[ (df.Cabin.notnull()), 'Cabin' ] = "Yes"
    df.loc[ (df.Cabin.isnull()), 'Cabin' ] = "No"
    return df

data_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_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)

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' ] = predictedAges

data_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 BaggingRegressor

train_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)

提交效果:

Kaggle:Titanic问题

问题经验总结

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

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

df.info()

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

Kaggle:Titanic问题

df.describe()

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

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

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

Kaggle:Titanic问题

数据预处理

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)})

Kaggle:Titanic问题

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

11.交叉验证

对模型进行评分

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)

输出每个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验证模型是否欠拟合或过拟合