Kaggle Titanic: Machine Learning from Disaster 一种思路
2016-03-26 18:32
609 查看
最近在做Kaggle中的Titanic,主要是练习Pandas和Sklearn,用了非常长的时间,下面是代码,成绩是0.80383,虽然不是很高,但基本上能想到的都实验了。下面是代码:
上面的代码仅仅是完整的运行代码,中间省略了分析过程代码。
做这个用了很长时间,收获也比较大:
1.之前一直没什么合适的项目练习Pandas和Sklearn,通过这个算是熟悉一些了;
2.通过每次提交的成绩,了解自己和其他参赛者的差距;
3.通过看其他参赛者的代码和思路,可以极大扩展自己的思路;
4.通过测试不同的算法,选择不同的参数,可以获得对算法更直观的感受;
5.最大的感受是特征工程非常重要,一方面取决于专注数据的程度,另一方面也需要具有一些特定的知识。例如在本例中,我一开始并没有留意到title其实包含了很重要的信息,因此将其忽略了。
<span style="font-family:Microsoft YaHei;"># -*- coding: utf-8 -*- import numpy as np import pandas as pd from sklearn import cross_validation from sklearn.linear_model import Lasso from sklearn.neighbors import KNeighborsClassifier from sklearn.ensemble import RandomForestClassifier train = pd.read_csv(r'D:\train.csv') test = pd.read_csv(r'D:\test.csv') train['from'] = 'train' test['from'] = 'test' comb_data = pd.concat([train, test], axis=0, ignore_index=True) # 首先将train和test合并在一起,所有的处理都在comb_data中进行,需要预测的时候再分开 comb_data['Sex'].replace({'male': 0, 'female': 1}, inplace=True) # 将Sex转换为数值型 # 少量缺失值的处理 comb_data.fillna({'Fare': 13.3}, inplace=True) # 只有一个缺失值,缺失乘客属于3等舱(Pclass==3),因此按照3等舱的均值填充 comb_data.fillna({'Embarked': "S"}, inplace=True) # 有两个缺失值,且船票号(Ticket)相同,将两个缺失的港口信息按照众数'S'处理 # 生成title变量,这个变量参考了其他参赛者分享的思路。由于部分title数量太少且表达含义相似,因此需要合并处理。 # 合并的原则是结合性别、年龄以及在英语世界的具体含义 comb_data['title'] = comb_data['Name'].str.split(',').str.get(1).str.split('.').str.get(0).str.strip() comb_data.loc[comb_data['title'].isin(["Capt", "Don", "Jonkheer", "Major", "Sir"]), 'title'] = 'Mr' comb_data.loc[comb_data['title'].isin(["Dona", "Lady", "the Countess"]), 'title'] = 'Mrs' comb_data.loc[comb_data['title'].isin(["Mlle", "Mme", "Ms"]), 'title'] = 'Miss' # 生成家庭规模变量,这个变量也参考了其他参赛者分享的思路。这里统计家庭规模的时候,参考的变量是last_name,也就是Name的第一项 # 通过肉眼观察,大部分比较准确。当然也有分错的 comb_data['last_name'] = comb_data['Name'].str.split(',').str.get(0) family_name_group = comb_data[comb_data[['Parch', 'SibSp']].sum(axis=1) > 0][['last_name']] family_name_freq = family_name_group['last_name'].value_counts() family_name_freq = pd.DataFrame({'last_name': family_name_freq.index, 'family_size': family_name_freq.values}) comb_data = pd.merge(left=comb_data, right=family_name_freq, how='left', on='last_name', sort=False) comb_data['family_size'] = np.where(comb_data[['Parch', 'SibSp']].sum(axis=1) == 0, 1, comb_data['family_size']) # 生成家庭角色变量,根据title、Sex、Parch和SibSp判断在家庭中的角色,通过肉眼观察,大部分比较准确 comb_data['family_role'] = np.where((comb_data['title'] == 'Master') & (comb_data['Parch'] > 0), 'son',\ np.where((comb_data['title'] == 'Miss') & (comb_data['Parch'] > 0), 'daughter',\ np.where((comb_data['title'] != 'Miss') & (comb_data['Parch'] == 0) & (comb_data['SibSp'] > 0) & (comb_data['Sex'] == 1), 'wife',\ np.where((comb_data['title'] != 'Master') & (comb_data['Parch'] == 0) & (comb_data['SibSp'] > 0) & (comb_data['Sex'] == 0), 'husband',\ np.where((comb_data['title'] != 'Miss') & (comb_data['Parch'] > 0) & (comb_data['SibSp'] == 0) & (comb_data['Sex'] == 1), 'mother',\ np.where((comb_data['title'] != 'Master') & (comb_data['Parch'] > 0) & (comb_data['SibSp'] == 0) & (comb_data['Sex'] == 0), 'father',\ np.where((comb_data['title'] != 'Miss') & (comb_data['Parch'] > 0) & (comb_data['SibSp'] > 0) & (comb_data['Sex'] == 1), 'wife_mother',\ np.where((comb_data['title'] != 'Master') & (comb_data['Parch'] > 0) & (comb_data['SibSp'] > 0) & (comb_data['Sex'] == 0), 'husband_father', \ np.where((comb_data['title'] == 'Mrs') & (comb_data['Parch'] == 0) & (comb_data['SibSp'] == 0), 'wife_only', 'other'))))))))) # 生成婚否变量,与家庭变量类似,判断是否已婚,主要针对女性。因为title为"Mrs"的女士基本可以判断已婚,男性则没找到合理的判断方法 comb_data['marry_flag'] = np.where(comb_data['family_role'].isin(['son', 'daughter']) | \ ((comb_data['family_role'] == 'other') & (comb_data['Sex'] == 1) & (comb_data['title'] == 'Miss')), 'un_marry',\ np.where(comb_data['family_role'].isin(['wife', 'husband', 'mother', 'father', 'wife_mother', 'husband_father', 'wife_only']), 'married', 'unknown')) # 生成昵称变量。如果Name变量中有英文双引号,则打标签1,否则为0。其实我也不是特别清楚,名字中的英文双引号的具体含义 comb_data['nick_name_flag'] = np.where(comb_data['Name'].str.find('"') == -1, 0, 1) # Ticket处理。有的Ticket是纯数字,有的带有英文字母,不了解其中的具体含义,只是将数字和字母的长度分别生成了两个变量 # 因为Ticket分类也很多,因此只提取了Ticket的第一个字母作为一个变量 comb_data['ticket_pos'] = comb_data['Ticket'].str[0] comb_data['ticket_alpha_len'] = comb_data['Ticket'].str.split(' ').str[0].str.len() comb_data['ticket_num_len'] = comb_data['Ticket'].str.split(' ').str[1].str.len() comb_data['ticket_num_len'].fillna(0, inplace=True) # Cabin处理,将Cabin的首字母作为单独的变量。根据其他参赛者分享的代码,貌似Cabin反映了在船中的位置。我一直单纯的理解为船舱的门牌号 comb_data['cabin_no'] = comb_data['Cabin'].str[0] comb_data['cabin_no'] = comb_data['cabin_no'].replace({"T": np.NaN}) # 将唯一的“T”替换成缺失值,通过算法填充 comb_data['cabin_cnt'] = comb_data['Cabin'].str.count(" ") + 1 # 有的Cabin具有连续多个值,猜测是船客同时买了多个船舱,将这个数量记录下来 comb_data['fare_avg'] = np.where(comb_data['cabin_cnt'].notnull(), comb_data['Fare'] / comb_data['cabin_cnt'], comb_data['Fare']) # 为了准确判断单个仓的价格,所以用Fare/cabin_cnt,做一个平均值代替Fare # Age有大量缺失值,所以为了让结果更准确一些,需要使用算法预测Age, 本例中使用Lasso for_age = comb_data[['Age', 'Embarked', 'Fare', 'Parch', 'Pclass', 'Sex', 'SibSp', 'title', \ 'family_size', 'family_role', 'marry_flag', 'nick_name_flag']].copy() for_age = pd.get_dummies(for_age, columns=['Embarked', 'Pclass', 'title', 'family_role', 'marry_flag']) # 将分类变量处理成哑变量 for_age_train = for_age[for_age['Age'].notnull()] X_age = for_age_train.drop(['Age'], axis=1) y_age = for_age_train['Age'] las = Lasso(alpha=0.055) X_train_age, X_test_age, y_train_age, y_test_age = cross_validation.train_test_split(X_age, y_age, test_size=0.2, random_state=56894) las.fit(X_train_age, y_train_age) for_age_predict = for_age[for_age['Age'].isnull()].drop(['Age'], axis=1) for_age_predict_index = for_age_predict.index predict_age = las.predict(for_age_predict) predict_age = pd.DataFrame(predict_age, index=for_age_predict_index, columns=["Age"]) comb_data = comb_data.combine_first(predict_age) # 预测Cabin,使用K-means。这里只使用了两个变量:Pclass和fare_avg。因为凭感觉,船舱位置和仓的等级以及仓的价格比较相关 # 这里的处理方式是,分别针对每个Pclass都做了一次K-means, 这样处理应该会相对准确一些 for_cabin = comb_data[['cabin_no', 'fare_avg', 'Pclass']].copy() unique_cabin_no = np.unique(for_cabin['cabin_no'].dropna(axis=0)) cabin_no_dict = {} cabin_no_back_dict 4000 = {} cnt = 1 for item in unique_cabin_no: cabin_no_dict[item] = cnt cabin_no_back_dict[cnt] = item cnt += 1 for_cabin['cabin_no_trans'] = for_cabin['cabin_no'].replace(cabin_no_dict) # 将字符转换成数字 for i in range(1, 4): temp = for_cabin[for_cabin['Pclass'] == i][['fare_avg', 'cabin_no_trans']] temp_train = temp[temp['cabin_no_trans'].notnull()] temp_predict = temp[temp['cabin_no_trans'].isnull()].drop(['cabin_no_trans'], axis=1) temp_predict_index = temp_predict.index nobs = temp_train.shape[0] knn = KNeighborsClassifier(n_neighbors=1) X_cabin = temp_train['fare_avg'].copy().reshape(-1, 1) y_cabin = temp_train['cabin_no_trans'].copy() knn.fit(X_cabin, y_cabin) temp_predict_cabin = knn.predict(temp_predict) temp_predict_cabin = pd.DataFrame(temp_predict_cabin, index=temp_predict_index, columns=['cabin_no_trans']) if i != 1: predict_cabin = pd.concat([predict_cabin, temp_predict_cabin], axis=0) else: predict_cabin = temp_predict_cabin.copy() pass predict_cabin['cabin_no'] = predict_cabin['cabin_no_trans'].replace(cabin_no_back_dict) # 再将数字转换成字符 predict_cabin.drop(['cabin_no_trans'], axis=1, inplace=True) comb_data = comb_data.combine_first(predict_cabin) # 最后处理一些小问题 comb_data['cabin_cnt'].fillna(1, inplace=True) # 这里的缺失值实际上都是1,可以在前面一并处理 # 由于ticket_pos中'5'和'8'的数量非常少,所以将他们合并进来。合并没有特别的依据,仅是分配进距离最近的ticket_pos中 comb_data['ticket_pos'] = np.where(comb_data['ticket_pos'] == '5', '6', np.where(comb_data['ticket_pos'] == '8', '7', comb_data['ticket_pos'])) comb_data = pd.get_dummies(comb_data, columns=['Embarked', 'Pclass', 'cabin_no', 'ticket_pos', 'title', 'family_role', 'marry_flag']) # 将数据集还原为train和test,下面开始预测 final_train = comb_data[comb_data['from'] == 'train'].drop(['Cabin', 'Fare', 'Name', 'Ticket', 'last_name', 'from'], axis=1) final_predict = comb_data[comb_data['from'] == 'test'].drop(['Cabin', 'Fare', 'Name', 'Ticket', 'Survived', 'last_name', 'from'], axis=1) final_predict_index = final_predict.index # 使用随机森林 X = final_train.drop(['Survived', 'PassengerId'], axis=1) y = final_train['Survived'] X_train, X_test, y_train, y_test = cross_validation.train_test_split(X, y, test_size=0.2, random_state=54684) rf = RandomForestClassifier(n_estimators=100, max_features=8, min_samples_leaf=5, random_state=54684) rf.fit(X_train, y_train) rf_result = rf.predict(final_predict.drop(['PassengerId'], axis=1)).astype(int) rf_result = pd.DataFrame(rf_result, index=final_predict_index, columns=['Survived']) # 结果输出 rf_output_result = pd.concat([final_predict['PassengerId'], rf_result['Survived']], axis=1) rf_output_result.to_csv(r'D:\result.csv', index=False) </span>
上面的代码仅仅是完整的运行代码,中间省略了分析过程代码。
做这个用了很长时间,收获也比较大:
1.之前一直没什么合适的项目练习Pandas和Sklearn,通过这个算是熟悉一些了;
2.通过每次提交的成绩,了解自己和其他参赛者的差距;
3.通过看其他参赛者的代码和思路,可以极大扩展自己的思路;
4.通过测试不同的算法,选择不同的参数,可以获得对算法更直观的感受;
5.最大的感受是特征工程非常重要,一方面取决于专注数据的程度,另一方面也需要具有一些特定的知识。例如在本例中,我一开始并没有留意到title其实包含了很重要的信息,因此将其忽略了。
相关文章推荐
- Python动态类型的学习---引用的理解
- Python3写爬虫(四)多线程实现数据爬取
- 垃圾邮件过滤器 python简单实现
- 下载并遍历 names.txt 文件,输出长度最长的回文人名。
- install and upgrade scrapy
- Scrapy的架构介绍
- Centos6 编译安装Python
- 使用Python生成Excel格式的图片
- 让Python文件也可以当bat文件运行
- [Python]推算数独
- Python中zip()函数用法举例
- Python中map()函数浅析
- Python将excel导入到mysql中
- Python在CAM软件Genesis2000中的应用
- 使用Shiboken为C++和Qt库创建Python绑定
- FREEBASIC 编译可被python调用的dll函数示例
- Python 七步捉虫法