Kaggle Titanic: Machine Learning from Disaster 一种思路

来源:互联网 发布:晋城网络电视台直播 编辑:程序博客网 时间:2024/05/16 06:31

最近在做Kaggle中的Titanic,主要是练习Pandas和Sklearn,用了非常长的时间,下面是代码,成绩是0.80383,虽然不是很高,但基本上能想到的都实验了。下面是代码:


<span style="font-family:Microsoft YaHei;"># -*- coding: utf-8 -*-import numpy as npimport pandas as pdfrom sklearn import cross_validationfrom sklearn.linear_model import Lassofrom sklearn.neighbors import KNeighborsClassifierfrom sklearn.ensemble import RandomForestClassifiertrain = 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, 本例中使用Lassofor_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.indexpredict_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 = {}cnt = 1for item in unique_cabin_no:    cabin_no_dict[item] = cnt    cabin_no_back_dict[cnt] = item    cnt += 1for_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()    passpredict_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其实包含了很重要的信息,因此将其忽略了。

0 0
原创粉丝点击