数据分析数分

【案例-数据挖掘】银行营销

2019-08-20  本文已影响6人  X_Ran_0a11

数据来源kaggle(uci数据集): https://www.kaggle.com/janiobachmann/bank-marketing-dataset/kernels

目录:
0 项目概述
一、业务分析
  1.1 基本属性
  1.2 业务联系
  1.3 最近一次营销活动
  1.4 目标数据
二、数据准备
  2.1 描述性数据概览
  2.2 数据清洗和过滤
三、探索性数据分析(EDA)
  3.1 数据项分布
    3.1.1 盈余
    3.1.2 职业
    3.1.3 婚姻状态
    3.1.4 受教育程度
    3.1.5 有无住房贷款和个人贷款
  3.2 是否有定期存款?
四 多重探究
  4.1 营销活动开展的月份
  4.2 潜在客户的年龄
  4.3 目标群体的职业分析
五、影响客户定期存款业务的特征相关性分析
  5.1 矩阵相关性分析
  5.2 住房贷款和个人贷款
六、分类模型
  6.1 模型概述
    6.1.1 模型目标
    6.1.2 建模过程
  6.1.3 决策树
  6.1.4 贝叶斯/费舍尔分类
  6.1.5 神经网络
  6.1.6 SVM
  6.1.7 确定最佳模型
七、营销建议
  7.1 营销目标客户群体
  7.2 营销策略

0 项目概述

  本项目的目的是充分挖掘客户的需求、刻画客户群体肖像,并针对营销活动的开展提供建设性的意见建议,从而真正促进推动银行业务的开展。 为此,我们需要对以下加点进行深入挖掘:
(1)目标人群:哪一部分人群是精准营销的客户群体,针对这一部分人开展营销推广,将使得活动变得高效、快速;
(2)营销渠道:有哪些营销渠道可以采用,例如电话、电视、社交媒体等,如何针对人群设定最佳的渠道策略;
(3)定价:具体的业务应该怎样定价以吸引客户?
(4)营销策略:推动业务落地,从而真正推动业务实效化开展。
  本数据集的营销场景是给客户推荐定期存款业务。

一、业务分析

import pandas as pd
import numpy as np 
import matplotlib.pyplot as plt
import seaborn as sns 
%matplotlib inline
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
bank=pd.read_csv('/Users/ranmo//Desktop/数据分析案例/银行营销/bank.csv')
bank.info()
bank.head()

显示如下:


image.png
image.png

一共11162行*17列数据,具体的数据项可以分为三个部分。

1.1 基本属性
1.2 业务联系
1.3 最近一次营销活动
1.4 目标数据

二、数据准备

2.1 描述性数据概览
image.png

可以得到以下基本信息:

2.2 数据清洗和过滤
image.png

因为没有缺失数据,所以不用进行数据填充。
针对pdays中存在的“-1”,也没有进行清洗。

三、探索性数据分析(EDA)

3.1 数据项分布

是否有定期存款是我们特别关注的数据,不过在此之前,我们可以先分析一下各个数据项的分布以及彼此可能存在的联系。

bank.hist(bins=20,figsize=(14,10))
image.png
3.1.1 盈余

和是否有违约之间的关系:

sns.set(style="darkgrid")
sns.boxplot(x='default',y='balance',hue='deposit',data=bank)
image.png

和职业之间的关系:

sns.boxplot(x='job',y='balance',hue='deposit',data=bank)
plt.xticks(rotation=90)
image.png

和教育程度之间的关系:

sns.violinplot(x='education',y='balance',hue='deposit',data=bank)
image.png

可以得知:

3.1.2 职业

职业的数量分布:

plt.rcParams['figure.figsize']=(10,6)
sns.set()
sns.barplot(x='index',y='job',data=bank['job'].value_counts().to_frame().reset_index())
plt.xticks(rotation=90)
image.png

职业和年龄的关系:

sns.boxplot(x='job',y='age',data=bank)
plt.xticks(rotation=90)
image.png

职业和收入的关系:(发现画图效率有点低,已经开始用tableau混用了)

#tableau创建计算字段balance status:
if [balance]<0
then 'negtive'
elseif [balance]<3000
then 'low'
elseif [balance]<10000
then 'mid'
else 'high'
end
image.png

可以得知:

3.1.3 婚姻状态

婚姻状态和盈余的关系:


image.png

可以得知:

3.1.4 受教育程度

受教育程度和婚姻状态的关系:


image.png

受教育程度和盈余的关系(这里是求的各种教育程度中negtive、low、mid、high的中值):


image.png
可以得知:
3.1.5 有无住房贷款和个人贷款

和盈余的关系:

plt.rcParams['figure.figsize']=(20,10)
plt.subplot(121)
sns.stripplot(x='housing',y='balance',data=bank)
plt.subplot(122)
sns.stripplot(x='loan',y='balance',data=bank)
image.png

可以得知:

3.2 是否有定期存款?

有无定期存款是我们最为关心的问题,也是直接影响预测模型精度的关键参数,首先我们可以进行整体的比例分析:

plt.rcParams['figure.figsize']=(10,6)
f, ax = plt.subplots(1,2)
plt.suptitle('Information on Term Suscriptions', fontsize=20)
bank["deposit"].value_counts().plot.pie(ax=ax[0],autopct='%.2f%%',explode=[0,0.25],startangle=25)
sns.barplot(x='education',y='balance',hue='deposit',data=bank,estimator=lambda x: len(x) / len(bank) * 100)
ax[1].set(ylabel='(%)')
image.png

这里我们似乎可以粗略地得知,受教育程度越高(tertiary),越趋于拥有定期存款,具体的存款业务与各个特征变量之间的关系将在第五章详细分析。

四 多重探究

4.1 营销活动开展的月份

日期函数处理:https://www.cnblogs.com/linkenpark/p/8079337.html

import datetime
# date=bank.pdays
now=datetime.datetime.today()
bank_date=bank
bank_date['compain_date']=bank_date.pdays.transform(lambda x:now-datetime.timedelta(days=x))
bank_date['month']=bank_date['compain_date'].transform(lambda x:x.strftime('%m'))
plt.bar(bank_date['month'].value_counts().index,bank_date['month'].value_counts())
plt.xlabel('month')
image.png
data=bank_date.groupby(['month','poutcome']).count().reset_index()
sns.barplot(x='month',y='age',data=data,hue='poutcome')
image.png

尽管8月份的营销客户数很多,但是营销结果却存在大量未知的数据。去除这部分数据进行分析:

sns.barplot(x='month',y='age',data=data[data['poutcome']!='unknown'],hue='poutcome')
image.png

可以得知:

4.2 潜在客户的年龄
plt.subplot(211)
sns.distplot(bank[bank.deposit=='yes'].age)
plt.ylabel('deposit=yes')
plt.subplot(212)
sns.distplot(bank[bank.deposit=='no'].age)
plt.ylabel('deposit=no')
image.png
data=bank
data['age_status']=data['age']
data.age_status=data.age_status
def agerank(age):
    if age<20:
        age_status='teen'
    elif age>=20 and age<20:
        age_status='young'
    elif age>=30 and age<40:
        age_status='mid'
    elif age>=40 and age<60:
        age_status='mid_old'
    else:age_status='old'
    return age_status
data.age_status=data.age_status.transform(lambda x:agerank(x))
data2=(data.groupby(['age_status','deposit']).age.count()/data.groupby(['age_status']).age.count()).to_frame().reset_index()
sns.barplot(x='age_status',y='age',data=data2,hue='deposit')
image.png
data3=(data[data.poutcome!='unknown'].groupby(['age_status','poutcome']).age.count()/data.groupby(['age_status']).age.count()).to_frame().reset_index()
sns.barplot(x='age_status',y='age',data=data3,hue='poutcome')
image.png

可以得知:

4.3 目标群体的职业分析
data['percent']=1
data4=(data.groupby(['job','deposit']).percent.count()/data.groupby(['job']).percent.count()).to_frame().reset_index()
data5=(data[data.poutcome!='unknown'].groupby(['job','poutcome']).percent.count()/data.groupby(['job']).percent.count()).to_frame().reset_index()

plt.subplot(211)
sns.barplot(x='job',y='percent',data=data4,hue='deposit')
plt.subplot(212)
sns.barplot(x='job',y='percent',data=data5,hue='poutcome')
image.png

可以得出:

五、影响客户定期存款业务的特征相关性分析

根据前文的分析,我们初步知道:
(1)age,小于20及大于60岁更趋于拥有定期存款
(2)job,学生和退休者更趋于拥有定期存款
(3)marital,婚姻状态似乎与业务没有太大联系
(4)education,受教育程度越高(tertiary),越趋于拥有定期存款
(5)default,有无违约似乎与业务没有太大联系
(6)housing,尚未分析
(7)load,尚未分析
(8)balance,盈余状态似乎与业务没有太大联系
(9)contact,无关变量
(10)day,尚未分析
(11)month,尚未分析
(12)duration,尚未分析
(13)compain,尚未分析
(14)pdays,尚未分析
(15)poutcome,尚未分析
未分析的几个特征变量中,有的是数值型变量,有的是字符串变量,数值变量采用矩阵相关性分析,其余的进行特性分析。

5.1 矩阵相关性分析
from sklearn.preprocessing import StandardScaler, OneHotEncoder, LabelEncoder
data5=bank
data5['deposit']=LabelEncoder().fit_transform(data5['deposit'])
#把deposit转化为数值变量
corrmat=data5.corr()
plt.figure(figsize=(15,10))
sns.heatmap(corrmat,annot=True,cmap=sns.diverging_palette(220, 20, as_cmap=True))
image.png

可以看出,这几个值当中与业务最为相关的就是duration通话时间了,进一步分析:


image.png

可以看出,duration的值主要集中在0~600之间,随着duration越大,开设定期存款的比例越高。

data5['duration_status']=(data5.duration-data5.duration.mean())
def dur_status(duration_status):
    if duration_status>=0:
        a='above_average'
    else:a='below_average'
    return a
data5['duration_status']=data5['duration_status'].transform(lambda x:dur_status(x))
percentage=(data5.groupby(['duration_status','deposit']).duration.count()/data5.groupby(['duration_status']).duration.count()).to_frame().reset_index()
percentage['percent']=percentage.duration
sns.barplot(x='duration_status',y='percent',data=percentage,hue='deposit',)

image.png

通话时间的均值为375s,以此作为分解,可以看到通话时间高于均值的,开办业务的比例为77.3%,低于均值的,仅为31.6%。
可以得知:

5.2 住房贷款和个人贷款
data6=bank[['deposit','housing','loan']]
data6['deposit']=LabelEncoder().fit_transform(data5['deposit'])
data6['housing']=LabelEncoder().fit_transform(data6['housing'])
data6['loan']=LabelEncoder().fit_transform(data6['loan'])
corrmat=data6.corr()
plt.figure(figsize=(15,10))
sns.heatmap(corrmat,annot=True,cmap=sns.diverging_palette(220, 20, as_cmap=True))
image.png

可以得知:

六、分类模型

6.1 模型概述
6.1.1 模型目标

构建一个分类模型,能够预测是否开通定期存款业务,比较适合的算法有:
(1)决策树:需要将连续数据处理为区间数据,能给出分类结果,如果构建的是完全拟合的决策树则不存在置信度问题,非完全决策树则可以给出分类置信度;
(2)贝叶斯/费舍尔分类:根据相似度进行分类,能给出分类结果和分类置信度;
(3)神经网络:将输出层设置为2个神经元,分别为属于两个分类的概率,总和为1,因此能给出分类结果和分类置信度;
(参见https://www.jianshu.com/p/d4fb3a391d22?from=timeline&isappinstalled=0
(4)SVM:能输出分类结果肯定的,能给出置信度么?我个人认为如果SVM过拟合比较严重,则给出的置信度为只有1,0之分,如果调整参数之后,会逐渐放宽分类的限制,相当于先分类成功的肯定置信度相对更高,后分类成功的相对置信度更低)
(别人的包:https://www.jianshu.com/p/f0c9b10a10af

ps,为什么想知道置信度,是因为除了做分类预测外,还想要根据置信度排序做出精准营销的推荐,置信度比较低的,就不成为我的营销目标。

6.1.2 建模过程
bank=pd.read_csv('/Users/ranmo//Desktop/数据分析案例/银行营销/bank.csv')
bank_spss=bank

bank_spss['job']=LabelEncoder().fit_transform(bank_spss['job'])
bank_spss['marital']=LabelEncoder().fit_transform(bank_spss['marital'])
bank_spss['education']=LabelEncoder().fit_transform(bank_spss['education'])
bank_spss['default']=LabelEncoder().fit_transform(bank_spss['default'])
bank_spss['housing']=LabelEncoder().fit_transform(bank_spss['housing'])
bank_spss['loan']=LabelEncoder().fit_transform(bank_spss['loan'])
bank_spss['contact']=LabelEncoder().fit_transform(bank_spss['contact'])
bank_spss['month']=LabelEncoder().fit_transform(bank_spss['month'])
bank_spss['poutcome']=LabelEncoder().fit_transform(bank_spss['poutcome'])
bank_spss['deposit']=LabelEncoder().fit_transform(bank_spss['deposit'])

bank_spss.to_csv(path_or_buf='/Users/ranmo//Desktop/数据分析案例/银行营销/bank_spss.csv')
image.png

6.2 决策树

这表明:duration、contact、month、poutcome、pdays、days都是与上一次营销活动以及近期联系紧密相关的参数,这一点上很好解释,即联系越频繁,表明其本身就是我们的优质客户和目标营销群体

ps:
spss中画ROC曲线。。接受的输入各个样本的类别以及阈值,然后根据不断调整阈值,来求得ROC的X和Y:
http://www.sohu.com/a/144925905_165070
但绝大多数情况下,是没办法得到各个样本的阈值的,所以可能还是要在python中自己编程或者调包。

6.3 贝叶斯/费舍尔分类

6.4 神经网络

PS,如果输出变量是连续值的话,SPSS会认为是做预测,从而只输出一个值,就是预测的值。

最后是采用BP跑了一下。

6.5 SVM

SPSS statistics没有SVM,只有SPSS modeler才有。。umm
决定用python掉包跑一下。
https://www.cnblogs.com/luyaoblog/p/6775342.html

from sklearn import svm
from sklearn import model_selection
x,y=np.split(bank_spss,(16,),axis=1)
x_train, x_test, y_train, y_test = model_selection.train_test_split(x, y,train_size=0.7)
clf = svm.SVC(C=0.8, kernel='rbf', gamma='auto_deprecated', decision_function_shape='ovr')
clf.fit(x_train, y_train)
print("SVM-输出训练集的准确率为:",clf.score(x_train,y_train))
print("SVM-输出测试集的准确率为:",clf.score(x_test,y_test))

显示:


image.png

因为训练集的准确率为1,所以最开始是猜测过拟合了,导致测试集准确率这么低。。结果我调整模型的C和gamma,得出的测试集的准确率都是一样的,然后我一查看结果,发现所有的输入都预测为0。。。
因为具体不知道是哪儿出了问题,就把所有变量做了归一化处理:

#做归一化
bank_spss_new=bank_spss
for i in bank_spss_new.columns:
    bank_spss_new[i]=bank_spss_new[i]/(bank_spss_new[i].max()-bank_spss_new[i].min())


x,y=np.split(bank_spss_new,(16,),axis=1)
x_train, x_test, y_train, y_test = model_selection.train_test_split(x, y,train_size=0.7)
clf = svm.SVC(C=0.8, kernel='rbf', gamma='auto_deprecated', decision_function_shape='ovr')
clf.fit(x_train, y_train)
print("SVM-输出训练集的准确率为:",clf.score(x_train,y_train))
print("SVM-输出测试集的准确率为:",clf.score(x_test,y_test))
image.png

现在正常了。。

from sklearn.metrics import classification_report
y_pred=clf.predict(x_test)
print(classification_report(y_test, y_pred, labels=None, target_names=None, sample_weight=None, digits=2))
image.png

(2)ROC曲线
SVM画ROC可能没有什么意义,如果要画的话,那就是通过调整C和gamma的值来取一个适合的模型精度。。

6.6 确定最佳模型

从模型的整体预测精度来说
决策树:0.807(测试集)
费舍尔判别:0.793(全部集)
BP神经网络:0.700(测试集)
svm:0.777(测试集)
决策树胜出!

七、营销建议

7.1 营销目标客户群体
7.2 营销策略
上一篇下一篇

猜你喜欢

热点阅读