實驗2:數據探索與分析
實驗目標:對葡萄牙銀行數據集的特征之間的關聯關系進行分析和探索,對於現有營銷方案給出建議。
完成時間:1.5小時(實驗),0.5小時(實驗報告)
實驗要求:
- 分析不同因素取值對營銷結果的影響。
- 相關度矩陣以熱力圖方式觀察不同變量對於營銷結果的影響高低
- 通過觀察兩個相關變量與營銷結果的關系,得到幫助營銷成功的更多信息
變量介紹
銀行客戶信息:
- 1 - age: 年齡 (數字)
- 2 - job: 工作類型 。管理員(admin),藍領(blue-collar),企業家(entrepreneur),家庭主婦(housemaid),管理者('management'),退休('retired'),個體經營('self-employed'),服務業('services'),學生('student'),技術人員('technician'),無業('unemployed'),未知('unknown')
- 3 - marital : 婚姻狀態,離婚('divorced'),結婚('married'),單身('single'),未知('unknown')。說明:離婚也包括寡居
- 4 - education: 教育情況 : 基本4年('basic.4y'), 基本6年('basic.6y'),基本九年('basic.9y'),高中('high.school'),文盲('illiterate'),專業課程('professional.course'),大學學位('university.degree'),未知('unknown')
- 5 - default: 是否有信用違約? ('no','yes','unknown')
- 6 - housing: 是否有房貸 ( 'no','yes','unknown')
- 7 - loan: 是否有個人貸款 (categorical: 'no','yes','unknown')
與聯絡相關信息:
- 8 - contact: 聯系類型,手機( 'cellular'),電話:'telephone'
- 9 - month: 年度最后一次聯系的月份 (categorical: 'jan', 'feb', 'mar', ..., 'nov', 'dec')
- 10 - day_of_week: 最后一次聯系的星期 (categorical: 'mon','tue','wed','thu','fri')
- 11 - duration: 上一次聯系的通話時長(秒). 重要提示:此屬性高度影響輸出目標(例如,如果持續時間=0,則y='no')。然而,在執行呼叫之前,持續時間還不知道。而且,在通話結束后,Y顯然是已知的。因此,這個輸入應該只包括在基准測試中,如果想要有一個實際的預測模型,就應該丟棄它。(預測時不知道會通話的時長)
其他屬性:
- 12 - campaign: 針對該客戶,為了此次營銷所發起聯系的數量。(數字,包括最后一次聯絡)
- 13 - pdays: 上次營銷到現在已經過了多少天。(數字,如果是999表示這個客戶還沒有聯系過)
- 14 - previous: 在本次營銷之前和客戶聯系過幾次(數字)
- 15 - poutcome: 上一次營銷活動的結果 ( 'failure','nonexistent','success')
社會和經濟相關屬性
- 16 - emp.var.rate: 就業變動率 -系度指標(numeric)
- 17 - cons.price.idx: 消費物價指數-月度指標 (numeric)
- 18 - cons.conf.idx: 消費者信心指數--月度指標(numeric)
- 19 - euribor3m: 歐元同業拆借利率3個月 - 每日指標 (numeric)
- 20 - nr.employed: 員工數量-季度指標 (numeric)
輸出變量(目標):
- 21 - y -客戶存錢了嗎(被成功營銷了嗎)? (binary: 'yes','no')
導入庫
import numpy as np import pandas as pd import warnings warnings.filterwarnings("ignore") import seaborn as sns import matplotlib.pyplot as plt %matplotlib inline
1. 數據裝載
- 裝載數據到dataframe
- 設置支持中文和指定中文字體(參考demo)
- 設置numberVar和categoryVar的值
df=pd.read_csv("bank-additional-full.csv",sep=";") plt.rcParams['font.sans-serif'] = ['KaiTi']#作圖指的定默認字體中文 plt.rcParams['font.serif'] = ['KaiTi']#作圖的中文 plt.rcParams['axes.unicode_minus'] = False # 解決保存圖像是負號'-'顯示為方塊的問題 df.head()
age | job | marital | education | default | housing | loan | contact | month | day_of_week | ... | campaign | pdays | previous | poutcome | emp.var.rate | cons.price.idx | cons.conf.idx | euribor3m | nr.employed | y | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 56 | housemaid | married | basic.4y | no | no | no | telephone | may | mon | ... | 1 | 999 | 0 | nonexistent | 1.1 | 93.994 | -36.4 | 4.857 | 5191.0 | no |
1 | 57 | services | married | high.school | unknown | no | no | telephone | may | mon | ... | 1 | 999 | 0 | nonexistent | 1.1 | 93.994 | -36.4 | 4.857 | 5191.0 | no |
2 | 37 | services | married | high.school | no | yes | no | telephone | may | mon | ... | 1 | 999 | 0 | nonexistent | 1.1 | 93.994 | -36.4 | 4.857 | 5191.0 | no |
3 | 40 | admin. | married | basic.6y | no | no | no | telephone | may | mon | ... | 1 | 999 | 0 | nonexistent | 1.1 | 93.994 | -36.4 | 4.857 | 5191.0 | no |
4 | 56 | services | married | high.school | no | no | yes | telephone | may | mon | ... | 1 | 999 | 0 | nonexistent | 1.1 | 93.994 | -36.4 | 4.857 | 5191.0 | no |
定義數值型列表和類別變量列表:
numberVar=['age','duration','campaign','pdays','previous','emp.var.rate','cons.price.idx','cons.conf.idx','euribor3m','nr.employed'] categoryVar=['job','marital','education','default','housing','loan','contact','month','day_of_week','poutcome','y']
2. 變量的不同取值對於目標的影響
將營銷成功和失敗分為兩個集合,分別統計每個集合中每一類人群的數量。由於營銷成功和失敗之間的樣本不平衡,直接使用絕對值進行比較是不容易發現有用的信息的。將營銷成功和失敗兩個集合分別求它們內部不同取值的占比,結果就相對明顯了。將兩個系列的百分比相減:
2.1 不同婚姻狀況對營銷結果的影響
1.將營銷成功(y=='yes')的樣本放入dfy中,失敗樣本放入dfn中:
dfy = df[df.y =='yes'] dfn = df[df.y == 'no']
2.分別計算出營銷成功和失敗的樣本總量
posCount=dfy.shape[0] negCount=dfn.shape[0] print(posCount) print(negCount)
3.將marital所有可能的取值,放入一個列表
vlist=df.marital.unique()
vlist
4.求出正負例(營銷成功/失敗)樣本中,婚姻狀況的取值分布(轉字典類型)
yCounts = dfy['marital'].value_counts().to_dict() nCounts = dfn['marital'].value_counts().to_dict() print(yCounts) print(nCounts)
5.計算每一種婚姻狀態下,營銷成功和失敗的百分比差值。成功百分百-失敗百分百(編碼參考demo)
這里使用'married',看看已婚和未婚群體接受營銷的百分比差值:
#使用yCount['married']不合適,當數據不存在時會報告錯誤。更合適的做法是當數據不存在時返回0 count = yCounts.get('married')/posCount - nCounts.get('married')/negCount count
CountLst=[yCounts.get(i)/posCount-nCounts.get(i)/negCount for i in vlist] print(CountLst)
6.可視化百分比
sns.barplot(CountLst,vlist) plt.title('婚姻狀態對營銷的態度')
2.2 所有分類變量的不同取值對營銷結果的影響
將前面“婚姻”狀況對營銷結果影響的分析,推廣到所有變量。使用for循環,遍歷所有的分類變量。參考前文代碼,原本使用'marital',現在使用col來表達。
for col in categoryVar: plt.figure(figsize=(8,4)) yCounts =dfy[col].value_counts().to_dict() nCounts =dfn[col].value_counts().to_dict() vlist =df[col].unique() countLst=[yCounts.get(i,0)/posCount-nCounts.get(i,0)/negCount for i in vlist] sns.barplot(countLst, vlist) plt.title(col) plt.tight_layout()
2.3 針對可視化結果,給出傾向於購買儲蓄產品的人群特征
這里以教育這個特征的分析為例:從圖中可以看出具備大學學歷的客戶更傾向於接受營銷進行定期存款。
3. 觀察兩兩變量之間的相關度
分析變量之間可能的關系,通過可視化方式去進一步挖掘其中的有用的信息
3.1 觀察數值變量相關性¶
- 通過映射改變Dataframe列的取值
以下語句將Dataframe的gender變量,當前取值為0,1,2,通過下面的語句可以將0,1,2分別映射為”girl”,”boy”,”unknown”
df['gender'] = df["gender"].map({0:"girl", 1:"boy",2:"unknown"})
為了實現對相關系數的計算(相關度矩陣的計算要求變量必須為數字),許多以字符串形式表達的分類變量需要轉為數值變量才可以進一步計算。對於大部分的分類變量,可以通過映射的方式實現直接的轉換。通過map方法,可以將DataFrame中的值實現映射。
這里,我們希望了解各個數值變量與目標(變量y)的相關度,因此需要先將y轉為數值類型(即,將’yes’映射成1,‘no’映射為0)
df['y'] = df["y"].map({"no":0, "yes":1})
這里定義了heatmap函數,dataset表示含有數據集的dataframe, col則是需要進行相關度分析的列表。本實驗要求所有的數值型變量另外再加一個y變量。例如['age','duration',....,'y']
# 使用熱力圖可視化數據集多個變量之間的相關度 def heatmap(dataset, col): corr_data = dataset[col] corr = corr_data.corr() #計算相關度矩陣 cor_plot = sns.heatmap(corr,annot=True,cmap='RdYlGn',linewidths=0.2) plt.xticks(fontsize=12,rotation=-30) #x軸的字體和旋轉角度 plt.yticks(fontsize=12) #y軸的字體和旋轉角度 plt.title('相關度矩陣') # 標題 plt.show()
使用相關度矩陣和熱力圖,觀察數值變量('age','duration','campaign','pdays','previous','emp.var.rate','cons.price.idx','cons.conf.idx','euribor3m','nr.employed','y')的相關度。
注:注意需要將圖形調整到合適大小。 例如: plt.figure(figsize=(11, 8))
plt.figure(figsize=(11, 8)) heatmap(df, ['age','duration','campaign','pdays','previous','emp.var.rate','cons.price.idx','cons.conf.idx','euribor3m','nr.employed','y'] )
3.2 將分類變量轉為數值變量
分類變量分為有序和無序兩種。本例中一些分類變量並無順序,這里先按照人為理解將其解釋為有序變量(但是在后續機器學習中,必須按照實際情況進行分析)。通過熱力圖大致了解其關聯情況。分類變量進行映射:在映射中,新增加一列_num結尾的變量,用於保存映射的結果
# 對default_num的值進行轉換 df['default_num'] = df['default'].map({'yes': 0,'unknown': 0,'no': 1}) #再次查看轉換后的值: df.default_num.value_counts()
# 每個不同的變量取什么值,由自己決定,只要是不重復的連續整數變量即可 df['education_num'] = df['education'].map({'illiterate': 0,'basic.4y': 1,'basic.6y': 2,'basic.9y':3,'high.school':4, 'professional.course':5,'unknown':6,'university.degree':7}) df['month_num'] = df['month'].map({'jan': 1,'feb': 2,'mar': 3,'apr':4,'may':5, 'jun':6,'jul':7,'aug':8,'sep':9,'oct':10,'nov':11,'dec':12}) df['loan_num'] = df['loan'].map({'no': 0,'unknown': 1,'yes': 2}) # 請補充:如下的變量 df['marital_num'] = df['marital'].map({'married':0,'single':1, 'divorced':2, 'unknown':3}) df['housing_num'] = df['housing'].map({'no':0, 'yes':1, 'unknown':2}) df['contact_num'] =df['contact'].map({'telephone':0, 'cellular':1}) df['day_of_week_num'] = df['day_of_week'].map({'mon':0, 'tue':1, 'wed':2, 'thu':3, 'fri':4}) df['poutcome_num'] =df['poutcome'] .map({'nonexistent':0, 'failure':1, 'success':2}) catCols = ['default_num','loan_num','marital_num','housing_num','day_of_week_num','education_num','month_num','poutcome_num','y'] df[catCols].head()
3.3 通過熱力圖觀察分類變量的相關性
調用heatmap函數,使用相關度矩陣和熱力圖,觀察變量('default_num','loan_num','marital_num','housing_num','day_of_week_num','education_num','month_num','poutcome_num','y')的相關度,變量已經保存在列表catCols中。
plt.figure(figsize=(11, 8)) heatmap(df,['default_num','loan_num','marital_num','housing_num','day_of_week_num','education_num','month_num','poutcome_num','y'])
3.4 所有特征的相關性¶
展示所有變量之間的相關度,觀察變量y與其他變量之間的相關度,找出與目標具有相對高相關度的變量。
plt.figure(figsize=(15, 15)) #設置較大的圖形尺寸 heatmap(df,numberVar+catCols) #將dataframe的所有數值變量列都加入顯示
4. 結合目標觀察變量間的關系
4.1 上次通話時長、聯絡次數和營銷結果的關系
4.1.1 觀察通話時長和聯絡次數的分布情況(直方圖)
df['duration']=df['duration']/60 df[['duration','campaign']].describe()
觀察通話時長和聯絡次數的分布情況:
p= plt.figure(figsize = (14,4)) r1 = p.add_subplot(1,2,1) r2 = p.add_subplot(1,2,2) r1.set_title('通話時長分布') r1.hist(df['duration'],50, edgecolor='black') r2.set_title('通話時長與聯絡次數的關系') #為圖片設置合適的標題 r2.hist(df['campaign'],20,edgecolor='blue') #直方圖2,方法與r1直方圖一樣,參考r1.hist,參數為campaign plt.tight_layout() plt.show()
圖像分析:
- 通話時長一般是多久?
- 客服與用戶一般聯絡多少次?
從上述信息可以看出,客服與用戶的聯系是怎樣一種情況?
4.2.2 通話時長和聯系次數的可視化(散點圖)
觀察可視化結果,回答以下問題:
- 聯系次數會增加營銷的成功率嗎?
- 絕大部分成功的營銷,聯系次數大約在一個什么樣的范圍內?
# hue依據y變量的值對點進行不同着色 dur_cam = sns.lmplot(x='duration', y='campaign',data = df, hue = 'y',fit_reg = False,markers=["o", "x"]) plt.axis([0,60,0,50]) plt.ylabel('聯絡次數') plt.xlabel('通話時長(分鍾)') plt.title('通話時長和聯系次數的關系') plt.show()
對於最終營銷成功的案例來看,大部分的聯系次數都在5次以下,而通話時長則從1~30分鍾都有分布。從數據上看,通話次數的增加並不會對營銷結果有什么幫助,更多的聯絡次數對應着更低的營銷效果。
4.2 可視化年齡、聯系次數和營銷結果之間的關系
4.2.2 可視化年齡、聯系次數和營銷成功率之間的關系
仿照上一個單元,可視化年齡、聯系次數和營銷成功率之間的關系。注意:將上例中的通話時長duration修改為年齡(age)即可。標題和xy軸提示文本需要做相應修改。
# hue依據y變量的值對點進行不同着色 dur_cam = sns.lmplot(x='age', y='campaign',data = df,hue = 'y',fit_reg= False,markers=['o', 'x']) plt.axis([0,60,0,50]) plt.ylabel('聯絡次數') plt.xlabel('年齡') plt.title('年齡和聯系次數的關系') plt.show()
進一步的人群進行細分,看看不同年齡的人的營銷情況。下面先將年齡分組:
def get_age_group(age): if age < 30: return 2 elif age >60: return 6 else: return age//10 df['age_group']=df['age'].apply(lambda x: get_age_group(x)) df.age_group.value_counts()
計算每個年齡段的聯絡次數以及營銷成功的案例數,這使用.groupby依據age_group對'campaign‘和'y'進行分組
ageGroupDF = df[['campaign','y']].groupby(df['age_group']) age_camp = ageGroupDF.sum() age_camp
為了更加方便進行比較,將每個年齡段的聯系總次數,轉為各個年齡段的聯系百分比。對y的處理也是同樣
age_camp_pct = age_camp.apply(lambda x: x/x.sum() * 100) age_camp_pct
4.2.3 可視化年齡段、聯系次數和營銷成功率之間的關系
將不同年齡段的聯系率和營銷率可視化
plot_age = age_camp_pct.plot(kind = 'bar',figsize=(8,6)) plt.xlabel('年齡段') plt.ylabel('百分百') plt.xticks(np.arange(5),('<30','30-39','40-49','50-59','>60')) plt.title plt.show()
4.3 可視化客戶特征(工作、聯系月份等)、營銷行為(聯系次數)和營銷結果之間的關系
將上述的分析方式打包稱為函數relationShow。relationShow(數據集, 營銷行為, 營銷結果,客戶特征),依次快速可視化一批數據,發現針對不同特征的用戶應該是否針對現有的營銷行為做調整。
def relationShow(dataset, action, target, character): pct = dataset[[action,target]].groupby(dataset[character]).sum().apply(lambda x: x/x.sum() * 100) plot = pct.plot(kind = 'bar',figsize=(12,6)) plt.xlabel(character) plt.ylabel('百分比') plt.xticks(np.arange(len(pct.index)),pct.index) plt.title(action+"、"+character + "、" + "y") plt.show()
以job為例:從下圖分析可知,后續在營銷時,應該適當減少對藍領的資源投入,增加對管理員、退休人員以及學生的資源投入。
relationShow(df, 'campaign','y','marital')
其他
可以運行供參考如何進行數據分析,不必寫入實驗報告
f = pd.crosstab(df['job'],df['education']) plt.figure(figsize=(8, 6)) sns.heatmap(f, annot=True,cmap='RdYlGn',fmt="d",linewidths=0.2)
觀察客戶群體中,學歷與工作的關系,幫助我們了解當時的葡萄牙社會。
-
管理者--大學和高中學歷
-
藍領學歷以高中以下為主;
-
經理:大學學歷
-
服務行業--高中
-
技術人員-- 專業學校
-
失業--各種學歷都有,大學最多。
-
對於大學學歷者:最可能的是管理者,然后是經理,再次是技術人員
-
專業學校:大部分進入了技術人員的行列
-
高中學歷:管理者、服務業
-
初中和以下學歷:大部分成為藍領