天池龙珠计划——机器学习训练营 Task4

目录

前言

龙珠机器学习训练营的第四个阶段了,这一部分主要是赛事实战,我这里也主要采用了前面所学到的一些模型来解决这次比赛。

快来一起挖掘幸福感

赛前预览

首先我们看一下比赛的大致要求和数据提供
天池龙珠计划——机器学习训练营 Task4
天池龙珠计划——机器学习训练营 Task4
这次比赛大概是通过问卷中的问题来获取一些人的个人情况等,从中提取重要因素用来判断幸福感。

数据可视化

import pandas as pd
import matplotlib.pyplot as plt
#显示所有列
pd.set_option('display.max_columns', None)
#显示所有行
pd.set_option('display.max_rows', None)

complete文件为变量完整版数据,abbr文件为变量精简版数据。

index文件中包含每个变量对应的问卷题目,以及变量取值的含义。

survey文件是数据源的原版问卷,作为补充以方便理解问题背景。

train = pd.read_csv('happiness_train_complete.csv', encoding = 'ansi')
test = pd.read_csv('happiness_test_complete.csv', encoding = 'ansi')
train_abbr = pd.read_csv('happiness_train_abbr.csv', encoding = 'ansi')
test_sub=pd.read_csv('happiness_submit.csv', encoding = 'ansi')

我们这里读取详细的train和test以及简略版的train和提交成绩的文件

train_abbr.shape, train.shape

天池龙珠计划——机器学习训练营 Task4
精简版提供的feature有42个,而详细的有140个,当然了,精简版只是为了更好地理解问卷中主要涉及的问题方面,这里我就直接从详细版下手,一步步分析

train.head()

天池龙珠计划——机器学习训练营 Task4
数据有很多,一眼看去就有不重要的id(可删),非数字类型的time等一些待处理的数据

test.head()

天池龙珠计划——机器学习训练营 Task4
测试数据就是缺少了happiness的label,这也是我们需要预测的目标

import seaborn as sns
sns.countplot(x = 'happiness', data = train)

我们这里将happiness的分布情况画出来
天池龙珠计划——机器学习训练营 Task4
幸福指数高的还是比较多啊,1、2、3、4、5是幸福等级,-8则是填了无法回答,那我们默认其为3类,尽可能不影响大局

label = train['happiness']
label = label.map(lambda x:3 if x==-8 else x)
train.drop(['happiness'], axis = 1, inplace = True)
data = pd.concat((train, test), axis = 0)

我们这里将-8替换为3,并且将label单独提取出来,方便之后输入模型作为y,同时将train和test整合到一起,方便数据的共同处理

sns.countplot(x = 'nationality', data = data)

天池龙珠计划——机器学习训练营 Task4
观察民族的分布,极为不均匀,这应该也是正常现象,毕竟少数民族占比不大,我们就不再处理

sns.countplot(x = 'religion', data = data)

天池龙珠计划——机器学习训练营 Task4
宗教分布,不信仰宗教的占大多数

sns.countplot(x = 'political', data = data)

天池龙珠计划——机器学习训练营 Task4
政治面貌群众最多,其次就是*员

sns.countplot(x='property_1', data = data)

天池龙珠计划——机器学习训练营 Task4
房子是否属于自己,5000左右的人的房子还是属于自己的,剩下一半多房子属于父母、配偶等其他人

sns.countplot(x = 'invest_0', data = data)

天池龙珠计划——机器学习训练营 Task4
家庭投资情况,绝大部分的人都没有投资行为,所以这里决定没有参考价值

数据处理

这一部分针对缺失数据,或者是-8类型的数据进行处理

isnull = data.isnull().sum().sort_values(ascending = False)#统计data中每个feature缺失值的总数,并以此排序
percent = (data.isnull().sum()/data.isnull().count()).sort_values(ascending=False)#计算缺失数目占总数的百分比
mis_data = pd.concat([isnull, percent], axis = 1, keys = ['isnull', 'percent'])#将两项结合组成dataframe
mis_data[mis_data['percent']>0]#只有缺失占比大于零才是缺失

统计缺省值
天池龙珠计划——机器学习训练营 Task4
可以看到有很多feature都存在缺失,其中不乏有几乎全部缺失的,我们应当舍弃掉那些缺失严重已经不具备太大意义的值

data.drop(['edu_other','invest_other','property_other','join_party',
              's_work_type','s_work_status','work_status',
              'work_yr','work_manage','work_type'], axis = 1, inplace = True)

同时我们应当发现,上面缺失的数据存在着分界,从work_type开始,往上都是过半缺失,往下都是四分之一或以下,所以我们选择直接剔除掉大于0.5的feature

data["edu_yr"] = data["edu_yr"].fillna(-2)#最高学历的年份,查看数据,只有具体,-2和nan,所以nan填充为-2
data["edu_status"] = data["edu_status"].fillna(0)#填充0表示未受过教育
data["marital_1st"] = data["marital_1st"].fillna(-2)#第一次结婚时间
data["marital_now"] = data["marital_now"].fillna(-2)#目前结婚时间填充-2
data['s_political'] = data['s_political'].fillna(0)
data['s_hukou'] = data['s_hukou'].fillna(0)
data['s_income'] = data['s_income'].fillna(-2)
data['s_birth'] = data['s_birth'].fillna(0)
data['s_edu'] = data['s_edu'].fillna(0)     
data['s_work_exper'] = data['s_work_exper'].fillna(0)#上述因结婚原因造成的数据缺失,要么使用-2,要么使用0
data['social_friend'] = data['social_friend'].fillna(7)
data['social_neighbor'] = data['social_neighbor'].fillna(7)#社交不积极填充7,越多表示越不频繁
data['minor_child'] = data['minor_child'].fillna(0)#子女个数,缺失认为没有
data["family_income"] = data["family_income"].fillna(50000.0)#家庭收入填充众数
data["hukou_loc"] = data["hukou_loc"].fillna(1)#户口所在地默认为本乡

对存在少量缺省值的feature进行填充

#查看非数字类型的feature
for col in data.columns:
    if data[col][1].dtype == 'object':
        print(col)

天池龙珠计划——机器学习训练营 Task4
时间应分割转化成年月日等类型,可表示为数字

#将问卷当前时间分成各个属性
from datetime import datetime
data['survey_time'] = pd.to_datetime(data['survey_time'], format = '%Y-%m-%d %H:%M:%S')
data["weekday"] = data["survey_time"].dt.weekday
data["year"] = data["survey_time"].dt.year
data["quarter"] = data["survey_time"].dt.quarter
data["hour"] = data["survey_time"].dt.hour
data["month"] = data["survey_time"].dt.month
#把一天的时间分段
def hour_cut(x):
    if 0<=x<6:
        return 0
    elif  6<=x<8:
        return 1
    elif  8<=x<12:
        return 2
    elif  12<=x<14:
        return 3
    elif  14<=x<18:
        return 4
    elif  18<=x<21:
        return 5
    elif  21<=x<24:
        return 6

    
data["hour_cut"] = data["hour"].map(hour_cut)
#出生的年代
def birth_split(x):
    if 1920<=x<=1930:
        return 0
    elif  1930<x<=1940:
        return 1
    elif  1940<x<=1950:
        return 2
    elif  1950<x<=1960:
        return 3
    elif  1960<x<=1970:
        return 4
    elif  1970<x<=1980:
        return 5
    elif  1980<x<=1990:
        return 6
    elif  1990<x<=2000:
        return 7
    
data["birth_s"]=data["birth"].map(birth_split)
#收入分组
def income_cut(x):
    if x<0:
        return 0
    elif  0<=x<1200:
        return 1
    elif  1200<x<=10000:
        return 2
    elif  10000<x<24000:
        return 3
    elif  24000<x<40000:
        return 4
    elif  40000<=x:
        return 5

    
data["income_cut"] = data["income"].map(income_cut)
#做问卷时候的年龄
data["survey_age"] = data["year"] - data["birth"]
#去除无关因素
data = data.drop(["id", "hour", "birth", "income", 'survey_time'], axis = 1)

这一部分还可以进行上面提到的-8替换,可以选择对缺省值大的数据但是参考价值大的进行保留和填充,另外还可以对一些范围较大的feature进行缩略分段,如小学初中及以下归类到低等教育,这里举出一个例子

data['edu'] = data['edu'].map(lambda x:0 if x==-8 else x)

def edu_split(x):
    if x in [1,2,14]:
        return 0
    elif x in [3]:
        return 1
    elif x in [4]:
        return 2
    elif x in [5,7,8]:
        return 3
    elif x in [6]:
        return 4
    elif x in [9,10]:
        return 5
    elif x in [11,12]:
        return 6
    elif x in[13]:
        return 7
data["edu"] = data["edu"].map(edu_split)

模型+训练

这一部分参考 天池上某位大佬的代码

X_train = data[:train.shape[0]]
X_test  = data[train.shape[0]:]

首先划分训练集和测试集

#让label从0开始
label = label.map(lambda x:x-1)
import numpy as np
X_train = np.array(X_train)
y_train = np.array(label)
X_test  = np.array(X_test)

数据类型转化

#自定义评价函数
def myFeval(preds, xgbtrain):
    label = xgbtrain.get_label()
    score = mean_squared_error(label,preds)
    return 'myFeval',score
xgb_params = {"booster":'gbtree','eta': 0.005, 'max_depth': 5, 'subsample': 0.7, 
              'colsample_bytree': 0.8, 'objective': 'reg:linear', 'eval_metric': 'rmse', 'silent': True, 'nthread': 8}

参数设置,这里提供xgb模型简略介绍

folds = KFold(n_splits = 5, shuffle = True, random_state = 2021)

使用KFold做五次交叉验证
天池龙珠计划——机器学习训练营 Task4
天池龙珠计划——机器学习训练营 Task4

最终结果展示,评价指数使用rmse和mse,提交到天池上成绩为0.48502,虽然并不能排上名次,但是能够对这一阶段所学到的知识进行实际使用,还是感觉收获颇丰

上一篇:设计模式七大原则——接口隔离原则


下一篇:论文翻译-A Comprehensive Survey on Graph Neural Networks《图神经网络GNN综述》