在情人节这个喜大普奔的日子里,终于找到了之前结果一直提不上去的原因……当然这个原因也不是自己想出来的。
在泰坦尼克之灾这个题的处理中,比较关键的两点都包含在名字这个字段中,反应出来的一是社会地位,而是家庭联系。之前做的时候直接把这个字段丢弃了,然后在如何填补年龄那里一直纠结。
总结一下:
1.XGBoost
https://www.missshi.cn/api/view/blog/5a06a441e519f50d0400035e
确实是杀器,相同数据预处理而且还没怎么调参的情况下结果有明显提升,关键在于综合最后几个分类器的结果。
2.字段的挖掘,需要发挥想象力和耐心,同时注意数值型字段的分桶也要按照基本法,不能乱分。简单的方法就是画图看哪些比较接近,当然这么调也不一定对,玄学玄学。。。
3.手动调参时可以试着先固定其他参数改变一个参数,也可以用自动调参工具。
4.手动调参时按照交叉验证调参的结果经常和提交结果不一致,比如在支持向量机中,交叉验证结果最好的是C=1,但C=0.1的kaggle提交分数能比C=1时高0.5%左右,这个很奇怪,以后再注意一下。自动调参倒是没发现这个问题。
附代码,主要参考队伍中排名155的wanngide大佬的算法,地址在这里
https://www.kaggle.com/wanngide/titanic-2
有一些自己加的注释,数据处理部分有一些无伤大雅的修改。
%matplotlib inline
import numpy as np
import pandas as pd
#读取数据
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
train=pd.read_csv(r'D:\学习\研一\数据挖掘\kaggle入门\练习赛\泰坦尼克之灾\train.csv',engine='python')
test=pd.read_csv(r'D:\学习\研一\数据挖掘\kaggle入门\练习赛\泰坦尼克之灾\test.csv',engine='python')
PassengerId=test['PassengerId']
all_data = pd.concat([train, test], ignore_index = True)
#####第一个关键信息:挖掘“姓名”时,将相同性质的title归为一类(3%)
##挖掘姓名的数据信息时,相同的title归为一类。这一步关键!!我之前做的时候,没有归类,直接按照匹配出来的title算特征,正确率直接低了3%
all_data['Title'] = all_data['Name'].apply(lambda x:x.split(',')[1].split('.')[0].strip())
Title_Dict = {}
Title_Dict.update(dict.fromkeys(['Capt', 'Col', 'Major', 'Dr', 'Rev'], 'Officer'))
Title_Dict.update(dict.fromkeys(['Don', 'Sir', 'the Countess', 'Dona', 'Lady'], 'Royalty'))
Title_Dict.update(dict.fromkeys(['Mme', 'Ms', 'Mrs'], 'Mrs'))
Title_Dict.update(dict.fromkeys(['Mlle', 'Miss'], 'Miss'))
Title_Dict.update(dict.fromkeys(['Mr'], 'Mr'))
Title_Dict.update(dict.fromkeys(['Master','Jonkheer'], 'Master'))
all_data['Title'] = all_data['Title'].map(Title_Dict)
#家庭大小
all_data['FamilySize']=all_data['SibSp']+all_data['Parch']+1
#sns.barplot(x="FamilySize", y="Survived", data=all_data)
#通过作图看分布情况,分桶也要按照基本法
def Fam_label(s):
if (s >= 2) & (s <= 4):
return 2
elif ((s > 4) & (s <= 7)) | (s == 1):
return 1
elif (s > 7):
return 0
all_data['FamilyLabel']=all_data['FamilySize'].apply(Fam_label)
#sns.barplot(x="FamilyLabel", y="Survived", data=all_data)
#直接用一个“没有”的标签来填充Cabin
all_data['Cabin'] = all_data['Cabin'].fillna('Unknown')
all_data['Deck']=all_data['Cabin'].str.get(0)
#sns.barplot(x="Deck", y="Survived", data=all_data)
#从属性相关热力图看出年龄和这两个属性关系比较大,做多项式拟合
x = all_data.loc[all_data["Age"].notnull(), "Age"]
y = all_data.loc[all_data["Age"].notnull(), ["Pclass","SibSp"]]
#用两个属性分别做多项式拟合,然后求一下平均
#用两个属性分别做多项式拟合,然后求一下平均
p1 = np.polyfit(y['Pclass'], x, 1)
#然后用拟合的多项式计算
age1 = np.polyval(p1, all_data.loc[all_data['Age'].isnull(), 'Pclass'])
#用两个属性分别做多项式拟合,然后求一下平均
p2 = np.polyfit(y['SibSp'], x, 1)
#然后用拟合的多项式计算
age2 = np.polyval(p2, all_data.loc[all_data['Age'].isnull(), 'SibSp'])
all_data.loc[all_data['Age'].isnull(), 'Age'] = (age1 + age2)/2
all_data.Age = all_data.Age.astype('int64')
#众数填充
all_data['Embarked'] = all_data['Embarked'].fillna(all_data['Embarked'].mode()[0])
all_data['Fare'] = all_data['Fare'].fillna(all_data['Fare'].mode()[0])
#####第二个关键信息:挖掘“姓”这个因素,把家庭分成容易获救的家庭和不容易获救的家庭(2%)
all_data['Surname']=all_data['Name'].apply(lambda x:x.split(',')[0].strip())
Surname_Count = dict(all_data['Surname'].value_counts())
all_data['FamilyGroup'] = all_data['Surname'].apply(lambda x:Surname_Count[x])
#有家庭的妇女儿童,放在一起可能是因为有一样的优先级
Female_Child_Group=all_data.loc[(all_data['FamilyGroup']>=2) & ((all_data['Age']<=12) | (all_data['Sex']=='female'))]
#有家庭的成年男子
Male_Adult_Group=all_data.loc[(all_data['FamilyGroup']>=2) & (all_data['Age']>12) & (all_data['Sex']=='male')]
#从图中看出,不同家庭的成活率不同
Female_Child=pd.DataFrame(Female_Child_Group.groupby('Surname')['Survived'].mean().value_counts())
Female_Child.columns=['GroupCount']
#sns.barplot(x=Female_Child.index, y=Female_Child["GroupCount"]).set_xlabel('AverageSurvived')
Male_Adult=pd.DataFrame(Male_Adult_Group.groupby('Surname')['Survived'].mean().value_counts())
Male_Adult.columns=['GroupCount']
#sns.barplot(x=Male_Adult.index, y=Male_Adult["GroupCount"]).set_xlabel('AverageSurvived')
#死亡名单:一个家族名单,该家族的妇女儿童全部死亡
Female_Child_Group=Female_Child_Group.groupby('Surname')['Survived'].mean()
Dead_List=set(Female_Child_Group[Female_Child_Group.apply(lambda x:x==0)].index)
#print(Dead_List)
#存活名单:一个家族名单,该家族的成年男子全部存活
Male_Adult_List=Male_Adult_Group.groupby('Surname')['Survived'].mean()
Survived_List=set(Male_Adult_List[Male_Adult_List.apply(lambda x:x==1)].index)
#print(Survived_List)
#由于妇女儿童的存活率本就高于成年男子,因此可以推断,死亡名单上的人都要死,存活名单上上的人都能活
train=all_data.loc[all_data['Survived'].notnull()]
test=all_data.loc[all_data['Survived'].isnull()]
#在训练集里,把这些名单对应的人的其他属性全都往活/死了的方向改
test.loc[(test['Surname'].apply(lambda x:x in Dead_List)),'Sex'] = 'male'
test.loc[(test['Surname'].apply(lambda x:x in Dead_List)),'Age'] = 60
test.loc[(test['Surname'].apply(lambda x:x in Dead_List)),'Title'] = 'Mr'
test.loc[(test['Surname'].apply(lambda x:x in Survived_List)),'Sex'] = 'female'
test.loc[(test['Surname'].apply(lambda x:x in Survived_List)),'Age'] = 5
test.loc[(test['Surname'].apply(lambda x:x in Survived_List)),'Title'] = 'Miss'
#筛选数据得到结果
all_data=pd.concat([train, test])
all_data=all_data[['Survived','Pclass','Sex','Age','Fare','Embarked','Title','FamilyLabel','Deck']]
all_data=pd.get_dummies(all_data)
train=all_data[all_data['Survived'].notnull()]
test=all_data[all_data['Survived'].isnull()].drop('Survived',axis=1)
X = train.as_matrix()[:,1:]
y = train.as_matrix()[:,0]
#####第三个关键点:模型选择及训练(1%),选择合适的模型很关键,这里使用的自动寻优和流水线函数也值得注意
#####这里选择的是随机森林,相比选择支持向量机最多可以获得接近1%的提升。用XGBoost进行综合的话可能会更好,鉴于还要调参懒得弄了就没试
#开始训练
#Pipeline:流水线
from sklearn.pipeline import Pipeline
#随机森林分类
from sklearn.ensemble import RandomForestClassifier
#参数自动寻优
from sklearn.model_selection import GridSearchCV
#前者选择排名前k个的特征
from sklearn.feature_selection import SelectKBest
#Pipeline 流水线,接受(name, transform) tuple的列表作为参数。按顺序执行列表中的transform,完成数据预处理
#选择20个特征,没说用什么函数
#分类算法是随机森林
pipe=Pipeline([('select',SelectKBest(k=20)),
('classify', RandomForestClassifier(random_state = 10, max_features = 'sqrt'))])
param_test = {'classify__n_estimators':list(range(20,50,2)),
'classify__max_depth':list(range(3,60,3))}
gsearch = GridSearchCV(estimator = pipe, param_grid = param_test, scoring='roc_auc', cv=10)
gsearch.fit(X,y)
#获得自动选出的最佳参数和最高评分
print(gsearch.best_params_, gsearch.best_score_)
#根据上面选出的最佳参数训练
from sklearn.pipeline import make_pipeline
select = SelectKBest(k = 20)
clf = RandomForestClassifier(random_state = 10, warm_start = True,
n_estimators = 42,
max_depth = 6,
max_features = 'sqrt')
pipeline = make_pipeline(select, clf)
pipeline.fit(X, y)
from sklearn import metrics
from sklearn.model_selection import cross_val_score
cv_score = cross_val_score(pipeline, X, y, cv= 10)
print("CV Score : Mean - %.7g | Std - %.7g " % (np.mean(cv_score), np.std(cv_score)))
#给出结果
predictions = pipeline.predict(test)
submission = pd.DataFrame({"PassengerId": PassengerId, "Survived": predictions.astype(np.int32)})
submission.to_csv(r'D:\学习\研一\数据挖掘\kaggle入门\练习赛\泰坦尼克之灾\result_159.csv', index=False)
submission.info()
提交结果0.82296,排名396/10029