discuz商业插件 SEO天涯海角 3.2.0高级版 DZ论坛插件 价值299元
什么收录、蜘蛛、收否一个插件搞定。
你还在为网站没有收录、流量而发愁吗?每日会员从十大搜索引擎的结果中访问你的网站,网站收录、权重、快照都不是梦!,还能方便会员查找相关信息,会员站长互利共赢。
请不要相信夸大其词的各类SEO插件,做好原创内容的更新比你把应用中心所有SEO插件都安装了还有用,内容的更新才是实打实的,插件起到辅助作用,没有原创内容也不建议只用关键词替换这种伪原创方式,因为各大搜索引擎都是可以很容易识别的,时间久了有很大的被K可能,你可以自己编辑整理下再发布,不要让百度觉得你的网站是垃圾采集站,所有帖子都只是关键字替换了,别当百度是傻子。
1.在帖子页面天涯海角搜索引擎便捷搜索功能
2.提供三种搜索方式:帖子标题、标签关键字、自定义关键字
3.提供三处位置显示,对应三种搜索方式,各个位置的搜索方式均可独立设置
4.支持十种搜索引擎,包括最近刚出来的360综合搜索,10%的搜索份额你不要?
5.背景颜色随模板自适应,不同模板不同效果,后台也可以设置调节
文件下载:n459.com/file/25127180-476821493
以下内容无关:
-------------------------------------------分割线-------------------------------------------
基于XGBoost模型的幸福度预测——阿里天池学习赛
加载数据#
加载的是完整版的数据 happiness_train_complete.csv 。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns
sns.set_style(‘whitegrid’)
data_origin = pd.read_csv(’./data/happiness_train_complete.csv’, index_col=‘id’, parse_dates=[‘survey_time’], encoding=‘gbk’)
数据集基本信息的探索#
下面简单输出前5行查看。
data_origin.head()
happiness survey_type province city county survey_time gender birth nationality religion … neighbor_familiarity public_service_1 public_service_2 public_service_3 public_service_4 public_service_5 public_service_6 public_service_7 public_service_8 public_service_9
id
1 4 1 12 32 59 2015-08-04 14:18:00 1 1959 1 1 … 4 50 60 50 50 30.0 30 50 50 50
2 4 2 18 52 85 2015-07-21 15:04:00 1 1992 1 1 … 3 90 70 70 80 85.0 70 90 60 60
3 4 2 29 83 126 2015-07-21 13:24:00 2 1967 1 0 … 4 90 80 75 79 80.0 90 90 90 75
4 5 2 10 28 51 2015-07-25 17:33:00 2 1943 1 1 … 3 100 90 70 80 80.0 90 90 80 80
5 4 1 7 18 36 2015-08-10 09:50:00 2 1994 1 1 … 2 50 50 50 50 50.0 50 50 50 50
5 rows × 139 columns
查看数据的详细信息,共8000条记录,139个特征。
第二列为特证名、第三列为非空记录个数、第四列为特征的数据格式。
data_origin.info(verbose=True, null_counts=True)
Int64Index: 8000 entries, 1 to 8000
Data columns (total 139 columns):
0 happiness 8000 non-null int64
1 survey_type 8000 non-null int64
2 province 8000 non-null int64
3 city 8000 non-null int64
4 county 8000 non-null int64
5 survey_time 8000 non-null datetime64[ns]
6 gender 8000 non-null int64
7 birth 8000 non-null int64
8 nationality 8000 non-null int64
9 religion 8000 non-null int64
10 religion_freq 8000 non-null int64
11 edu 8000 non-null int64
12 edu_other 3 non-null object
13 edu_status 6880 non-null float64
14 edu_yr 6028 non-null float64
15 income 8000 non-null int64
16 political 8000 non-null int64
17 join_party 824 non-null float64
18 floor_area 8000 non-null float64
19 property_0 8000 non-null int64
20 property_1 8000 non-null int64
21 property_2 8000 non-null int64
22 property_3 8000 non-null int64
23 property_4 8000 non-null int64
24 property_5 8000 non-null int64
25 property_6 8000 non-null int64
26 property_7 8000 non-null int64
27 property_8 8000 non-null int64
28 property_other 66 non-null object
29 height_cm 8000 non-null int64
30 weight_jin 8000 non-null int64
31 health 8000 non-null int64
32 health_problem 8000 non-null int64
33 depression 8000 non-null int64
34 hukou 8000 non-null int64
35 hukou_loc 7996 non-null float64
36 media_1 8000 non-null int64
37 media_2 8000 non-null int64
38 media_3 8000 non-null int64
39 media_4 8000 non-null int64
40 media_5 8000 non-null int64
41 media_6 8000 non-null int64
42 leisure_1 8000 non-null int64
43 leisure_2 8000 non-null int64
44 leisure_3 8000 non-null int64
45 leisure_4 8000 non-null int64
46 leisure_5 8000 non-null int64
47 leisure_6 8000 non-null int64
48 leisure_7 8000 non-null int64
49 leisure_8 8000 non-null int64
50 leisure_9 8000 non-null int64
51 leisure_10 8000 non-null int64
52 leisure_11 8000 non-null int64
53 leisure_12 8000 non-null int64
54 socialize 8000 non-null int64
55 relax 8000 non-null int64
56 learn 8000 non-null int64
57 social_neighbor 7204 non-null float64
58 social_friend 7204 non-null float64
59 socia_outing 8000 non-null int64
60 equity 8000 non-null int64
61 class 8000 non-null int64
62 class_10_before 8000 non-null int64
63 class_10_after 8000 non-null int64
64 class_14 8000 non-null int64
65 work_exper 8000 non-null int64
66 work_status 2951 non-null float64
67 work_yr 2951 non-null float64
68 work_type 2951 non-null float64
69 work_manage 2951 non-null float64
70 insur_1 8000 non-null int64
71 insur_2 8000 non-null int64
72 insur_3 8000 non-null int64
73 insur_4 8000 non-null int64
74 family_income 7999 non-null float64
75 family_m 8000 non-null int64
76 family_status 8000 non-null int64
77 house 8000 non-null int64
78 car 8000 non-null int64
79 invest_0 8000 non-null int64
80 invest_1 8000 non-null int64
81 invest_2 8000 non-null int64
82 invest_3 8000 non-null int64
83 invest_4 8000 non-null int64
84 invest_5 8000 non-null int64
85 invest_6 8000 non-null int64
86 invest_7 8000 non-null int64
87 invest_8 8000 non-null int64
88 invest_other 29 non-null object
89 son 8000 non-null int64
90 daughter 8000 non-null int64
91 minor_child 6934 non-null float64
92 marital 8000 non-null int64
93 marital_1st 7172 non-null float64
94 s_birth 6282 non-null float64
95 marital_now 6230 non-null float64
96 s_edu 6282 non-null float64
97 s_political 6282 non-null float64
98 s_hukou 6282 non-null float64
99 s_income 6282 non-null float64
100 s_work_exper 6282 non-null float64
101 s_work_status 2565 non-null float64
102 s_work_type 2565 non-null float64
103 f_birth 8000 non-null int64
104 f_edu 8000 non-null int64
105 f_political 8000 non-null int64
106 f_work_14 8000 non-null int64
107 m_birth 8000 non-null int64
108 m_edu 8000 non-null int64
109 m_political 8000 non-null int64
110 m_work_14 8000 non-null int64
111 status_peer 8000 non-null int64
112 status_3_before 8000 non-null int64
113 view 8000 non-null int64
114 inc_ability 8000 non-null int64
115 inc_exp 8000 non-null float64
116 trust_1 8000 non-null int64
117 trust_2 8000 non-null int64
118 trust_3 8000 non-null int64
119 trust_4 8000 non-null int64
120 trust_5 8000 non-null int64
121 trust_6 8000 non-null int64
122 trust_7 8000 non-null int64
123 trust_8 8000 non-null int64
124 trust_9 8000 non-null int64
125 trust_10 8000 non-null int64
126 trust_11 8000 non-null int64
127 trust_12 8000 non-null int64
128 trust_13 8000 non-null int64
129 neighbor_familiarity 8000 non-null int64
130 public_service_1 8000 non-null int64
131 public_service_2 8000 non-null int64
132 public_service_3 8000 non-null int64
133 public_service_4 8000 non-null int64
134 public_service_5 8000 non-null float64
135 public_service_6 8000 non-null int64
136 public_service_7 8000 non-null int64
137 public_service_8 8000 non-null int64
138 public_service_9 8000 non-null int64
dtypes: datetime64ns, float64(25), int64(110), object(3)
memory usage: 8.5+ MB
查看数据总体统计量。
data_origin.describe()
happiness survey_type province city county gender birth nationality religion religion_freq … neighbor_familiarity public_service_1 public_service_2 public_service_3 public_service_4 public_service_5 public_service_6 public_service_7 public_service_8 public_service_9
count 8000.000000 8000.000000 8000.000000 8000.000000 8000.000000 8000.00000 8000.000000 8000.00000 8000.000000 8000.000000 … 8000.000000 8000.000000 8000.000000 8000.000000 8000.000000 8000.000000 8000.000000 8000.00000 8000.000000 8000.000000
mean 3.850125 1.405500 15.155375 42.564750 70.619000 1.53000 1964.707625 1.37350 0.772250 1.427250 … 3.722250 70.809500 68.170000 62.737625 66.320125 62.794187 67.064000 66.09625 65.626750 67.153750
std 0.938228 0.491019 8.917100 27.187404 38.747503 0.49913 16.842865 1.52882 1.071459 1.408441 … 1.143358 21.184742 20.549943 24.771319 22.049437 23.463162 21.586817 23.08568 23.827493 22.502203
min -8.000000 1.000000 1.000000 1.000000 1.000000 1.00000 1921.000000 -8.00000 -8.000000 -8.000000 … -8.000000 -3.000000 -3.000000 -3.000000 -3.000000 -3.000000 -3.000000 -3.00000 -3.000000 -3.000000
25% 4.000000 1.000000 7.000000 18.000000 37.000000 1.00000 1952.000000 1.00000 1.000000 1.000000 … 3.000000 60.000000 60.000000 50.000000 60.000000 55.000000 60.000000 60.00000 60.000000 60.000000
50% 4.000000 1.000000 15.000000 42.000000 73.000000 2.00000 1965.000000 1.00000 1.000000 1.000000 … 4.000000 79.000000 70.000000 70.000000 70.000000 70.000000 70.000000 70.00000 70.000000 70.000000
75% 4.000000 2.000000 22.000000 65.000000 104.000000 2.00000 1977.000000 1.00000 1.000000 1.000000 … 5.000000 80.000000 80.000000 80.000000 80.000000 80.000000 80.000000 80.00000 80.000000 80.000000
max 5.000000 2.000000 31.000000 89.000000 134.000000 2.00000 1997.000000 8.00000 1.000000 9.000000 … 5.000000 100.000000 100.000000 100.000000 100.000000 100.000000 100.000000 100.00000 100.000000 100.000000
8 rows × 135 columns
数据预处理#
缺失值处理#
查看子特征的缺失情况,其中
required_list 表示特征中的必填项
continuous_list 表示特征属性为连续型变量
categorical_list 表示分类型变量
其余特征均为等级(ordinal)型的分类变量。
required_list = [‘survey_type’, ‘province’, ‘city’, ‘county’, ‘survey_time’, ‘gender’, ‘birth’, ‘nationality’, ‘religion’,
‘religion_freq’, ‘edu’, ‘income’, ‘political’, ‘floor_area’, ‘height_cm’, ‘weight_jin’, ‘health’, ‘health_problem’,
‘depression’, ‘hukou’, ‘socialize’, ‘relax’, ‘learn’, ‘equity’, ‘class’, ‘work_exper’, ‘work_status’, ‘work_yr’, ‘work_type’,
‘work_manage’, ‘family_income’, ‘family_m’, ‘family_status’, ‘house’, ‘car’, ‘marital’, ‘status_peer’, ‘status_3_before’,
‘view’, ‘inc_ability’]
continuous_list = [‘birth’, ‘edu_yr’, ‘income’, ‘floor_area’, ‘height_cm’, ‘weight_jin’, ‘work_yr’, ‘family_income’, ‘family_m’, ‘house’, ‘son’,
‘daughter’, ‘minor_child’, ‘marital_1st’, ‘s_birth’, ‘marital_now’, ‘s_income’, ‘f_birth’, ‘m_birth’, ‘inc_exp’,
‘public_service_1’, ‘public_service_2’, ‘public_service_3’, ‘public_service_4’, ‘public_service_5’, ‘public_service_6’,
‘public_service_7’, ‘public_service_8’, ‘public_service_9’]
categorical_list = [‘survey_type’, ‘province’, ‘gender’, ‘nationality’]
必填项的缺失值分析#
查看必填项中缺失值的情况。
data_origin[required_list].isna().sum()[data_origin[required_list].isna().sum() > 0].to_frame().T
work_status work_yr work_type work_manage family_income
0 5049 5049 5049 5049 1
其中
work_status 表示 目前工作的状况
work_yr 表示 一共工作了多少年
work_type 表示 目前工作的性质
work_manage 表示 目前工作的管理活动情况
family_income 表示 去年全年家庭总收入
首先分析 work_ 开头的四项特征的缺失情况,它们的缺失计数一样,可能说明调查问卷的填写方式,可能被跳过了。
首先检查调查问卷,找到对应的问卷问题,发现在 work_exper 特征中,即 工作经历及状况,根据不同的工作经历,将上面四个问题跳过。
查看 work_exper 对应的问卷。
图片
可以发现 work_exper 除了 1 分类,其它问题均被跳问;所以将上面四列的缺失记录的 work_exper 输出,查看是否都为非 1 类的记录。
通过下面的输出可以看到,在上面四项特征为缺失值的情况下,其记录对应的 work_exper 的取值大部分不为 1 。
data_origin.loc[data_origin[required_list].isna().sum(axis=1)[data_origin[required_list].isna().sum(axis=1) > 0].index, ‘work_exper’].to_frame().plot.hist()
pd.value_counts(data_origin.loc[data_origin[required_list].isna().sum(axis=1)[data_origin[required_list].isna().sum(axis=1) > 0].index, ‘work_exper’])
5 1968
3 1242
4 1065
2 387
6 380
1 7
Name: work_exper, dtype: int64
output_15_1
进一步查看取值为 1 的记录。
(data_origin[data_origin[required_list].isna().sum(axis=1) > 0])[(data_origin[data_origin[required_list].isna().sum(axis=1) > 0].work_exper == 1)]
happiness survey_type province city county survey_time gender birth nationality religion … neighbor_familiarity public_service_1 public_service_2 public_service_3 public_service_4 public_service_5 public_service_6 public_service_7 public_service_8 public_service_9
id
692 4 2 21 64 101 2015-07-20 11:12:00 2 1975 1 1 … 5 80 70 80 80 80.0 80 80 80 80
841 4 2 31 88 133 2015-08-17 13:49:00 2 1971 1 0 … 4 50 30 -2 -2 -2.0 50 50 50 70
1411 4 2 2 2 9 2015-07-23 09:25:00 1 1967 8 1 … 4 90 85 80 90 90.0 92 93 94 90
3117 4 1 4 7 18 2015-10-03 16:02:00 1 1980 1 1 … 2 30 35 30 40 60.0 40 30 70 70
4783 5 2 22 65 103 2015-07-08 18:45:00 1 1955 1 1 … 5 90 90 90 90 80.0 90 80 90 90
5589 5 2 16 46 78 2015-07-29 11:34:00 2 1964 1 1 … 3 89 63 67 75 74.0 67 65 78 79
7368 4 2 21 64 101 2015-07-19 08:32:00 2 1963 1 1 … 5 70 70 70 60 70.0 70 60 60 60
7 rows × 139 columns
可以发现 work_exper 为 1 的记录存在7条,故将此删除。
data_origin.drop((data_origin[data_origin[required_list].isna().sum(axis=1) > 0])[(data_origin[data_origin[required_list].isna().sum(axis=1) > 0].work_exper == 1)].index, inplace=True)
因为 family_income 缺失个数只有1条,不影响数据规模,所以直接将其删除。
data_origin.drop(data_origin[‘family_income’].isna()[data_origin[‘family_income’].isna()].index, inplace=True)
连续型特征缺失值分析#
查看连续型特征的却失情况。
data_origin[continuous_list].isna().sum()[data_origin[continuous_list].isna().sum() > 0].to_frame().T
edu_yr work_yr minor_child marital_1st s_birth marital_now s_income
0 1970 5041 1066 828 1718 1770 1718
其中
edu_yr 表示 已经完成的最高学历是哪一年获得的
work_yr 表示 第一份非农工作到目前的工作一共工作了多少年
minor_child 表示 有几个18周岁以下未成年子女
marital_1st 表示 第一次结婚的时间
s_birth 表示 目前的配偶或同居伴侣是哪一年出生的
martital_now 表示 与目前的配偶是哪一年结婚的
s_income 表示 配偶或同居伴侣去年全年的总收入
对于 edu_yr 即 已经完成的最高学历是哪一年获得的,查看缺失记录的 edu_status 取值分布情况。
data_origin[data_origin[‘edu_yr’].isna()][‘edu_status’].plot.hist()
pd.value_counts(data_origin[data_origin[‘edu_yr’].isna()][‘edu_status’])
2.0 746
3.0 103
4.0 1
1.0 1
Name: edu_status, dtype: int64
output_26_1
查看 edu_yr 缺失的记录的 edu_status 特征后,只有选项 4 即 毕业的记录才应该填写 edu_yr 的毕业年份,所以应该删除记录。
data_origin.drop(data_origin[(data_origin[‘edu_status’] == 4) & (data_origin[‘edu_yr’].isna())].index, inplace=True)
data_origin.shape
(7991, 139)
对于 minor_child 特征,可以检查这个特征缺失的记录另外两项特征 son 和 daughter 分别表示儿子、女儿的数量,如果为0,则将 minor_child 也填充为0。
print(data_origin[np.array(data_origin[‘minor_child’].isna())].loc[:, ‘son’].sum())
print(data_origin[np.array(data_origin[‘minor_child’].isna())].loc[:, ‘daughter’].sum())
data_origin[np.array(data_origin[‘minor_child’].isna())].loc[:, ‘son’:‘daughter’]
0
0
son daughter
id
2 0 0
5 0 0
9 0 0
29 0 0
31 0 0
… … …
7967 0 0
7972 0 0
7991 0 0
7999 0 0
8000 0 0
1066 rows × 2 columns
可以看对 minor_child 缺失的记录,其儿子和女儿的个数也为0,所以将 minor_child 缺失值填充为0。
data_origin[‘minor_child’].fillna(0, inplace=True)
对于 marital_1st 的记录的缺失情况,可以查看对应的记录的 marital 的取值是否为 1 表示 未婚。
print(data_origin[np.array(data_origin[‘marital_1st’].isna())][‘marital’].sum() == data_origin[np.array(data_origin[‘marital_1st’].isna())][‘marital’].shape[0])
data_origin[np.array(data_origin[‘marital_1st’].isna())][‘marital’].plot.hist()
pd.value_counts(data_origin[np.array(data_origin[‘marital_1st’].isna())][‘marital’])
True
1 828
Name: marital, dtype: int64
output_35_2
可以看到输出结果表明对于 marital_1st 缺失的记录都是未婚人士,所以缺失值正常。
下面查看 s_birth 即 目前的配偶或同居伴侣是哪一年出生的的缺失情况,首先查看缺失的记录的 marital 状态,查看是否满足无配偶或同居伴侣的情况。
data_origin[data_origin[‘s_birth’].isna()][‘marital’].plot.hist()
pd.value_counts(data_origin[data_origin[‘s_birth’].isna()][‘marital’])
1 828
7 718
6 171
2 1
Name: marital, dtype: int64
output_37_1
根据输出可以看到,marital 取值为 1 、6、7 分别表示 未婚、离婚和丧偶,所以 s_birth 缺失属于正常;而且取值为 2 表示同居的缺失记录只有一条,所以直接将其删除即可。
data_origin.drop(data_origin[data_origin[‘s_birth’].isna()][‘marital’][data_origin[data_origin[‘s_birth’].isna()][‘marital’] == 2].index, inplace=True)
对于 marital_now 即 与目前的配偶是哪一年结婚的,首先输出 marital 查看婚姻的状态,是否满足没结婚的条件。
data_origin[data_origin[‘marital_now’].isna()][‘marital’].plot.hist()
pd.value_counts(data_origin[data_origin[‘marital_now’].isna()][‘marital’])
1 828
7 718
6 171
2 51
3 1
Name: marital, dtype: int64
output_41_1
根据输出可以得到 1 和 2 表示没有结婚的情况,所以缺失属于正常;
对于 3、6、7 分别表示 初婚有配偶、离婚、丧偶;只有 3 属于目前有配偶并结婚的情况,所以应该删除。
data_origin.drop(data_origin[data_origin[‘marital_now’].isna()].loc[data_origin[data_origin[‘marital_now’].isna()][‘marital’] == 3].index, inplace=True)
data_origin.shape
(7989, 139)
对于 s_income 即 配偶或同居伴侣去年全年的总收入的缺失情况,可以检查对于 marital 查看其是否满足无配偶或伴侣的条件。
data_origin[data_origin[‘s_income’].isna()][‘marital’].plot.hist()
pd.value_counts(data_origin[data_origin[‘s_income’].isna()][‘marital’])
1 828
7 718
6 171
Name: marital, dtype: int64
output_46_1
可以看到对于 s_income 的缺失值,其记录对应的婚姻状态都为未婚、离婚或丧偶,所以 s_income 缺失是正常的。
分类变量缺失值分析#
查看分类型(categorical)变量的缺失情况,全部为0,则没有缺失值。
data_origin[categorical_list].isna().sum().to_frame().T
survey_type province gender nationality
0 0 0 0 0
所有特征缺失值分析#
查看所有特征的缺失情况。
data_origin.isna().sum()[data_origin.isna().sum() > 0].to_frame().T
edu_other edu_status edu_yr join_party property_other hukou_loc social_neighbor social_friend work_status work_yr … marital_1st s_birth marital_now s_edu s_political s_hukou s_income s_work_exper s_work_status s_work_type
0 7986 1119 1969 7167 7923 4 795 795 5038 5038 … 828 1717 1768 1717 1717 1717 1717 1717 5427 5427
1 rows × 23 columns
首先对于 edu_other 特征,只有在 edu 填写了 14 的情况下才填写,首先检查 edu_other 缺失的记录的 edu 是否为 14 若为 14 则说明 edu_other 不应该为缺失,应该将其删除。
data_origin[data_origin[‘edu_other’].isna()][data_origin[data_origin[‘edu_other’].isna()][‘edu’] == 14]
happiness survey_type province city county survey_time gender birth nationality religion … neighbor_familiarity public_service_1 public_service_2 public_service_3 public_service_4 public_service_5 public_service_6 public_service_7 public_service_8 public_service_9
id
1242 4 2 3 6 13 2015-09-24 17:58:00 1 1971 1 1 … 5 100 90 60 80 70.0 80 70 60 50
3651 3 2 3 6 13 2015-09-24 20:25:00 1 1953 1 1 … 5 100 100 60 50 70.0 50 30 70 40
5330 2 2 3 6 13 2015-09-25 07:57:00 1 1953 1 1 … 5 100 100 100 100 100.0 100 30 100 50
3 rows × 139 columns
可以看到 edu 为 14 的记录中,有3条记录 edu_other 也为缺失;所以将3条记录删除。
data_origin.drop(data_origin[data_origin[‘edu_other’].isna()][data_origin[data_origin[‘edu_other’].isna()][‘edu’] == 14].index, inplace=True)
对于 edu_status 的缺失记录,可以先检查记录对应的 edu 是取的何值。
data_origin[data_origin[‘edu_status’].isna()][‘edu’].plot.hist()
pd.value_counts(data_origin[data_origin[‘edu_status’].isna()][‘edu’])
1 1052
2 65
3 2
Name: edu, dtype: int64
output_57_1
可以看到对于 edu_status 缺失的记录,其对应的 edu 教育程度为别为没有受过任何教育、私塾、扫盲班和小学;对于取值为 1 和 2 的情况,属于跳问选项,对应的 edu_status 属于缺失是正常的;所以将 edu 取值为 3 的记录删除。
data_origin.drop(data_origin[data_origin[‘edu_status’].isna()][data_origin[data_origin[‘edu_status’].isna()][‘edu’] == 3].index, inplace=True)
对于 join_party 即 目前政治面貌是党员的入党时间,只有政治面貌不是党员的缺失值才算正确,查看分布情况。
data_origin[data_origin[‘join_party’].isna()][‘political’].plot.hist()
pd.value_counts(data_origin[data_origin[‘join_party’].isna()][‘political’])
1 6703
2 402
-8 41
3 11
4 5
Name: political, dtype: int64
output_61_1
根据直方图看到,有5条记录的 partical 的取值是 4 而入党时间没有填写,所以将这5条记录删除。
data_origin.drop(data_origin[data_origin[‘join_party’].isna()][data_origin[data_origin[‘join_party’].isna()][‘political’] == 4].index, inplace=True)
对于 hukou_loc 即 目前的户口登记地,查看缺失记录的 hukou 登记情况,发现取值都为 7 即 没有户口,所以缺失属于正常。
data_origin[data_origin[‘hukou_loc’].isna()][‘hukou’].to_frame()
hukou
id
589 7
3657 7
3799 7
7811 7
对于 social_neighbor 和 social_friend 即与与其他朋友进行社交娱乐活动的频繁程度和有多少个晚上是因为出去度假或者探访亲友而没有在家过夜,首先查看缺失记录的 socialize 的分布情况。
data_origin[data_origin[‘social_neighbor’].isna()][‘socialize’].plot.hist()
pd.value_counts(data_origin[data_origin[‘social_neighbor’].isna()][‘socialize’])
1 793
Name: socialize, dtype: int64
output_67_1
可以发现所有的 social_neighbor 和 social_friend 缺失记录的 socialize 即 是否经常在空闲时间做社交的事情全部均为 1 即 从不社交,所以两个特征的缺失值可以使用 1 填充。
data_origin[‘social_neighbor’].fillna(1, inplace=True)
data_origin[‘social_friend’].fillna(1, inplace=True)
对于 s_edu 到 s_work_exper 的特征,缺失值的记录数都一样,所以存在可能这几项特征的缺失记录都来自同一批问卷对象。
首先查看 s_edu 的缺失记录的 marital 的分布情况。
data_origin[data_origin[‘s_edu’].isna()][‘marital’].plot.hist()
pd.value_counts(data_origin[data_origin[‘s_edu’].isna()][‘marital’])
1 827
7 717
6 171
Name: marital, dtype: int64
output_71_1
可以发现 s_edu 缺失的记录的婚姻情况全部均为未婚、离婚或丧偶,均属于没有配偶或同居伴侣的情况,所以属于正常的缺失。
对于 s_political 到 s_work_exper 全部均属于上述情况。
对于 s_work_status 即 配偶或同居伴侣目前的工作状况,首先查看调查问卷。
图片
可以得知只有 s_work_exper 填写了 1 的情况下才应该填写 s_work_status 和 s_work_type 其它选项均需要跳过,所以属于正常缺失值。
下面查看 s_work_status 缺失记录的 s_work_exper 的分布情况。
data_origin[data_origin[‘s_work_status’].isna()][‘s_work_exper’].plot.hist()
pd.value_counts(data_origin[data_origin[‘s_work_status’].isna()][‘s_work_exper’])
5.0 1424
3.0 1017
4.0 823
6.0 221
2.0 217
1.0 1
Name: s_work_exper, dtype: int64
output_73_1
查看得知 s_work_exper 选 1 的记录只有1条,直接删除即可。
data_origin.drop(data_origin[data_origin[‘s_work_status’].isna()][data_origin[data_origin[‘s_work_status’].isna()][‘s_work_exper’] == 1].index, inplace=True)
在调查问卷中,每个选项通用含义,其 -1 表示不适用;-2 表示不知道;-3 表示拒绝回答;-8 表示无法回答。
在这里将所有的特征的负数使用每一个特征的中位数进行填充。
data_origin.shape
(7978, 139)
no_ne_rows_index = (data_origin.drop([‘survey_time’, ‘edu_other’, ‘property_other’, ‘invest_other’], axis=1) < 0).sum(axis=1)[(data_origin.drop([‘survey_time’, ‘edu_other’, ‘property_other’, ‘invest_other’], axis=1) < 0).sum(axis=1) == 0].index
for column, content in data_origin.items():
if pd.api.types.is_numeric_dtype(content):
data_origin[column] = data_origin[column].apply(lambda x : pd.Series(data_origin.loc[no_ne_rows_index, :][column].unique()).median() if(x < 0 and x != np.nan) else x)
将所有的负数填充完成后,再将 NaN 数值全部使用统一的一个值 -1 填充。
data_origin.fillna(-1, inplace=True)
至此,所有特征的缺失值已经全部处理完毕。
文本数据处理#
在所有的特征中,有3个特征分别是 edu_other、property_other、invest_other 是字符串数据,需要将其转换成序号编码(Ordinal Encoding)。
首先查看 edu_other 的填写情况。
data_origin[data_origin[‘edu_other’] != -1][‘edu_other’].to_frame()
edu_other
id
1170 夜校
2513 夜校
4926 夜校
可以看到 edu_other 的填写情况全都是夜校,将字符串转换成序号编码。
data_origin[‘edu_other’] = data_origin[‘edu_other’].astype(‘category’).values.codes + 1
查看 property_other 即 房子产权归属谁,首先检查调查问卷的填写情况。
data_origin[data_origin[‘property_other’] != -1][‘property_other’].to_frame()
property_other
id
76 无产权
92 已购买,但未过户
99 家庭共同所有
132 待办
455 没有产权
… …
7376 家人共有
7746 全家人共有
7776 兄弟共有
7821 未分家,全家所有
7917 家人共有
66 rows × 1 columns
根据填写情况来看,其中有很多填写信息都是一个意思,例如 家庭共同所有 和 全家所有 是同一个意思,但是在python处理中只能一个个的手动处理。
#data_origin.loc[[8009, 9212, 9759, 10517], ‘property_other’] = ‘多人拥有’
#data_origin.loc[[8014, 8056, 10264], ‘property_other’] = ‘未过户’
#data_origin.loc[[8471, 8825, 9597, 9810, 9842, 9967, 10069, 10166, 10203, 10469], ‘property_other’] = ‘全家拥有’
#data_origin.loc[[8553, 8596, 9605, 10421, 10814], ‘property_other’] = ‘无产权’
data_origin.loc[[76, 132, 455, 495, 1415, 2511, 2792, 2956, 3647, 4147, 4193, 4589, 5023, 5382, 5492, 6102, 6272, 6339,
6507, 7184, 7239], ‘property_other’] = ‘无产权’
data_origin.loc[[92, 1888, 2703, 3381, 5654], ‘property_other’] = ‘未过户’
data_origin.loc[[99, 619, 2728, 3062, 3222, 3251, 3696, 5283, 6191, 7295, 7376, 7746, 7821, 7917], ‘property_other’] = ‘全家拥有’
data_origin.loc[[1597, 4993, 5398, 5899, 7240, 7776], ‘property_other’] = ‘多人拥有’
data_origin.loc[[6469, 6891], ‘property_other’] = ‘小产权’
将字符串编码为整数型的序号(ordinal)类型。
data_origin[‘property_other’] = data_origin[‘property_other’].astype(‘category’).values.codes + 1
查看 invest_other 即 从事的投资活动的填写情况。
pd.DataFrame(data_origin[data_origin[‘invest_other’] != -1][‘invest_other’].unique())
0
0 理财产品
1 民间借贷
2 银行理财
3 储蓄存款
4 理财
5 银行存款利息
6 活期储蓄
7 投资服务业、家具业
8 银行存款
9 个人融资
10 租房
11 老人家不清楚
12 家中有部分土地承包出去
13 没有
14 高利贷
15 彩票
16 自己没有,儿女不清楚
17 网上理财
18 统筹
19 福利车票
20 其他理财产品
21 商业万能保险
22 投资开发区
23 字画、茶壶
同样地,将其转换成整数类型的序号(ordinal)编码。
data_origin[‘invest_other’] = data_origin[‘invest_other’].astype(‘category’).values.codes + 1
离群值处理#
data_nona = data_origin.copy()
画出箱型图分析特征的异常值。
并删除离群记录。
sns.boxplot(x=data_nona[‘house’])
AxesSubplot:xlabel=‘house’
output_100_1
data_nona.drop(data_nona[data_nona[‘house’] > 25].index, inplace=True)
sns.boxplot(x=data_nona[‘family_m’])
AxesSubplot:xlabel=‘family_m’
output_102_1
data_nona.drop(data_nona[data_nona[‘family_m’] > 40].index, inplace=True)
sns.boxplot(x=data_nona[‘inc_exp’])
AxesSubplot:xlabel=‘inc_exp’
output_104_1
data_nona.drop(data_nona[data_nona[‘inc_exp’] > 0.6e8].index, inplace=True)
查看调查时间的月份分布情况,因为调查问卷都是在2015年填写,只需要查看月份的离群点。
图片
由图可知调查问卷是从6月开始的,记录中2月的问卷属于异常数据,应该删除。
sns.boxplot(x=data_nona[‘survey_time’].dt.month)
AxesSubplot:xlabel=‘survey_time’
output_107_1
data_nona.drop(data_nona[data_nona[‘survey_time’].dt.month < 6].index, inplace=True)
特征构造#
特征构造也可称为特征交叉、特征组合、数据变换。
连续变量离散化#
离散化除了一些计算方面等等好处,还可以引入非线性特性,也可以很方便的做cross-feature。离散特征的增加和减少都很容易,易于模型的快速迭代。此外,噪声很大的环境中,离散化可以降低特征中包含的噪声,提升特征的表达能力。
pd.DataFrame(continuous_list)
0
0 birth
1 edu_yr
2 income
3 floor_area
4 height_cm
5 weight_jin
6 work_yr
7 family_income
8 family_m
9 house
10 son
11 daughter
12 minor_child
13 marital_1st
14 s_birth
15 marital_now
16 s_income
17 f_birth
18 m_birth
19 inc_exp
20 public_service_1
21 public_service_2
22 public_service_3
23 public_service_4
24 public_service_5
25 public_service_6
26 public_service_7
27 public_service_8
28 public_service_9
将连续型变量全部进行分箱,然后对每个区间进行编码,生成新的离散的特征。
for column in continuous_list:
cut = pd.qcut(data_nona[column], q=5, duplicates=‘drop’)
cat = cut.values
codes = cat.codes
data_nona[column + ‘_discrete’] = codes
for column, content in data_nona.items():
if pd.api.types.is_numeric_dtype(content):
data_nona[column] = content.astype(‘int’)
特征选择#
将连续变量离散化后,生成以后缀 _discrete 的新特征,所以将原来的连续变量的特征删除掉。
data_nona.to_csv(’./data/happiness_train_complete_analysis.csv’)
data_nona.drop(continuous_list, axis=1, inplace=True)
data_nona.to_csv(’./data/happiness_train_complete_nona.csv’)
特征分析#
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns
data = pd.read_csv(’./data/happiness_train_complete_analysis.csv’, index_col=‘id’, parse_dates=[‘survey_time’])
data.head()
happiness survey_type province city county survey_time gender birth nationality religion … inc_exp_discrete public_service_1_discrete public_service_2_discrete public_service_3_discrete public_service_4_discrete public_service_5_discrete public_service_6_discrete public_service_7_discrete public_service_8_discrete public_service_9_discrete
id
1 4 1 12 32 59 2015-08-04 14:18:00 1 1959 1 1 … 2 0 0 0 0 0 0 0 0 0
2 4 2 18 52 85 2015-07-21 15:04:00 1 1992 1 1 … 2 4 1 2 3 4 1 4 0 0
3 4 2 29 83 126 2015-07-21 13:24:00 2 1967 1 0 … 3 4 2 3 3 3 4 4 4 2
4 5 2 10 28 51 2015-07-25 17:33:00 2 1943 1 1 … 0 4 3 2 3 3 4 4 3 2
5 4 1 7 18 36 2015-08-10 09:50:00 2 1994 1 1 … 4 0 0 0 0 0 0 0 0 0
5 rows × 168 columns
data.describe()
happiness survey_type province city county gender birth nationality religion religion_freq … inc_exp_discrete public_service_1_discrete public_service_2_discrete public_service_3_discrete public_service_4_discrete public_service_5_discrete public_service_6_discrete public_service_7_discrete public_service_8_discrete public_service_9_discrete
count 7968.000000 7968.000000 7968.000000 7968.000000 7968.000000 7968.000000 7968.000000 7968.000000 7968.000000 7968.000000 … 7968.000000 7968.000000 7968.000000 7968.000000 7968.000000 7968.000000 7968.000000 7968.000000 7968.000000 7968.000000
mean 3.866466 1.405120 15.158258 42.572164 70.631903 1.530748 1964.710216 1.399724 0.880271 1.452560 … 1.725653 1.665537 1.272214 1.841365 1.613328 1.848519 1.643449 1.651732 1.654869 1.302962
std 0.818844 0.490946 8.915876 27.183764 38.736751 0.499085 16.845155 1.466409 0.324665 1.358444 … 1.338535 1.420309 1.108440 1.342524 1.499494 1.297290 1.533445 1.544477 1.511468 1.078601
min 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1921.000000 1.000000 0.000000 1.000000 … 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000
25% 4.000000 1.000000 7.000000 18.000000 37.000000 1.000000 1952.000000 1.000000 1.000000 1.000000 … 1.000000 0.000000 0.000000 1.000000 0.000000 1.000000 0.000000 0.000000 0.000000 0.000000
50% 4.000000 1.000000 15.000000 42.000000 73.000000 2.000000 1965.000000 1.000000 1.000000 1.000000 … 1.000000 2.000000 1.000000 2.000000 1.000000 2.000000 1.000000 1.000000 1.000000 1.000000
75% 4.000000 2.000000 22.000000 65.000000 104.000000 2.000000 1977.000000 1.000000 1.000000 1.000000 … 3.000000 2.000000 2.000000 3.000000 3.000000 3.000000 3.000000 3.000000 3.000000 2.000000
max 5.000000 2.000000 31.000000 89.000000 134.000000 2.000000 1997.000000 8.000000 1.000000 9.000000 … 4.000000 4.000000 3.000000 4.000000 4.000000 4.000000 4.000000 4.000000 4.000000 3.000000
8 rows × 167 columns
data.info(verbose=True, null_counts=True)
Int64Index: 7968 entries, 1 to 8000
Data columns (total 168 columns):
0 happiness 7968 non-null int64
1 survey_type 7968 non-null int64
2 province 7968 non-null int64
3 city 7968 non-null int64
4 county 7968 non-null int64
5 survey_time 7968 non-null datetime64[ns]
6 gender 7968 non-null int64
7 birth 7968 non-null int64
8 nationality 7968 non-null int64
9 religion 7968 non-null int64
10 religion_freq 7968 non-null int64
11 edu 7968 non-null int64
12 edu_other 7968 non-null int64
13 edu_status 7968 non-null int64
14 edu_yr 7968 non-null int64
15 income 7968 non-null int64
16 political 7968 non-null int64
17 join_party 7968 non-null int64
18 floor_area 7968 non-null int64
19 property_0 7968 non-null int64
20 property_1 7968 non-null int64
21 property_2 7968 non-null int64
22 property_3 7968 non-null int64
23 property_4 7968 non-null int64
24 property_5 7968 non-null int64
25 property_6 7968 non-null int64
26 property_7 7968 non-null int64
27 property_8 7968 non-null int64
28 property_other 7968 non-null int64
29 height_cm 7968 non-null int64
30 weight_jin 7968 non-null int64
31 health 7968 non-null int64
32 health_problem 7968 non-null int64
33 depression 7968 non-null int64
34 hukou 7968 non-null int64
35 hukou_loc 7968 non-null int64
36 media_1 7968 non-null int64
37 media_2 7968 non-null int64
38 media_3 7968 non-null int64
39 media_4 7968 non-null int64
40 media_5 7968 non-null int64
41 media_6 7968 non-null int64
42 leisure_1 7968 non-null int64
43 leisure_2 7968 non-null int64
44 leisure_3 7968 non-null int64
45 leisure_4 7968 non-null int64
46 leisure_5 7968 non-null int64
47 leisure_6 7968 non-null int64
48 leisure_7 7968 non-null int64
49 leisure_8 7968 non-null int64
50 leisure_9 7968 non-null int64
51 leisure_10 7968 non-null int64
52 leisure_11 7968 non-null int64
53 leisure_12 7968 non-null int64
54 socialize 7968 non-null int64
55 relax 7968 non-null int64
56 learn 7968 non-null int64
57 social_neighbor 7968 non-null int64
58 social_friend 7968 non-null int64
59 socia_outing 7968 non-null int64
60 equity 7968 non-null int64
61 class 7968 non-null int64
62 class_10_before 7968 non-null int64
63 class_10_after 7968 non-null int64
64 class_14 7968 non-null int64
65 work_exper 7968 non-null int64
66 work_status 7968 non-null int64
67 work_yr 7968 non-null int64
68 work_type 7968 non-null int64
69 work_manage 7968 non-null int64
70 insur_1 7968 non-null int64
71 insur_2 7968 non-null int64
72 insur_3 7968 non-null int64
73 insur_4 7968 non-null int64
74 family_income 7968 non-null int64
75 family_m 7968 non-null int64
76 family_status 7968 non-null int64
77 house 7968 non-null int64
78 car 7968 non-null int64
79 invest_0 7968 non-null int64
80 invest_1 7968 non-null int64
81 invest_2 7968 non-null int64
82 invest_3 7968 non-null int64
83 invest_4 7968 non-null int64
84 invest_5 7968 non-null int64
85 invest_6 7968 non-null int64
86 invest_7 7968 non-null int64
87 invest_8 7968 non-null int64
88 invest_other 7968 non-null int64
89 son 7968 non-null int64
90 daughter 7968 non-null int64
91 minor_child 7968 non-null int64
92 marital 7968 non-null int64
93 marital_1st 7968 non-null int64
94 s_birth 7968 non-null int64
95 marital_now 7968 non-null int64
96 s_edu 7968 non-null int64
97 s_political 7968 non-null int64
98 s_hukou 7968 non-null int64
99 s_income 7968 non-null int64
100 s_work_exper 7968 non-null int64
101 s_work_status 7968 non-null int64
102 s_work_type 7968 non-null int64
103 f_birth 7968 non-null int64
104 f_edu 7968 non-null int64
105 f_political 7968 non-null int64
106 f_work_14 7968 non-null int64
107 m_birth 7968 non-null int64
108 m_edu 7968 non-null int64
109 m_political 7968 non-null int64
110 m_work_14 7968 non-null int64
111 status_peer 7968 non-null int64
112 status_3_before 7968 non-null int64
113 view 7968 non-null int64
114 inc_ability 7968 non-null int64
115 inc_exp 7968 non-null int64
116 trust_1 7968 non-null int64
117 trust_2 7968 non-null int64
118 trust_3 7968 non-null int64
119 trust_4 7968 non-null int64
120 trust_5 7968 non-null int64
121 trust_6 7968 non-null int64
122 trust_7 7968 non-null int64
123 trust_8 7968 non-null int64
124 trust_9 7968 non-null int64
125 trust_10 7968 non-null int64
126 trust_11 7968 non-null int64
127 trust_12 7968 non-null int64
128 trust_13 7968 non-null int64
129 neighbor_familiarity 7968 non-null int64
130 public_service_1 7968 non-null int64
131 public_service_2 7968 non-null int64
132 public_service_3 7968 non-null int64
133 public_service_4 7968 non-null int64
134 public_service_5 7968 non-null int64
135 public_service_6 7968 non-null int64
136 public_service_7 7968 non-null int64
137 public_service_8 7968 non-null int64
138 public_service_9 7968 non-null int64
139 birth_discrete 7968 non-null int64
140 edu_yr_discrete 7968 non-null int64
141 income_discrete 7968 non-null int64
142 floor_area_discrete 7968 non-null int64
143 height_cm_discrete 7968 non-null int64
144 weight_jin_discrete 7968 non-null int64
145 work_yr_discrete 7968 non-null int64
146 family_income_discrete 7968 non-null int64
147 family_m_discrete 7968 non-null int64
148 house_discrete 7968 non-null int64
149 son_discrete 7968 non-null int64
150 daughter_discrete 7968 non-null int64
151 minor_child_discrete 7968 non-null int64
152 marital_1st_discrete 7968 non-null int64
153 s_birth_discrete 7968 non-null int64
154 marital_now_discrete 7968 non-null int64
155 s_income_discrete 7968 non-null int64
156 f_birth_discrete 7968 non-null int64
157 m_birth_discrete 7968 non-null int64
158 inc_exp_discrete 7968 non-null int64
159 public_service_1_discrete 7968 non-null int64
160 public_service_2_discrete 7968 non-null int64
161 public_service_3_discrete 7968 non-null int64
162 public_service_4_discrete 7968 non-null int64
163 public_service_5_discrete 7968 non-null int64
164 public_service_6_discrete 7968 non-null int64
165 public_service_7_discrete 7968 non-null int64
166 public_service_8_discrete 7968 non-null int64
167 public_service_9_discrete 7968 non-null int64
dtypes: datetime64ns, int64(167)
memory usage: 10.3 MB
首先,查看 happiness 幸福程度的分布,可以发现多数人都属于 比较幸福 的程度。
sns.set_theme(style=“darkgrid”)
sns.displot(data, x=“happiness”, facet_kws=dict(margin_titles=True))
output_129_1
查看每个人的收入和幸福度的散点图,通过散点图可以看出随着收入的提高,大多数点都落在了较高的幸福程度上;即使如此,也会发现存在一些收入非常高的人也处在一个说不上幸福不幸福的程度。
sns.set_theme(style=“whitegrid”)
f, ax = plt.subplots()
sns.despine(f, left=True, bottom=True)
sns.scatterplot(x=“happiness”, y=“income”,
size=“income”,
palette=“ch:r=-.2,d=.3_r”,
data=data, ax=ax)
output_131_1
查看性别男女的幸福程度的分布直方图,在性别特征上没有过多的类别不平衡情况。
sns.set_theme(style=“darkgrid”)
sns.displot(
data, x=“happiness”, col=“gender”,
facet_kws=dict(margin_titles=True)
)
output_133_1
通过直线图,可以看出,随着 edu 受到的教育的提高,幸福程度也随之提升。
sns.set_theme(style=“ticks”)
palette = sns.color_palette(“rocket_r”)
sns.relplot(
data=data,
x=“edu”, y=“happiness”,
kind=“line”, size_order=[“T1”, “T2”], palette=palette,
facet_kws=dict(sharex=False)
)
output_135_0
查看每个幸福程度的出生日期,可以看出,不同幸福程度的年代的人分布都是大同小异的。
sns.set_theme(style=“ticks”, palette=“pastel”)
sns.boxplot(x=“happiness”, y=“birth”,
data=data)
sns.despine(offset=10, trim=True)
output_135_0
将记录分为是否信仰宗教信仰,查看幸福度和健康状况的分裂小提琴图,也可以看出一个趋势,幸福度高的人大多数都分布在较高的健康状况上,而且也可以看出一个现象,随着健康状况和幸福度的提高,信仰宗教信仰的人数也慢慢增加。
sns.set_theme(style=“whitegrid”)
sns.violinplot(data=data, x=“happiness”, y=“health”, hue=“religion”,
split=True, inner=“quart”, linewidth=1)
sns.despine(left=True)
output_137_0
绘制一个多变量分布直方图,可以看出大多数比较幸福的人,房产的数量也不会大幅增加。
import seaborn as sns
sns.set_theme(style=“ticks”)
g = sns.JointGrid(data=data, x=“happiness”, y=“house”, marginal_ticks=True)
g.ax_joint.set(yscale=“linear”)
cax = g.fig.add_axes([.15, .55, .02, .2])
g.plot_joint(
sns.histplot, discrete=(True, False),
cmap=“light:#03012d”, pmax=.8, cbar=True, cbar_ax=cax
)
g.plot_marginals(sns.histplot, element=“step”, color="#03012d")
output_139_1
绘制幸福度和住房建筑面积的核密度估计图,可以看出同样的现象,多数比较幸福的人的房屋建筑面积也不会集中在很高的一个水平,但是也会有一个随着房屋建筑面积的增加幸福度也增加的现象。
sns.set_theme(style=“ticks”)
g = sns.jointplot(
data=data[data[‘floor_area’] < 600],
x=“happiness”, y=“floor_area”,
kind=“kde”,
)
output_141_0
查看各个特征的热力图,可以根据图中的颜色深度看出两两特征之间的相关性的高低。
sns.set_theme(style=“whitegrid”)
corr_list = [‘survey_type’, ‘province’, ‘city’, ‘county’, ‘survey_time’, ‘gender’, ‘birth’, ‘nationality’, ‘religion’,
‘religion_freq’, ‘edu’, ‘income’, ‘political’, ‘floor_area’, ‘height_cm’, ‘weight_jin’, ‘health’, ‘health_problem’,
‘depression’, ‘hukou’, ‘socialize’, ‘relax’, ‘learn’, ‘equity’, ‘class’, ‘work_exper’, ‘work_status’, ‘work_yr’, ‘work_type’,
‘work_manage’, ‘family_income’, ‘family_m’, ‘family_status’, ‘house’, ‘car’, ‘marital’, ‘status_peer’, ‘status_3_before’,
‘view’, ‘inc_ability’]
df = data
corr_mat = data[corr_list].corr().stack().reset_index(name=“correlation”)
g = sns.relplot(
data=corr_mat,
x=“level_0”, y=“level_1”, hue=“correlation”, size=“correlation”,
palette=“vlag”, hue_norm=(-1, 1), edgecolor=".7",
height=10, sizes=(50, 250), size_norm=(-.2, .8),
)
g.set(xlabel="", ylabel="", aspect=“equal”)
g.despine(left=True, bottom=True)
g.ax.margins(.02)
for label in g.ax.get_xticklabels():
label.set_rotation(90)
for artist in g.legend.legendHandles:
artist.set_edgecolor(".7")
output_143_0
查看全国省会城市的幸福人数的占比条形图,通过图中可以看出,湖北省调查人数最多但幸福人数不算高;河南省和山东省的幸福人数的占比非常之高;即使内蒙古自治区的调查人数最少,但是幸福人数的占比却是非常高的。
sns.set_theme(style=“whitegrid”)
province_total = data[‘province’].groupby(data[‘province’]).count().sort_values(ascending=False).to_frame()
province_total.columns = [‘total’]
happiness_involved = []
for index in province_total.index:
happiness_involved.append((data[data[‘province’] == index][data[data[‘province’] == index][‘happiness’] > 3].shape[0]))
happiness_involved = pd.DataFrame(happiness_involved, index=province_total.index)
happiness_involved.columns = [‘involved’]
province_total[‘province’] = province_total.index.map({
1 : ‘Shanghai’, 2 : ‘Yunnan’, 3 : ‘Neimeng’, 4 : ‘Beijing’, 5 : ‘Jilin’, 6 : ‘Sichuan’, 7 : ‘Tianjin’, 8 : ‘Ningxia’,
9 : ‘Anhui’, 10 : ‘Shandong’, 11 : ‘Shanxi’, 12 : ‘Guangdong’, 13 : ‘Guangxi’, 14 : ‘Xinjiang’, 15 : ‘Jiangsu’,
16 : ‘Jiangxi’, 17 : ‘Hebei’, 18 : ‘Henan’, 19 : ‘Zhejiang’, 20 : ‘Hainan’, 21 : ‘Hubei’, 22 : ‘Hunan’, 23 : ‘Gansu’,
24 : ‘Fujian’, 25 : ‘XIzang’, 26 : ‘Guizhou’, 27 : ‘Liangning’, 28 : ‘Chongqing’, 29 : ‘Shaanxi’, 30 : ‘Qinghai’, 31 : ‘Heilongjiang’})
happiness_involved[‘province’] = province_total[‘province’]
f, ax = plt.subplots(figsize=(6, 15))
sns.set_color_codes(“pastel”)
sns.barplot(x=“total”, y=“province”, data=province_total,
label=“Total”, color=“b”)
sns.set_color_codes(“muted”)
sns.barplot(x=“involved”, y=“province”, data=happiness_involved,
label=“Alcohol-involved”, color=“b”)
ax.legend(ncol=2, loc=“lower right”, frameon=True)
ax.set(ylabel="", xlabel=“Happiness of every province”)
sns.despine(left=True, bottom=True)
output_145_0
查看调查对象认为的当今社会的公平度中的幸福人数占比的直方图,多数调查对象认为当今社会是出于一个比较公平的,但仍有近半数人认为不算太公平。
sns.set_theme(style=“ticks”)
f, ax = plt.subplots(figsize=(7, 5))
sns.despine(f)
sns.histplot(
data, hue=‘happiness’,
x=“equity”,
multiple=“stack”,
palette=“light:m_r”,
edgecolor=".3",
linewidth=.5
)
output_147_1
根据多变量的散点图,幸福度高的人的都均匀地分布在了不同身高、体重的地方;体形没有太大地影响幸福度。
sns.set_theme(style=“white”)
sns.relplot(x=“height_cm”, y=“weight_jin”, hue=“happiness”, size=“health”,
alpha=.5, palette=“muted”, data=data)
output_149_1
绘制一个带有误差带的直线图,横轴表示幸福度的提升,纵轴表示期待的年收入的提升,可以看出,在幸福度比较低的人期待的年收入通常会很高并带有非常大的误差,随着幸福度的提升每个人期待的年收入也没有变得更高,并且随之误差带也变小了。
sns.set_theme(style=“ticks”)
palette = sns.color_palette(“rocket_r”)
sns.relplot(
data=data,
x=“happiness”, y=“inc_exp”,
kind=“line”, palette=palette,
aspect=.75, facet_kws=dict(sharex=False)
)
output_151_1
模型建立#
XGBoost 模型介绍#
XGBoost 是一个具有高效、灵活和可移植性的经过优化的分布式 梯度提升 库。它的实现是基于机器学习算法梯度提升框架。XGBoost 提供了并行的提升树(例如GBDT、GBM)以一个非常快速并且精准的方法解决了许多的数据科学问题。相同的代码可以运行在主流的分布式环境(如Hadoop、SGE、MPI)并且可以处理数十亿的样本。
XGBoost代表了极端梯度提升(Extreme Gradient Boosting)。
集成决策树#
首先了解XGBoost的模型选择:集成决策树。树的集成模型是由CART(classification and regression trees)的集合组成。下面一张图简单说明了一个CART分出某个人是否喜欢玩电脑游戏的例子。
图片
将每个家庭成员分到不同的叶子结点上,并赋给他们一个分数,每一个叶结点对应了一个分数。CART与决策树是略有不同的,决策树中每个叶结点只包含了一个决策值。在CART上,真实的分数是与叶结点关联的,可以给出比分类更丰富的解释。这也允许了更具有原则、更一致性的优化方法。
通常,在实践中一个单独的树是不够强大的。实际上使用的是集成模型,将多个树的预测结果汇总到一起。
图片
上图中是一个由两棵树集成在一起的例子。每一个树的预测分数被加到一起得到最终的分数。一个重要的因素是两棵树努力补足彼此。可以写出模型:
y^i=∑k=1Kfk(xi),fk∈F
其中,K 是树的数量,f 是一个在函数空间 F 的函数,并且 F 是一个所有可能的CART的集合。可被优化的目标函数为:
obj(θ)=∑inℓ(yi,y^i)+∑k=1KΩ(fk)
随机森林和提升树实际上都是相同的模型;不同之处是如何去训练它们。如果需要一个用来预测的集成树,只需要写出一个并其可以工作在随机森林和提升树上。
提升树#
正如同所有的监督学习一样,想要训练树就要先定义目标函数并优化它。
一个目标函数要总是包含训练的损失度和正则化项。
obj=∑inℓ(yi,y^i)+∑k=1KΩ(fk)
加性训练#
树需要训练的参数有 fi 每一个都包含了树的结构和叶结点的得分。训练树的结构是比传统的可以直接采用梯度的优化问题更难。一次性训练并学习到所有的树是非常棘手的。相反地,可以采取一个附加的策略,修正已经学习到的,同时增加一课新树。可以写出在第 t 步的预测值 y^(t)i
y(0)iy(1)iy(2)iy(t)i=0=f1(xi)=y(0)i+f1(xi)=f1(xi)+f2(xi)=y(1)i+f2(xi)…=∑k=1Kfk(xi)=y^(t−1)i+ft(xi)
在每一步需要什么的树,增加一棵树,优化目标函数。
obj(t)=∑i=1nℓ(yi,y(t)i)+∑i=1tΩ(fi)=∑i=1nℓ(yi,y(t−1)i+ft(xi))+Ω(ft)+C
如果考虑使用均方误差(MSE)作为损失函数,目标函数将会变成:
obj(t)=∑i=1nℓ(yi,y(t−1)i+ft(xi))+Ω(ft)+C=∑i=1n(yi−(y(t−1)i+ft(xi)))2+Ω(ft)+C=∑i=1n((yi−y(t−1)i)−ft(xi))2+Ω(ft)+C=∑i=1n((yi−y(t−1)i)2−2(yi−y(t−1)i)ft(xi)+ft(xi)2)+Ω(ft)+C=∑i=1n(−2(yi−y(t−1)i)ft(xi)+ft(xi)2)+Ω(ft)+C
MSE的形式是非常优雅的,其中有一个一阶项(通常称作残差)和一个二阶项。对于其它的损失函数(例如logistic的损失函数)而言,是没有那么轻易就可以得到如此优雅的形式。因此,通常会使用泰勒公式损失函数展开到二阶项:
泰勒公式:函数 f(x) 在开区间 (a,b) 上具有 (n+1) 阶导数,对于任一 x∈(a,b) 有
f(x)=f(x0)0!+f′(x0)1!(x−x0)+f′′(x0)2!(x−x0)2+⋯+f(n)(x0)n!(x−x0)n+Rn(x)
obj(t)=∑i=1nℓ(yi,y(t−1)i+ft(xi))+Ω(ft)+C=∑i=1n[ℓ(yi,y(t−1)i)0!+ℓ′(yi,y(t−1)i)1!(y(t)i−y(t−1)i)+ℓ′′(yi,y(t−1)i)2!(y(t)i−y(t−1)i)2]+Ω(ft)+C=∑i=1n[ℓ(yi,y(t−1)i)+ℓ′(yi,y(t−1)i)ft(xi)+12ℓ′′(yi,y(t−1)i)ft(xi)2]+Ω(ft)+C=∑i=1n[ℓ(yi,y(t−1)i)+gift(xi)+12hift(xi)2]+Ω(ft)+C
其中,gi 和 hi 被定义为:
gihi=∂y(t−1)iℓ(yi,y(t−1)i)=∂2y(t−1)iℓ(yi,y(t−1)i)
移除所有的常量,在第 t 步的目标函数就成了:
∑i=1n[gift(xi)+12hift(xi)2]+Ω(ft)
这就成了对于一颗新树的优化目标。一个非常重要的优势就是这个定义的目标函数的值只依赖于 gi 和 hi 这正是XGBoost支持自定义损失函数。可以优化各种损失函数,包括逻辑回归和成对排名(pairwise ranking),使用 gi 和 hi 作为输入的完全相同的求解器求解。
模型复杂度#
定义树的复杂度 Ω(f) 。首先提炼出树的定义 f(x) 为:
ft(x)=wq(x),w∈RT,q:Rd→{1,2,…,T}.
其中 w 是叶结点上的得分向量,q 是一个将每一个数据点分配到对应的叶结点上的函数,T 是叶结点的数量。在XGBoost中,定义复杂度为:
Ω(f)=γT+12λ∑j=1Tw2j
有不止一个方法定义复杂度,但是这种方式在实践中可以表现的很好。正则化项是大多数树包都会被忽略的一部分。这是因为传统的树学习的对待仅仅强调改善杂质,模型的复杂度的控制留给了启发式。通过正式的定义它,可以更好的理解模型并使模型的表现更具有泛化能力。
树的结构分数#
通过对树模型的目标函数的推导,可以得到在第 t 步的树的目标值:
obj(t)≈∑i=1n[giwq(xi)+12hiw2q(xi)]+γT+12λ∑j=1Tw2j=[g1wq(x1)+12h1w2q(x1)+g2wq(x2)+12h2w2q(x2)+⋯+gnwq(xn)+12hnw2q(xn)]+γT+12λ∑j=1Tw2j=∑j=1T[(∑i∈Ijgi)wj+12(∑i∈Ijhi)w2j]+γT+12λ∑j=1Tw2j=∑j=1T[(∑i∈Ijgi)wj+12(∑i∈Ijhi)w2j+12λw2j]+γT=∑j=1T[(∑i∈Ijgi)wj+12(∑i∈Ijhi+λ)w2j]+γT
其中 Ij={i|q(xi)=j} 是第 i 个数据点被分配到第 j 个叶结点上的下标集合。改变了其累加的索引,因为被分配到相同的叶结点上的数据点得到的分数是统一的。进一步压缩表达令 Gj=∑i∈Ijgi 和 Hj=∑i∈Ijhi
obj(t)=∑j=1T[Gjwj+12(Hj+λ)w2j]+γT
其中,wj 是彼此独立的,式子 Gjwj+12(Hj+λ)w2j 是二次的,并且对于给定的结构 q(x) 最好的 wj 和可以得到的最佳的目标规约为:
wj∗obj∗=−GjHj+λ=−12∑j=1TG2jHj+λ+γT
此公式衡量了一棵树的结构 q(x) 有多好。
图片
基本上,对于一颗给定的树结构,将统计量 gi 和 hi 推到它们所属的叶结点上,并将它们累加到一起,使用公式计算衡量这棵树多好。这个分数类似于决策树中的不纯度度量(impurity measure),区别之处在于它还将模型复杂度考虑在内。
学习树的结构#
现在已经有了衡量一棵树好坏的指标,一个典型的想法是枚举所有可能的树并从中挑出最好的一个。实际上这是非常棘手的,所以应该尝试一次优化树的一个级别。具体来说,是将一个子结点分割成两个叶结点,得分增益为:
Gain=12[G2LHL+λ+G2RHR+λ−(GL+GR)2HL+HR+λ]−γ
这个公式可以被分解为几个部分,一部分是在新左子结点的得分,第二部分是在新右子结点上的得分,第三部分是原先叶结点上的得分,第四部分是在新叶结点上的正则化项。可以看到非常重要的因素是,如果增益小于 γ 更好的选择是不去分割出一个新分支。这就是基本的树模型的剪枝(pruning)技术。
对于实际中的数据,通常想要搜索一个最优的分割点。一个高效率的做法是,将所有的实例(记录)排好序,如下图示。
图片
从左到右扫描计算所有分割方案的结构分数是非常高效的,并且可以快速地找出最优的分割点。
加性数训练的限制
因为将所有可能的树结构枚举出来是非常棘手的,所以每次增加一个分割点(split)。这个方法在大多数情况下运行的很好,但是有一些边缘案例导致这个方法失效。对于退化模型的训练结果,每次仅仅考虑一个特征维度。参考Can Gradient Boosting Learn Simple Arithmetic?
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
train = pd.read_csv(’./data/happiness_train_complete_nona.csv’, index_col=‘id’, parse_dates=[‘survey_time’])
test = pd.read_csv(’./data/happiness_test_complete_nona.csv’, index_col=‘id’, parse_dates=[‘survey_time’])
submit = pd.read_csv(’./data/happiness_submit.csv’, index_col=‘id’)
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
X = train.drop([‘happiness’, ‘survey_time’], axis=1)
y = train[‘happiness’]
X_train, X_test, y_train, y_test = train_test_split(X, y)
from xgboost import XGBRegressor
from xgboost import plot_importance
model = XGBRegressor(gamma=0.1, learning_rate=0.1)
model.fit(X_train, y_train)
mean_squared_error(y_test, model.predict(X_test))
0.4596381608913307
predict = pd.DataFrame({‘happiness’ : model.predict(test.drop(‘survey_time’, axis=1))}, index=test.index)
submit.loc[predict.index, ‘happiness’] = predict[‘happiness’]
submit.to_csv(’./data/predict.csv’)