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,女性也一样。