超詳細的一篇資料分析實戰案例,模擬真實業務,建議收藏慢慢閱讀!
目錄
- 必看前言
- 1 行業背景
- 1.1 業務環境
- 1.2 發展現狀
- 1.3 發展趨勢
- 1.4 衡量指標
- 2 資料介紹
- 2.1 基本資訊
- 2.2 家庭資訊
- 2.3 家庭成員
- 2.4 疾病史
- 2.5 金融資訊
- 2.6 個人習慣
- 2.7 家庭狀況
- 2.8 居住城市
- 3 案例分析
- 3.1 分析流程
- 3.1.1 匯入資料,觀察資料
- 3.1.2 探索資料&資料可視化分析
- 3.1.2.1 處理列標簽名例外
- 3.1.2.2 創建自定義翻譯函式
- 3.1.2.3 探索用戶基本資訊
- 3.1.2.4 探索家庭成員欄位資訊
- 3.1.2.5 探索疾病相關欄位
- 3.1.2.6 探索投資相關欄位
- 3.1.2.7 探索生活習慣
- 3.1.2.8 探索家庭收入
- 3.1.2.9 探索所處地區情況
- 3.1.3 資料清洗
- 3.1.3.1 洗掉特征
- 3.1.3.2 洗掉重復值
- 3.1.3.3 劃分訓練集與測驗集
- 3.1.3.4 填充缺失值
- 3.1.4 轉碼
- 3.1.4.1 0-1轉碼
- 3.1.4.2 啞變數轉碼
- 3.1.4.3 對測驗集進行轉碼(總結)
- 3.1.5 資料建模與評估
- 3.1.5.1 初步建模
- 3.1.5.2 網格搜索找最優引數
- 3.1.5.3 模型評估
- 3.1.5.4 輸出規則
- 3.1.6 輸出結果的商業應用
- 3.1.6.1 第一類
- 3.1.6.2 第二類
- 3.1.6.3 了解客戶需求
- 3.1.6.4 開發新的產品
- 結束語
必看前言
本次文章將會介紹一篇基于決策樹模型的金融保險用戶分類綜合專案,我會從行業背景講起,將整個專案包括代碼全po上來,大家看目錄就知道有多么詳細啦,
1 行業背景
1.1 業務環境
- 宏觀
中國是世界第二大保險市場,但在保險密度上與世界平均水平仍有明顯差距, - 業界
保險行業2018年保費規模為3.8萬億,同比增長不足4%,過去"短平快"的發展模式已經不能適應新時代的行業發展需求,行業及用戶長期存在難以解決的痛點,限制了行業發展, - 社會
互聯網經濟的發展,為保險行業帶來了增量市場,同時隨著網民規模的擴大,用戶的行為習慣已發生轉變,這些都需要互聯網的方式進行觸達,保險科技∶當前沿科技不斷應用于保險行業,互聯網保險的概念將會與保險科技概念高度融合,

中國保險市場持續高速增長,根據保監會資料,2011~2018年,全國保費收入從1.4萬億增長至3.8萬億,年復合增長率17.2%,2014年,中國保費收入突破2萬億,成為全球僅次于美國、日本的第三大新興保險市場市場;2016年,中國整體保費入突破3萬億,超過日本,成為全球第二大保險市場;2019年,中國保費收入突破4萬億,
1.2 發展現狀
- 概覽
受保險行業結構轉型時期影響,互聯網保險整體發展受阻,2018年行業保費收入為1889億元,較17年持平,不同險種發展呈現分化格局,其中健康險增長迅猛,2018年同比增長108%,主要由短期醫療險驅動, - 格局
供給端專業互聯網保險公司增長迅速,但過高的固定成本及渠道費用使得其盈利問題凸顯,加上發展現狀強,自營渠道建設及科技輸出是未來的破局方法,渠道端形成第三方平臺為主,官網為輔的格局,第三方平臺逐漸發展出B2C、B2A、B2B2C等多種創新業務模式, - 模式
互聯網保險不僅僅局限于渠道創新,其核心優勢同樣體現在產品設計的創新和服務體驗的提升,
1.3 發展趨勢
- 競合格局
隨著入局企業增多,流量爭奪更加激烈,最終保險公司與第三方平臺深度合作將成為常態, - 保險科技
當前沿科技不斷應用于保險行業,互聯網保險的概念將會與保險科技概念高度融合,
1.4 衡量指標

2 資料介紹
- 資料來源
美國某保險公司,該公司的一款醫療產品準備上市, - 商品介紹
這是一款針對65歲人群推出的醫療附加險,銷售渠道是直郵, - 商業目的
為該產品做用戶畫像,找到最具購買傾向的人群進行營銷,
本次案例資料中共有76個欄位,欄位繁多,在處理資料時,需要先將資料按照類別進行劃分,方便理解查看,
2.1 基本資訊
| 變數名稱 | 變數含義 | 備注 |
|---|---|---|
| KBM_INDY_ID | 用戶ID | 無意義特征 |
| resp_flag | 用戶是否購買保險 | 我們的目標變數 |
| age | 用戶年齡 | 本產品為針對65歲以上的人群保險 |
| GEDN | 性別 | |
| c210mys | 學歷 | 0-unknown; 1-初中; 2-高中不到; 3-高中畢業; 4-大學未畢業; 5-大專; 6-本科; 7-研究生; 8-專業院校畢業; 9-博士 |
| POC19 | 是否有小孩 | |
| CA[XX] | 小孩年齡 |
2.2 家庭資訊
| 變數名稱 | 變數含義 | 備注 |
|---|---|---|
| NOC19 | 家庭小孩個數 | |
| NAH19 | 家庭成年人個數 | |
| NPH19 | 家庭成員人數 | |
| POEP | 家庭是否為有老人 |
2.3 家庭成員
| 變數名稱 | 變數含義 | 備注 |
|---|---|---|
| U18 | 是否有家庭成員小于18歲 | |
| N1819 | 是否有家庭成員在18-19歲之間 | |
| N2029 | 是否有家庭成員在20-29歲之間 | |
| N3039 | 是否有家庭成員在30-39歲之間 | |
| N4049 | 是否有家庭成員在40-49歲之間 | |
| N5059 | 是否有家庭成員在50-59歲之間 | |
| N6064 | 是否有家庭成員在60-64歲之間 | |
| N65P | 是否有家庭成員在65歲以上 |
2.4 疾病史
| 變數名稱 | 變數含義 | 備注 |
|---|---|---|
| AART | 是否有關節炎 | |
| ADBT | 是否有糖尿病 | |
| ADEP | 是否有抑郁癥 | |
| AHBP | 是否有高血壓 | |
| AHCH | 膽固醇含量是否過高 | |
| ARES | 是否有呼吸疾病 | |
| … | … |
2.5 金融資訊
| 變數名稱 | 變數含義 | 備注 |
|---|---|---|
| BANK | 是否有過破產記錄 | |
| FINI | 是否有過保險服務 | |
| INLI | 是否投資過壽險 | |
| INMEDI | 是否購買過醫療險 | |
| INVE | 是否有投資 | |
| … | … |
2.6 個人習慣
| 變數名稱 | 變數含義 | 備注 |
|---|---|---|
| IOLP | 是否網上購買過產品 | |
| MOBPLUS | 是否通過快遞買過東西 | M-通過多種快遞渠道購買;P-或許通過多種快遞渠道購買;S-單一快遞渠道購買;U-不知道; |
| ONLA | 是否上網 | |
| SGFA | 是否喜歡美術 | |
| SGLL | 是否經常有奢侈消費 | |
| SGDE | 是否經常戶外活動 | |
| SGSE | 是否喜歡運動 | |
| … | … |
2.7 家庭狀況
| 變數名稱 | 變數含義 | 備注 |
|---|---|---|
| LIVEWELL | 幸福指數 | 值越大,說明越幸福 |
| HOMSTAT | 是否有房子 | Y:有房子;P:可能有房子;R:租房;TU:不確定 |
| HINSUB | 是否有醫保補貼 | A-C,補貼依次增加 |
| c210cip | 收入所處排名 | 值越大,說明收入越高 |
| c210ebi | 普查家庭有效購買收入 | 值越大,說明有效購買收入越高 |
| c210hmi | 家庭收入 | 值越大,說明家庭收入越高 |
| c210hva | 家庭房屋價值 | 值越大,說明房屋價值越高 |
| c210… | 家庭經濟類資料 | 值越大,說明經濟地位越高 |
2.8 居住城市
| 變數名稱 | 變數含義 | 備注 |
|---|---|---|
| STATE_NAME | 是否網上購買過產品 | |
| c210apvt | 貧窮以上人的比例 | 值越大,說明比例越高 |
| c210b200 | 所處地區有多少居住小區在2000年后建立 | 值越大,說明比例越高 |
| c210blu | 所處地區藍領所占百分比 | 值越大,說明比例越高 |
| c210bpvt | 貧窮以下人的比例 | 值越大,說明比例越高 |
| c210mob | 所處地區mobile home的比例 | 值越大,說明比例越高 |
| c210pdv | 離婚或者分局人群所占比例 | 值越大,說明比例越高 |
| … | 居住地統計資料 | 值越大,說明比例越高 |
3 案例分析
我們可以大概判別哪些特征很可能和用戶是否購買保隙訓有相關關系,
也可以結合我們的業務經驗,以及資料可視化,特征工程方法,先行探索一下,這些特征中哪些特征更重要一些,
可以在建模之后,再回顧我們這里認為比較重要或不重要的特征,看一下判斷是否準確,
3.1 分析流程
- 匯入資料,觀察資料
- 探索資料 & 資料可視化分析(探索缺失值填充方案、轉碼方案、一些特殊欄位的處理方案等)
- 空值填充
- 變數編碼
- 資料建模
- 輸出規則(決策樹圖)
#全部行都能輸出
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
# 解決坐標軸刻度負號亂碼
plt.rcParams['axes.unicode_minus'] = False
# 解決中文亂碼問題
plt.rcParams['font.sans-serif'] = ['Simhei']
3.1.1 匯入資料,觀察資料
data_00 = pd.read_csv('data/ma_resp_data_temp.csv')
feature_dict = pd.read_excel('保險案例資料字典.xlsx')
data_01 = data_00.copy()
3.1.2 探索資料&資料可視化分析
注意:在我們進行探索資料時,可以結合一下EXCEL,用EXCEL來記錄資料后續需要進行的對應操作(可以分成洗掉、轉換、填充三種情況),這樣也方便我們后續進行資料清洗,
data_01.head()

feature_dict

3.1.2.1 處理列標簽名例外
表中的欄位與資料字典不匹配,
判斷data_01中的列標簽名是否都出現在資料字典的變數名中,
data_01.columns

feature_dict.變數名

#求補集
np.setxor1d(data_01.columns,feature_dict.變數名)

'''
#資料表列標簽
NY8Y9', 'N2N29', 'N3N39', 'N4N49', 'N5N59', 'N6N64'
#資料字典
'N1819', 'N2029','N3039','N4049', 'N5059','N6064'
meda # 這個莫名其妙,刪掉
'''
# 刪掉
data_01['meda'].nunique()
75
替換例外標簽
a = ['NY8Y9', 'N2N29', 'N3N39', 'N4N49', 'N5N59', 'N6N64']
b = ['N1819', 'N2029','N3039','N4049', 'N5059','N6064']
# 要替換的列標簽,做成映射字典
dic = dict(zip(a,b))
dic
#自定義要轉化的向量化函式
def tran(x):
if x in dic:
return dic[x]
else:
return x
tran = np.vectorize(tran) #向量化
#使用向量化函式替換例外表頭
data_01.columns = tran(data_01.columns)

3.1.2.2 創建自定義翻譯函式
提高探索資料效率,創建自定義翻譯函式,通過映射字典的方式,替換DataFrame列標簽名為中文:
dic = {k:v for k,v in feature_dict[['變數名','變數說明']].values.reshape(-1,2)}
def chinese(x):
y = x.copy()
#將輸入進來的欄位名通過字典映射的方式去對應
y.columns = pd.Series(y.columns).map(dic)
return y
chinese(data_01).head()

3.1.2.3 探索用戶基本資訊
feature_dict.變數名[:5]

feature_dict.變數名[:5].tolist() #得到串列

data_01[feature_dict.變數名[:5].tolist()].head()

#將0_4列取出來并進行翻譯
data0_4 = chinese(data_01[feature_dict.變數名[:5].tolist()])
data0_4.head()

下面來看一下資料的基本資訊,
data0_4.info()

再看一下缺失值
data0_4.isnull().sum()

發現是有缺失的(后續要處理),
同時,我們不難發現上面這些方法都是常用到的,可以考慮封裝成函式,方便后續使用,
自定義探索特征頻率函式
輸入一個DataFrame,輸出每個特征的頻數分布:
def fre(x):
for i in x.columns:
print("欄位名:",i)
print("----------")
print("欄位資料型別:",x[i].dtype)
print("----------------------------")
print(x[i].value_counts()) #頻數
print("----------------------------")
print("缺失值的個數:",x[i].isnull().sum())
print("------------------------------------------------\n\n")
fre(data0_4)
輸出結果如下:
欄位名: Individual ID
----------
欄位資料型別: int64
----------------------------
142936063 1
68797618 1
193209817 1
172309160 1
171816124 1
..
172641155 1
76214763 1
228943803 1
185032198 1
1966080 1
Name: Individual ID, Length: 43666, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 是否response
----------
欄位資料型別: int64
----------------------------
0 26177
1 17489
Name: 是否response, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 年齡
----------
欄位資料型別: float64
----------------------------
66.0 3967
67.0 3670
65.0 3475
69.0 3449
68.0 3423
70.0 2948
71.0 2943
72.0 2909
74.0 2817
73.0 2814
75.0 2448
76.0 2220
78.0 2038
77.0 2012
79.0 1823
80.0 691
91.0 2
86.0 2
88.0 2
96.0 1
99.0 1
82.0 1
95.0 1
90.0 1
98.0 1
87.0 1
94.0 1
101.0 1
Name: 年齡, dtype: int64
----------------------------
缺失值的個數: 4
------------------------------------------------
欄位名: 性別
----------
欄位資料型別: object
----------------------------
F 25405
M 18261
Name: 性別, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 學歷
----------
欄位資料型別: int64
----------------------------
4 18597
3 12437
6 7493
5 4474
2 462
7 130
0 60
1 9
8 4
Name: 學歷, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
- Individual ID是我們要刪的(后面也有一個ID);
- 是否response則是我們的標簽;
- 年齡則是有些缺失值(年齡有高有低,最高都101歲,可以考慮用中位數填充);
- 性別則是需要進行0-1編碼;
- 學歷需要轉成啞變數(即one-hot編碼),
來看一下購買情況,
import seaborn as sns
#中文編碼
sns.set_style("darkgrid",{"font.sans-serif":['simhei','Droid Sans Fallback']})
#sns.set()
plt.figure(1,figsize=(6,2))
sns.countplot(y='是否response',data=data0_4)
plt.show()

差別不是很大,后續使用class_weight引數與否也不重要,
#根據年齡 概率密度圖
sns.kdeplot(data0_4.年齡[data0_4.是否response==1],label='購買')
sns.kdeplot(data0_4.年齡[data0_4.是否response==0],label='不購買')
sns.kdeplot(data0_4.年齡.dropna(),label='所有人')
plt.xlim([60,90])
plt.xlabel('Age')
plt.ylabel('Density')

3.1.2.4 探索家庭成員欄位資訊
#將5_22列取出來并進行翻譯
data5_22 = chinese(data_01[feature_dict.變數名[5:23].tolist()])
data5_22.head()

呼叫一下剛自定好的函式,
fre(data5_22)
輸出結果為:
欄位名: 是否有小孩
----------
欄位資料型別: object
----------------------------
U 24500
Y 10225
P 8941
Name: 是否有小孩, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 小孩是否在0-2歲之間
----------
欄位資料型別: int64
----------------------------
0 40677
4 2856
1 57
2 48
3 16
6 9
5 3
Name: 小孩是否在0-2歲之間, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 小孩是否在3-5歲之間
----------
欄位資料型別: int64
----------------------------
0 41087
4 2068
1 204
2 202
3 45
5 30
6 29
7 1
Name: 小孩是否在3-5歲之間, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 小孩是否在6-10歲之間
----------
欄位資料型別: int64
----------------------------
0 38969
4 2960
1 553
2 528
6 240
5 209
3 176
7 31
Name: 小孩是否在6-10歲之間, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 小孩是否在11-15歲之間
----------
欄位資料型別: int64
----------------------------
0 39430
4 2281
1 757
2 651
3 211
5 176
6 140
7 20
Name: 小孩是否在11-15歲之間, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 小孩是否在16-18歲之間
----------
欄位資料型別: int64
----------------------------
0 40243
4 1590
1 826
2 718
3 164
5 70
6 54
7 1
Name: 小孩是否在16-18歲之間, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 家庭小孩個數
----------
欄位資料型別: int64
----------------------------
0 33441
1 5089
2 2365
3 1298
4 1008
5 290
6 116
7 30
8 26
9 3
Name: 家庭小孩個數, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 家庭成年人個數
----------
欄位資料型別: int64
----------------------------
1 15792
2 13414
3 7633
4 3942
5 1292
0 1105
6 349
7 101
8 29
9 9
Name: 家庭成年人個數, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 家庭成員人數量
----------
欄位資料型別: int64
----------------------------
1 14970
2 11148
3 6120
4 4123
5 2499
6 1547
0 1105
7 994
8 569
9 316
10 152
11 79
12 25
13 12
15 3
14 3
16 1
Name: 家庭成員人數量, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 是否有家庭成員小于18歲
----------
欄位資料型別: object
----------------------------
N 43665
Y 1
Name: 是否有家庭成員小于18歲, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 是否有家庭成員在18-19歲之間
----------
欄位資料型別: object
----------------------------
N 43579
Y 78
Name: 是否有家庭成員在18-19歲之間, dtype: int64
----------------------------
缺失值的個數: 9
------------------------------------------------
欄位名: 是否有家庭成員在20-29歲之間
----------
欄位資料型別: object
----------------------------
N 41318
Y 2348
Name: 是否有家庭成員在20-29歲之間, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 是否有家庭成員在30-39歲之間
----------
欄位資料型別: object
----------------------------
N 36624
Y 7042
Name: 是否有家庭成員在30-39歲之間, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 是否有家庭成員在40-49歲之間
----------
欄位資料型別: object
----------------------------
N 34740
Y 8926
Name: 是否有家庭成員在40-49歲之間, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 是否有家庭成員在50-59歲之間
----------
欄位資料型別: object
----------------------------
N 38345
Y 5321
Name: 是否有家庭成員在50-59歲之間, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 是否有家庭成員在60-64歲之間
----------
欄位資料型別: object
----------------------------
N 39544
Y 4121
0 1
Name: 是否有家庭成員在60-64歲之間, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 是否有家庭成員在65歲以上
----------
欄位資料型別: object
----------------------------
Y 42461
N 1205
Name: 是否有家庭成員在65歲以上, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 家里是否有老人
----------
欄位資料型別: object
----------------------------
N 33522
Y 10136
Name: 家里是否有老人, dtype: int64
----------------------------
缺失值的個數: 8
------------------------------------------------
- 是否有小孩這個欄位有三種值:U代表不知道,Y代表有,P表示可能有(美國老人和子女間的聯系比較少,不知道自己有沒有孫子孫女),后續轉成啞變數;
- 小孩是否在0~2歲之間:我們發現有4個0~2歲的小孩的數量有2856,如果是子女的話很不科學,所以我們猜測是孫子孫女(所以上面的P才解釋得通);
- 是否有家庭成員是小于18歲的:發現只有1個資料是有小于18歲的成員的,與上述資料完全不符,說明這個資料是有問題的,后續需要刪掉;
- 是有有家庭成員在18~19歲之間:存在缺失值,考慮用眾數填充;
- 是否有家庭成員在60~64歲之間:發現有個資料是0,懷疑可能原本是N弄成了0,后續可以將0替換成N;
- 家里是否有老人:我們統計的物件本來就是老人,但是資料卻很多沒有老人,推測可能存在物件覺得自己不算在其中的情況,我們可以考慮將這個欄位刪掉;
- 其他欄位可以將N和P轉成0和1;
3.1.2.5 探索疾病相關欄位
老套路:
#將23_35列取出來并進行翻譯
data23_35 = chinese(data_01[feature_dict.變數名[23:35].tolist()])
data23_35.head()

fre(data23_35)
欄位名: 是否有關節炎
----------
欄位資料型別: object
----------------------------
N 38369
Y 5297
Name: 是否有關節炎, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 是否有糖尿病
----------
欄位資料型別: object
----------------------------
N 40554
Y 3112
Name: 是否有糖尿病, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 是否有抑郁癥
----------
欄位資料型別: object
----------------------------
N 41674
Y 1992
Name: 是否有抑郁癥, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 是否有高血壓
----------
欄位資料型別: object
----------------------------
N 38102
Y 5564
Name: 是否有高血壓, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 膽固醇含量是否過高
----------
欄位資料型別: object
----------------------------
N 37395
Y 6271
Name: 膽固醇含量是否過高, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 是否有呼吸疾病
----------
欄位資料型別: object
----------------------------
N 40900
Y 2766
Name: 是否有呼吸疾病, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 是否有心臟病
----------
欄位資料型別: object
----------------------------
N 41733
Y 1933
Name: 是否有心臟病, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 是否有過敏性鼻炎
----------
欄位資料型別: object
----------------------------
N 36824
Y 6832
Name: 是否有過敏性鼻炎, dtype: int64
----------------------------
缺失值的個數: 10
------------------------------------------------
欄位名: 是否有消化不良
----------
欄位資料型別: object
----------------------------
N 37381
Y 6285
Name: 是否有消化不良, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 是否耳聾
----------
欄位資料型別: object
----------------------------
N 42082
Y 1584
Name: 是否耳聾, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 是否有皮膚病
----------
欄位資料型別: object
----------------------------
N 41161
Y 2497
Name: 是否有皮膚病, dtype: int64
----------------------------
缺失值的個數: 8
------------------------------------------------
欄位名: 是否視力不好
----------
欄位資料型別: object
----------------------------
N 38399
Y 5267
Name: 是否視力不好, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
- 是否有過敏性鼻炎:有10個缺失值,考慮用眾數填充;
- 是否有皮膚病:有8個缺失值,考慮用眾數填充;
- N和P轉成0和1,
從常理性來講,疾病之間很可能是存在相關性的,所以我們可以看看這些欄位之間的相關性(畫熱力圖),找到高度相關的欄位并洗掉,
那我們得先將欄位資料 轉換成0-1編碼:
#0 1 轉碼
def zero_one(x):
for i in x.columns:
if x[i].dtype == 'object':
dic = dict(zip(list(x[i].value_counts().index),range(x[i].nunique())))
x[i] = x[i].map(dic)
return x
zero_one(data23_35).corr()

單看表可能比較頭疼,下面我們畫張熱力圖看看:
import matplotlib.pyplot as plt
import seaborn as sns
sns.heatmap(zero_one(data23_35).corr(),cmap='Blues')

越藍相關性越高,肉眼可以看出來相關性了,不過還是寫個函式直接輸出相關性高的欄位更好,這里我們輸出相關性高于0.65的欄位(可以刪,但不然刪太多,對決策樹的創建也不好),
def higt_cor(x,y=0.65):
data_cor = (x.corr()>y)
a=[]
for i in data_cor.columns:
if data_cor[i].sum()>=2: # 有兩個或兩個以上的TRUE
a.append(i)
return a #這些是我們要考慮洗掉的
higt_cor(data23_35) #是否有關節炎 膽固醇含量是否過高 是否有過敏性鼻炎
[‘是否有關節炎’, ‘是否有高血壓’, ‘膽固醇含量是否過高’, ‘是否有過敏性鼻炎’, ‘是否有消化不良’]
我們可以再結合一下圖,考慮洗掉是否有關節炎 膽固醇含量是否過高 是否有過敏性鼻炎這三個欄位,
3.1.2.6 探索投資相關欄位
老套路:
#將35_41列取出來并進行翻譯
data35_41 = chinese(data_01[feature_dict.變數名[35:41].tolist()])
data35_41.head()

- 都是要改成0-1編碼,
fre(data35_41)
欄位名: 是否有過破產記錄
----------
欄位資料型別: object
----------------------------
N 40599
Y 3067
Name: 是否有過破產記錄, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 是否大學畢業
----------
欄位資料型別: object
----------------------------
N 39236
Y 4422
Name: 是否大學畢業, dtype: int64
----------------------------
缺失值的個數: 8
------------------------------------------------
欄位名: 是否用過保險服務
----------
欄位資料型別: object
----------------------------
N 42793
Y 873
Name: 是否用過保險服務, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 是否投資過壽險
----------
欄位資料型別: object
----------------------------
N 35871
Y 7795
Name: 是否投資過壽險, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 是否購買過醫療險
----------
欄位資料型別: object
----------------------------
N 40016
Y 3650
Name: 是否購買過醫療險, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 是否有投資
----------
欄位資料型別: object
----------------------------
N 33896
Y 9770
Name: 是否有投資, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
- 是否大學畢業:存在缺失值,不過這個資料明顯和之前的學歷資訊重復了,可以刪掉,
接下來還是畫圖看一下屬性間的相關性:
sns.heatmap(zero_one(data35_41).corr(),cmap='Blues')

可以看到后下角顏色明顯很深,
是否有過投資和是否投資過壽險明顯相關,當然我們也可以再呼叫一下函式查看結果:
higt_cor(data35_41)
[‘是否投資過壽險’, ‘是否有投資’]
結果確實是這樣,那我們這個時候要考慮洗掉其中哪一個?
我們結合一下我們的目的——推薦醫療附加險,是否投資過壽險感覺更貼合我們的目的,所以考慮刪掉是否有投資這個欄位,
3.1.2.7 探索生活習慣
老套路:
#將41_51列取出來并進行翻譯
data41_51 = chinese(data_01[feature_dict.變數名[41:51].tolist()])
data41_51.head()

還是都要改成0-1編碼,
我們接著在呼叫一下函式:
fre(data41_51)
欄位名: 是否網上購買過產品
----------
欄位資料型別: object
----------------------------
N 36197
Y 7469
Name: 是否網上購買過產品, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 是否通過快遞買過東西
----------
欄位資料型別: object
----------------------------
M 27450
S 9947
U 3912
P 2350
Name: 是否通過快遞買過東西, dtype: int64
----------------------------
缺失值的個數: 7
------------------------------------------------
欄位名: 所處的縣的大小
----------
欄位資料型別: object
----------------------------
A 26539
B 12687
C 3823
D 607
Name: 所處的縣的大小, dtype: int64
----------------------------
缺失值的個數: 10
------------------------------------------------
欄位名: 是否上網
----------
欄位資料型別: object
----------------------------
Y 28473
N 15193
Name: 是否上網, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 是否喜歡美術
----------
欄位資料型別: object
----------------------------
N 37029
Y 6637
Name: 是否喜歡美術, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 是否經常有奢侈消費
----------
欄位資料型別: object
----------------------------
N 33801
Y 9865
Name: 是否經常有奢侈消費, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 是否經常戶外活動
----------
欄位資料型別: object
----------------------------
N 35728
Y 7938
Name: 是否經常戶外活動, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 是否喜歡運動
----------
欄位資料型別: object
----------------------------
N 31867
Y 11799
Name: 是否喜歡運動, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 是否熱愛科技
----------
欄位資料型別: object
----------------------------
N 31370
Y 12296
Name: 是否熱愛科技, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 幸福指數
----------
欄位資料型別: float64
----------------------------
4.0 16093
3.0 11975
1.0 8837
2.0 6395
6.0 361
Name: 幸福指數, dtype: int64
----------------------------
缺失值的個數: 5
------------------------------------------------
- 是否通過快遞買過東西:轉成啞變數,同時用眾數填補缺失值;
- 所處的縣的大小:轉成啞變數,同時用眾數填補缺失值;
- 其他欄位則改成0-1編碼,
我們也可以來看下縣的大小和購買結果的關系:
sns.countplot(x='N2NCY',hue='resp_flag',data=data_01)
plt.xlabel('縣的大小')
plt.ylabel('購買數量')

不難看出,縣越大的購買人數就越多,跟我們的日常理解差不多,
3.1.2.8 探索家庭收入
老套路:
#將51_59列取出來并進行翻譯
data51_59 = chinese(data_01[feature_dict.變數名[51:59].tolist()])
data51_59.head()

fre(data51_59)
輸出結果:
欄位名: 是否有房子
----------
欄位資料型別: object
----------------------------
Y 31478
U 4747
P 4604
R 2623
T 204
Name: 是否有房子, dtype: int64
----------------------------
缺失值的個數: 10
------------------------------------------------
欄位名: 是否有醫保補貼
----------
欄位資料型別: object
----------------------------
U 21612
C 8972
A 6765
B 6306
Name: 是否有醫保補貼, dtype: int64
----------------------------
缺失值的個數: 11
------------------------------------------------
欄位名: 收入所處排名
----------
欄位資料型別: float64
----------------------------
79.0 713
61.0 648
55.0 647
82.0 636
88.0 607
...
25.0 258
46.0 254
17.0 246
23.0 225
42.0 162
Name: 收入所處排名, Length: 99, dtype: int64
----------------------------
缺失值的個數: 2
------------------------------------------------
欄位名: 普查家庭有效購買收入
----------
欄位資料型別: int64
----------------------------
48 1034
45 994
44 980
47 967
49 957
...
230 1
198 1
154 1
164 1
191 1
Name: 普查家庭有效購買收入, Length: 208, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 家庭收入
----------
欄位資料型別: float64
----------------------------
49.0 746
46.0 713
63.0 710
59.0 687
60.0 683
...
246.0 1
221.0 1
218.0 1
207.0 1
241.0 1
Name: 家庭收入, Length: 224, dtype: int64
----------------------------
缺失值的個數: 7
------------------------------------------------
欄位名: 家庭房屋價值
----------
欄位資料型別: float64
----------------------------
999.0 1371
138.0 244
168.0 233
125.0 232
175.0 207
...
956.0 1
796.0 1
983.0 1
940.0 1
20.0 1
Name: 家庭房屋價值, Length: 983, dtype: int64
----------------------------
缺失值的個數: 15
------------------------------------------------
欄位名: 社會經濟地位評分
----------
欄位資料型別: int64
----------------------------
77 1194
75 1153
78 1119
80 1119
79 1114
...
166 13
169 12
162 11
165 11
168 8
Name: 社會經濟地位評分, Length: 111, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 家庭自成立日起的時間
----------
欄位資料型別: float64
----------------------------
55.0 3257
56.0 3110
54.0 3034
53.0 2866
57.0 2826
...
28.0 3
25.0 2
26.0 1
81.0 1
20.0 1
Name: 家庭自成立日起的時間, Length: 64, dtype: int64
----------------------------
缺失值的個數: 15
------------------------------------------------
- 是否有房子:轉成啞變數,同時用眾數填補缺失值;
- 是否有醫保補貼:轉成啞變數,同時用眾數填補缺失值;
- 收入所處排名:有缺失值,用中位數填補缺失值;
- 家庭收入:有缺失值,用中位數填補缺失值;
- 家庭自成立的時間:有缺失值,用中位數填補缺失值;
接下來我們同樣來查看屬性間的相關性,看看哪些是需要洗掉的,
sns.heatmap(zero_one(data51_59).corr(),cmap='Blues')

其實主要就是中間這一部分顏色特別深,
higt_cor(data51_59)
[‘收入所處排名’, ‘普查家庭有效購買收入’, ‘家庭收入’, ‘家庭房屋價值’, ‘社會經濟地位評分’]
從我們的認知來講,這些自然是存在高度相關性的,我們可以考慮保留普查家庭有效購買收入這一個屬性,
3.1.2.9 探索所處地區情況
老套路:
#將59列之后取出來并進行翻譯
data59 = chinese(data_01[feature_dict.變數名[59:].tolist()])
data59.head()

很明顯,像這個貧窮以下人的比例和所處地區mobile home的比例這兩個屬性,我們一看就可以知道是相關性很高的,當然其他欄位也是,我們還是用定義好的函式來看一下情況,
fre(data59)
輸出結果為:
欄位名: 該客戶被多少個名單source 包含
----------
欄位資料型別: int64
----------------------------
8 4168
9 4152
10 4131
7 3878
11 3735
6 3654
5 3400
4 3144
3 3044
12 3038
2 2229
13 2017
14 1243
1 728
15 630
16 310
17 104
18 48
19 13
Name: 該客戶被多少個名單source 包含, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 所處的省份
----------
欄位資料型別: object
----------------------------
CA 11700
OH 8959
NY 6481
IN 4954
CT 2873
MO 2202
WI 1884
GA 1687
KY 1375
NH 904
ME 647
Name: 所處的省份, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 貧窮以上人的比例
----------
欄位資料型別: int64
----------------------------
99 4081
98 3624
97 3440
96 3307
95 2885
...
17 1
25 1
35 1
22 1
15 1
Name: 貧窮以上人的比例, Length: 81, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 所處地區有多少居住小區在2000年及以后建立
----------
欄位資料型別: float64
----------------------------
4.0 2467
2.0 2401
3.0 2364
5.0 2317
1.0 2305
...
81.0 5
90.0 4
95.0 4
86.0 3
94.0 2
Name: 所處地區有多少居住小區在2000年及以后建立, Length: 100, dtype: int64
----------------------------
缺失值的個數: 5
------------------------------------------------
欄位名: 所處地區藍領所占百分比
----------
欄位資料型別: int64
----------------------------
18 1717
16 1706
20 1696
17 1679
22 1670
...
61 3
64 2
63 2
67 1
58 1
Name: 所處地區藍領所占百分比, Length: 66, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 貧窮以下人的比例
----------
欄位資料型別: int64
----------------------------
2 3604
3 3464
4 3308
1 3082
5 2884
...
65 1
99 1
83 1
78 1
85 1
Name: 貧窮以下人的比例, Length: 82, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 所處地區mobile home的比例
----------
欄位資料型別: int64
----------------------------
0 33478
1 1677
2 1442
3 906
4 658
...
85 2
66 2
73 1
99 1
78 1
Name: 所處地區mobile home的比例, Length: 96, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 離婚或者分居人群所占比例
----------
欄位資料型別: int64
----------------------------
14 3288
15 3254
16 3162
13 3100
17 3073
12 2826
18 2805
11 2525
19 2345
10 2139
20 2055
9 1727
21 1724
22 1484
8 1228
23 1154
24 1050
7 999
25 656
26 517
6 500
27 414
5 282
28 266
29 199
30 175
31 135
4 130
32 106
0 62
33 56
3 45
34 39
36 38
2 22
35 21
37 13
38 12
42 7
39 6
41 5
1 5
50 4
40 4
55 3
44 2
48 1
47 1
46 1
43 1
Name: 離婚或者分居人群所占比例, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 已婚人群所占比例
----------
欄位資料型別: int64
----------------------------
53 1221
52 1209
48 1164
51 1161
40 1157
...
81 4
79 2
80 2
78 1
82 1
Name: 已婚人群所占比例, Length: 83, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 有房子人所占比例
----------
欄位資料型別: int64
----------------------------
85 972
89 913
88 890
81 877
86 856
...
7 120
3 116
97 69
98 9
99 8
Name: 有房子人所占比例, Length: 100, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 獨宅住戶所占比例
----------
欄位資料型別: float64
----------------------------
99.0 6996
98.0 1213
0.0 1193
97.0 1043
95.0 994
...
14.0 142
19.0 139
17.0 138
27.0 123
24.0 122
Name: 獨宅住戶所占比例, Length: 100, dtype: int64
----------------------------
缺失值的個數: 23
------------------------------------------------
欄位名: 有小孩的家庭所占比例
----------
欄位資料型別: int64
----------------------------
32 1777
33 1754
34 1719
30 1704
31 1690
...
79 2
76 2
99 1
86 1
83 1
Name: 有小孩的家庭所占比例, Length: 86, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 白領所占比例
----------
欄位資料型別: float64
----------------------------
61.0 1198
59.0 1098
63.0 1098
57.0 1072
64.0 1071
...
16.0 3
11.0 2
13.0 1
1.0 1
14.0 1
Name: 白領所占比例, Length: 91, dtype: int64
----------------------------
缺失值的個數: 14
------------------------------------------------
欄位名: 所處地區居住年限
----------
欄位資料型別: float64
----------------------------
19.0 6287
12.0 2756
1.0 2319
17.0 2125
7.0 2013
...
96.0 1
59.0 1
68.0 1
98.0 1
67.0 1
Name: 所處地區居住年限, Length: 70, dtype: int64
----------------------------
缺失值的個數: 6
------------------------------------------------
欄位名: Individual ID
----------
欄位資料型別: int64
----------------------------
142936063 1
68797618 1
193209817 1
172309160 1
171816124 1
..
172641155 1
76214763 1
228943803 1
185032198 1
1966080 1
Name: Individual ID, Length: 43666, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: 所在地區處方藥計劃覆寫的比例
----------
欄位資料型別: int64
----------------------------
46 5765
65 2634
47 2518
61 2002
42 1782
...
86 1
88 1
27 1
93 1
95 1
Name: 所在地區處方藥計劃覆寫的比例, Length: 69, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
欄位名: zip level的家庭收入排名
----------
欄位資料型別: int64
----------------------------
0 6326
9 5119
8 4784
1 4657
7 4191
2 3998
6 3896
3 3611
5 3549
4 3535
Name: zip level的家庭收入排名, dtype: int64
----------------------------
缺失值的個數: 0
------------------------------------------------
-
所處地區有多少居住小區在2000年及以后建立:有5個缺失值,可以用中位數填充缺失值;
-
獨宅住戶所占比例:有23個缺失值,可以用中位數填充缺失值;
-
白領所占比例:有14個缺失值,可以用中位數填充缺失值;
-
所處地區居住年限:有6個缺失值,可以用中位數填充缺失值;
-
Individual ID:前面就說過啦,可以刪掉這個無用資料;
-
所在地區處方藥計劃覆寫的比例:和是否有醫療補貼感覺也存在相關性,可以先mark一下;
-
所處的省份:轉成啞變數,
可以看下省份之間跟購買結果的情況;
sns.countplot(x='STATE_NAME',hue='resp_flag',data=data_01)
plt.xlabel('所處的省份')
plt.ylabel('購買數量')

從圖可以看出,我們后面營銷時重點可以放在NY和OH這兩個地方,特別是NY這個地方,
接著我們可以單獨將與人群有關的資料拿出來看下彼此之間的相關性,
a = chinese(data_01[["c210apvt","c210blu","c210bpvt","c210mob","c210wht","zhip19"]])
sns.heatmap(a.corr(),cmap='Blues')

可以看到這里還會出現一些負的相關性,主要是因為我們的資料是比例資料,
從產品的目的來講,我們的主要目標人群肯定是貧窮以上的人(窮人都這么窮了,應該不會再花錢購買保險了)
接著我們再呼叫一下方法看下那些相關性高的屬性,
higt_cor(data59)
[‘貧窮以上人的比例’, ‘已婚人群所占比例’, ‘有房子人所占比例’, ‘獨宅住戶所占比例’]
但是這里確實不太適合用這種方法,因為資料主要是比例而不是0-1資料,
當然我們可以看一下所處地區情況的所有欄位之間的相關性:
sns.heatmap(data59.corr(),cmap='brg')

這里越綠相關性越好,
我們這里考慮刪掉所處地區藍領所占百分比、貧窮以下人的比例、有房子人所占比例這三個欄位給刪掉,
好,到這里,我們資料探索的程序就搞定了,接下來就是通過資料,將我們做好標記的(哪些是要轉的、哪些是要刪的、哪些是要填充的)資料進行處理,
3.1.3 資料清洗
這里我們還是先備份一下資料:
data_02 = data_01.copy()
data_02.shape
(43666, 76)
3.1.3.1 洗掉特征
我們從EXCEL找出要洗掉的欄位,直接全部刪掉,就不用一個個找啦,
del_col = ["KBM_INDV_ID","U18","POEP","AART","AHCH","AASN","COLLEGE",
"INVE","c210cip","c210hmi","c210hva","c210kses","c210blu","c210bpvt","c210poo","KBM_INDV_ID","meda"]
data_02 = data_02.drop(columns=del_col)
data_02.shape
(43666, 60)
3.1.3.2 洗掉重復值
我記得我們是沒有發現重復值的,不過一般我們還是會進行這一環節的(而且也很簡單),
data_02.drop_duplicates().shape
(43666, 60)
果然,沒有重復值,
3.1.3.3 劃分訓練集與測驗集
一定要先劃分資料集再填充、轉碼,別急!
from sklearn.model_selection import train_test_split
y = data_02.pop('resp_flag') #標簽
X = data_02 #特征
Xtrain,Xtest,Ytrain,Ytest = train_test_split(X,y,test_size=0.3,random_state=100)
# 最好還是再備份一下,以防萬一
Xtrain_01=Xtrain.copy()
Xtest_01=Xtest.copy()
Ytrain_01=Ytrain.copy()
Ytest_01=Ytest.copy()
3.1.3.4 填充缺失值
填充中位數
老規矩,我們打開EXCEL,找到我們標記了需要填充中位數的欄位,
fil = ["age","c210mah","c210b200","c210psu","c210wht","ilor"]
Xtrain_01[fil].median()
輸出結果為:
age 71.0
c210mah 53.0
c210b200 10.0
c210psu 77.0
c210wht 61.0
ilor 15.0
dtype: float64
dic = dict(zip(Xtrain_01[fil].median().index,Xtrain_01[fil].median()))
dic
輸出結果為:
{'age': 71.0,
'c210mah': 53.0,
'c210b200': 10.0,
'c210psu': 77.0,
'c210wht': 61.0,
'ilor': 15.0}
#向訓練集填充中位數
Xtrain_01 = Xtrain_01.fillna(dic)
填充眾數
基本同樣的操作:
mod = ["N1819","ASKN","MOBPLUS","N2NCY","LIVEWELL","HOMSTAT","HINSUB"]
dic_mod = dict(zip(Xtrain_01[mod].mode().columns,Xtrain_01[mod].iloc[0,:]))
Xtrain_01 = Xtrain_01.fillna(dic_mod)
替換填充
這里可不要忘了我們最開始發現的0換N,
Xtrain_01['N6064'] = Xtrain_01['N6064'].replace('0','N') #0 替換成 N
Xtrain_01.isnull().sum()[Xtrain_01.isnull().sum()!=0]
Series([], dtype: int64)
對測驗集進行填充(總結)
這里測驗集也是一樣的方式:
# 需要填的欄位
fil = ["age","c210mah","c210b200","c210psu","c210wht","ilor"]
#填充中位數--測驗集
dic = dict(zip(Xtest_01[fil].median().index,Xtest_01[fil].median()))
Xtest_01 = Xtest_01.fillna(dic)
# #填充眾數--測驗集
mod = ["N1819","ASKN","MOBPLUS","N2NCY","LIVEWELL","HOMSTAT","HINSUB"]
dic_mod = dict(zip(Xtest_01[mod].mode().columns,Xtest_01[mod].iloc[0,:]))
Xtest_01 = Xtest_01.fillna(dic_mod)
# #替換填充
Xtest_01['N6064'] = Xtest_01['N6064'].replace('0','N')
Xtest_01.isnull().sum()[Xtest_01.isnull().sum() !=0]
3.1.4 轉碼
打開我們記錄好的EXCEL檔案,找到需要進行轉碼的資料,
不過我們可以發現確實是有點多,就不適合一個個復制出來啦,我們可以寫一個方法,讀取EXCEL檔案,把變數名和轉碼的兩列欄位復制到另一個sheet里,方便讀取,
encod_col = pd.read_excel('保險案例資料字典_清洗.xlsx',sheet_name=2)
encod_col.head()

# 查看Xtrain_01中object型別
object_tr =Xtrain_01.describe(include='O').columns
object_tr
輸出結果為:
Index(['GEND', 'ADBT', 'ADEP', 'AHBP', 'ARES', 'AHRT', 'ADGS', 'AHRL', 'ASKN',
'AVIS', 'BANK', 'FINI', 'INLI', 'INMEDI', 'IOLP', 'MOBPLUS', 'N2NCY',
'N1819', 'N2029', 'N3039', 'N4049', 'N5059', 'N6064', 'N65P', 'ONLA',
'SGFA', 'SGLL', 'SGOE', 'SGSE', 'SGTC', 'POC19', 'HOMSTAT', 'HINSUB',
'STATE_NAME'],
dtype='object')
#檢查一下轉碼的目標是否出現
np.setdiff1d(object_tr,encod_col['變數名'])
array([], dtype=object)
3.1.4.1 0-1轉碼
# 獲取0-1 轉碼的變數名
z_0_list = encod_col[encod_col['轉']=='0-1'].變數名
z_0_list.head()
輸出結果為:
0 GEND
3 N1819
4 N2029
5 N3039
6 N4049
Name: 變數名, dtype: object
# 重新定義一個Xtrain_02,用于轉碼
Xtrain_02 = Xtrain_01[z_0_list]
Xtrain_02.head()

#sklearn的預處理模塊
from sklearn.preprocessing import OrdinalEncoder
#fit_transform 直接轉
new_arr = OrdinalEncoder().fit_transform(Xtrain_02)
new_arr

下面我們來做下合并,
# columns 設定表頭為原來的 index 索引也是原來
Xtrain_02 = pd.DataFrame(data=new_arr,columns=Xtrain_02.columns,index=Xtrain_02.index)
Xtrain_02.head()

將轉好的Xtrain_02 0-1編碼變數 替換掉Xtrain_01:
Xtrain_01[z_0_list] = Xtrain_02
Xtrain_01.head()

3.1.4.2 啞變數轉碼
注意:啞變數轉碼有兩種方法,一種是pandas中的get_dummies方法,另一種是sklearn中的onehot方法,區別主要在于pandas中的方法只能處理字符變數不能處理數值變數,而sklearn都可以,所以如果用get_dummies處理數值型資料的話得先將數值型資料轉換成字符型資料,
下面我們用get_dummies來實作轉碼,
#獲取啞變數---轉碼的變數名
o_h_list = encod_col[encod_col['轉']=='啞變數'].變數名
o_h_list
輸出結果為:
1 c210mys
2 POC19
24 MOBPLUS
25 N2NCY
32 LIVEWELL
33 HOMSTAT
34 HINSUB
35 STATE_NAME
Name: 變數名, dtype: object
Xtrain_01[o_h_list].head()

o_h_01 = ['c210mys','LIVEWELL'] #非字符型的變數
o_h_02 = [i for i in o_h_list if i not in o_h_01] #字符型別的變數
#先轉o_h_02
Xtrain_02 = Xtrain_01.copy()
chinese(Xtrain_02[o_h_02]).head()

Xtrain_02 = pd.get_dummies(chinese(Xtrain_02[o_h_02]))
Xtrain_02.head()

#w我們再轉 o_h_01
Xtrain_03 = Xtrain_01.copy()
#轉成字符型別
Xtrain_03 = Xtrain_03[o_h_01].astype(str)
#轉化覆寫
Xtrain_03 = pd.get_dummies(chinese(Xtrain_03[o_h_01]))
Xtrain_03.head()

Xtrain_02 Xtrain_03 是轉好的 先洗掉原轉碼的欄位再將轉好的插入到資料集中,
# Xtrain_04 洗掉原轉碼的欄位
Xtrain_04 = Xtrain_01.copy()
Xtrain_04 = chinese(Xtrain_04.drop(columns=o_h_01+o_h_02))
Xtrain_04.head()

Xtrain_04.shape
(30566, 51)
Xtrain_02.shape #字符的啞變數
(30566, 31)
Xtrain_03.shape #非字符的啞變數
(30566, 14)
#將 Xtrain_04 Xtrain_02 Xtrain_03 合并
Xtrain_05 = pd.concat([Xtrain_04,Xtrain_02,Xtrain_03],axis=1)
Xtrain_05.shape
(30566, 96)
Xtrain_05.head()

3.1.4.3 對測驗集進行轉碼(總結)
0-1 轉碼總結:
#獲取需要轉碼的欄位
encod_col = pd.read_excel('保險案例資料字典_清洗.xlsx',sheet_name=2)
# 查看Xtest_01中object型別
object_tr =Xtest_01.describe(include='O').columns
#檢查一下轉碼的目標是否出現
np.setdiff1d(object_tr,encod_col['變數名'])
#0-1 轉碼
# 獲取0-1 轉碼的變數名
z_0_list = encod_col[encod_col['轉']=='0-1'].變數名
Xtest_02 = Xtest_01[z_0_list]
#sklearn的預處理模塊
from sklearn.preprocessing import OrdinalEncoder
#fit_transform 直接轉
new_arr = OrdinalEncoder().fit_transform(Xtest_02)
# columns 設定表頭為原來的 index 索引也是原來
Xtest_02 = pd.DataFrame(data=new_arr,columns=Xtest_02.columns,index=Xtest_02.index)
Xtest_01[z_0_list] = Xtest_02
Xtest_01.head()

啞變數 總結
#獲取啞變數轉碼的變數
o_h_list = encod_col[encod_col['轉']=='啞變數'].變數名
o_h_01 = ['c210mys','LIVEWELL'] #非字符型的變數
o_h_02 = [i for i in o_h_list if i not in o_h_01] #字符型別的變數
#先轉o_h_02 字符型別
Xtest_02 = Xtest_01.copy()
Xtest_02 = pd.get_dummies(chinese(Xtest_02[o_h_02]))
#w我們再轉 o_h_01 非字符
Xtest_03 = Xtest_01.copy()
#轉成字符型別
Xtest_03 = Xtest_03[o_h_01].astype(str)
#轉化覆寫
Xtest_03 = pd.get_dummies(chinese(Xtest_03[o_h_01]))
# Xtrain_04 洗掉原轉碼的欄位
Xtest_04 = Xtest_01.copy()
Xtest_04 = chinese(Xtest_04.drop(columns=o_h_01+o_h_02))
#將 Xtest_04 Xtest_02 Xtest_03 合并
Xtest_05 = pd.concat([Xtest_04,Xtest_02,Xtest_03],axis=1)
Xtest_05.shape
(13100, 96)
Xtest_05.head()

3.1.5 資料建模與評估
3.1.5.1 初步建模
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score
clf = DecisionTreeClassifier(random_state=420,class_weight='balanced')
cvs =
cross_val_score(clf,Xtrain_05,Ytrain)
cvs.mean()
0.5993914576104883
3.1.5.2 網格搜索找最優引數
from sklearn.model_selection import GridSearchCV
#測驗引數
param_test = {
'splitter':('best','random'),
'criterion':('gini','entropy'), #基尼 資訊熵
'max_depth':range(3,15) #最大深度
#,min_samples_leaf:(1,50,5)
}
gsearch= GridSearchCV(estimator=clf, #對應模型
param_grid=param_test,#要找最優的引數
scoring='roc_auc',#準確度評估標準
n_jobs=-1,# 并行數 個數 -1:跟CPU核數一致
cv = 5,#交叉驗證 5折
iid=False,# 默認是True 與各個樣本的分布一致
verbose=2#輸出訓練程序
)
gsearch.fit(Xtrain_05,Ytrain_01)
輸出結果如下:
GridSearchCV(cv=5,
estimator=DecisionTreeClassifier(class_weight='balanced',
random_state=420),
iid=False, n_jobs=-1,
param_grid={'criterion': ('gini', 'entropy'),
'max_depth': range(3, 15),
'splitter': ('best', 'random')},
scoring='roc_auc', verbose=2)
#優化期間觀察到的最高評分
gsearch.best_score_
0.691856415170639
gsearch.best_params_
{‘criterion’: ‘entropy’, ‘max_depth’: 6, ‘splitter’: ‘best’}
3.1.5.3 模型評估
from sklearn.metrics import accuracy_score #準確率
from sklearn.metrics import precision_score #精準率
from sklearn.metrics import recall_score #召回率
from sklearn.metrics import roc_curve
y_pre = gsearch.predict(Xtest_05)
accuracy_score(y_pre,Ytest)
0.6090076335877863
precision_score(y_pre,Ytest)
0.748152359295054
recall_score(y_pre,Ytest)
0.5100116264048572
接下來我們來畫圖看一下ROC曲線:
fpr,tpr,thresholds = roc_curve(y_pre,Ytest) #roc引數
import matplotlib.pyplot as plt
plt.plot(fpr,tpr,c='b',label='roc曲線')
plt.plot(fpr,fpr,c='r',ls='--')

所以綜上來看,我們的模型效果算是不好不壞吧,
3.1.5.4 輸出規則
#最優引數
#{'criterion': 'entropy', 'max_depth': 6, 'splitter': 'best'}
from sklearn.tree import DecisionTreeClassifier
from sklearn import tree
import graphviz
#將最優引數放到分類器
clf = DecisionTreeClassifier(criterion='entropy',max_depth=6,splitter='best')
clf = clf.fit(Xtrain_05,Ytrain)
features = Xtrain_05.columns
dot_data = tree.export_graphviz(clf,
feature_names=features,
class_names=['Not Buy','Buy'],
filled=True,
rounded=True,
leaves_parallel=False)
graph= graphviz.Source(dot_data)
graph
我們來看下圖:

3.1.6 輸出結果的商業應用
我們來看一下購買比例最高的兩類客戶的特征是什么?
結合上圖,我們只要看藍色的部分,可以得到以下結果,
3.1.6.1 第一類
- 處于醫療險覆寫率比例較低區域
- 居住年限小于7年
- 65-72歲群體
那么我們對業務人員進行建議的時候就是,建議他們在醫療險覆寫率比例較低的區域進行宣傳推廣,然后重點關注那些剛到該區域且年齡65歲以上的老人,向這些人群進行保險營銷,成功率應該會更高,
3.1.6.2 第二類
- 處于醫療險覆寫率比例較低區域
- 居住年限大于7年(小于)年
- 居住房屋價值較高
這一類人群,是區域內常住的高端小區的用戶,這些人群也同樣是我們需要重點進行保險營銷的物件,
除此之外,我們還可以做些什么?
3.1.6.3 了解客戶需求
我們需要了解客戶的需求,并根據客戶的需求舉行保險營銷,PIOS資料∶向客戶推薦產品,并利用個人的資料(個人特征)向客戶推薦保險產品,旅行者∶根據他們自己的資料(家庭資料),生活階段資訊推薦的是財務保險、人壽保險、保險、舊保險和用戶教育保險,外部資料、資產保險和人壽保險都提供給高層人士,利用外部資料,我們可以改進保險產品的管理,增加投資的收益,
3.1.6.4 開發新的產品
保險公司還應協助外部渠道開發適合不同商業環境的保險產品,例如新的保險型別,如飛行延誤保險、旅行時間保險和電話盜竊保險,目的是提供其他保險產品,而不是從這些保險中受益,而是尋找潛在的客戶,此外,保險公司將通過資料分析與客戶聯系,了解客戶,外部因素將降低保險的營銷成本,并直接提高投資回報率,
結束語
本次專案真的非常非常的詳細,從這次專案我們也不難發現,資料探索和可視化這一部分是最花時間的,而這一部分也就是為了資料清洗來服務的(當然最終也可以結合可視化得到的結論),后續建模部分反而花的工夫不是很多,因為資料質量的好壞其實是決定最終效果的最大因素,如果我們不先了解清楚資料并進行清洗轉換,后續作業也變得沒有什么意義,
總之,程序雖然繁瑣,但也飽含著樂趣,能從繁瑣的資料中得到更多有用的結論,也顯得那么的有意義,
CSDN@報告,今天也有好好學習
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/300673.html
標籤:AI
上一篇:機器學習演算法學習01:利用Numpy撰寫KNN演算法解決手寫體數字識別問題
下一篇:26.疲勞檢測
