精华内容
下载资源
问答
  • 银行客户流失分析

    千次阅读 多人点赞 2019-07-22 09:12:51
    随着互联网金融的异军突起,银行业的竞争愈加激烈,防止客户流失和挽留老客户成为各大银行关心的重要问题。本文首先根据已有数据集对各特征进行描述性统计分析,初步了解数据;之后进行数据预处理,包括数据清洗,...

    摘要

                        摘要
      随着互联网金融的异军突起,银行业的竞争愈加激烈,防止客户流失和挽留老客户成为各大银行关心的重要问题。本文首先根据已有数据集对各特征进行描述性统计分析,初步了解数据;之后进行数据预处理,包括数据清洗,数据变换、特征选择;再后用SVM、LR、朴素贝叶斯、决策树、RF、XGBoost、LM神经网络进行建模,通过不同性能度量,选出XGBoost为表现最好的模型并进行调参;最后,根据描述性统计和特征重要性为银行挽留客户提出建议。
    关键词:客户流失;数据预处理;XGBoost;调参;建议

    研究背景

      银行客户流失是指银行的客户不再继续参与原业务、不再重复购买或者终止原先的产品或者服务。近年来,随着互联网金融的异军突起和传统银行业的竞争加剧,银行发展自身潜力、吸引优质顾客、防止客户流失就显得格外重要。研究表明,发展一位新客户所花费的成本要比维持一位老客户的成本多达5到6倍。所以说,在客户流失后,如果企业要去重新发展新客户所需要的成本是巨大的,且大多数新用户产生的利润不如老用户。因此,不管是哪个行业都越来越重视客户流失管理。预测潜在的流失客户、有效挽留和关怀客户是各个企业关心的重要问题之一。

    研究目标

    1、通过确定客户流失模型,有效预测客户的流失情况。
    2、通过预测模型的建立,提出相应建议提高用户的活跃度,实现挽留关怀客户的有效性,降低开展挽留关怀工作的成本。

    分析流程

    客户流失数据
    数据探索与预处理
    数据清洗
    数据变换
    特征选择
    建模
    训练样本
    测试样本
    模型评价

    数据探索与预处理

      本节首先观察样本数据,初步了解属性特征,对各属性进行描述性统计分析,初步探索各属性与客户流失情况的关系;其次进行数据预处理工作,包括异常值处理,数据变换,数据规约等;之后将样本划分为训练集和测试集,观察客户流失情况样本是否均衡,不均衡时需要对训练样本进行均衡化处理;最后,对训练集和测试集进行标准化处理。

    数据探索

      本次所使用的数据集是某欧洲银行的数据。数据集一共包含了14个变量,10000个样本,不包含缺失值。本数据集可从superdatascience官网下载。首先先来观察数据情况。图1—数据集变量情况              图1 数据集变量预览
      在图1展示的数据集中,从左到右,数据集所具有的的变量有编号、用户ID、姓名、信用分、地区、性别、年龄、用户时长(使用银行产品时长)、存贷款情况、使用产品数量、是否有信用卡、是否为活跃用户、估计收入、是否已流失。将数据集特征进行汇总,由表1可以清楚地看出特征变量名称和属性。
                  表1 数据集特征汇总
    在这里插入图片描述
      我们从表1可以看到,数据集一共包含14个变量,其中第14个变量就是我们的目标变量,目标变量定义的是是否已经流失的分类变量,从而确定了本次数据挖掘的目标是分类。除了目标变量外,特征属性中有7个分类变量,其中有5个是字符类型的,这意味着在数据预处理时首先要处理这些变量才能做下一步分析。其余6个是数值变量,我们接下来要注意考虑数值变量是否存在异常值,是否需要进行数据变换。
      下面对各变量首先进行描述性统计分析,观察变量分布情况,进一步了解数据。

    1、定量变量初探

      本小节主要是通过绘制定量变量的频数分布图来观察变量的分布情况,初步了解各变量分布。

    图2-1 CreditScore-信用得分频数分布
    图2-2 Age-年龄频数分布
    图2-3 Tenure-客户使用年数分布
    图2-4 Balance-客户存贷款情况
    图2-5 NumOfProducts-拥有产品数量
    图2-6 EstimateSalary-估计收入
                图2 定量变量频数分布图

      总体来看图2-1,对于信用分这个属性来说,流失和非流失总体呈现出一个偏正态分布,信用分在650-700之间达到峰值。由图2-2可以看出,年龄呈现出一个偏左态分布。该银行35到40岁的客户最多,在60岁以上的客户很少。且给银行的目标用户大部分年龄在25到45岁之间,比较符合多数银行客户年龄的分布情况。对于该银行,图2-3用户使用年数在1到9年的分布均匀,使用10年的人数最多,使用1年的人数较少。我们可以初步猜测该银行忠诚客户较多,但是在发展新客户方面不够重视或者策略需要调整。;对于客户存贷款情况,该样本数据显现的都是存贷款大于0的情况。从图中可以看出,存款在25000以下的占据大部分。在存款大于25000呈现正态分布。在持有产品数方面,该银行大多数用户使用银行的产品数量为1个或2个,拥有3个或4个产品的极少。说明该银行的产品只有少数具有吸引力,银行应该实施相应措施激励用户使用该银行产品。从图2-6可以看出,该银行客户估计收入在0到200000之间,且不同收入客户分布均匀,猜测该银行并没有清晰的目标客户定位,对不同收入层的客户效用没有差别。

    2、分类指标分布情况

      对于分类指标,这里展示了不同指标分类的频数分布情况,同时展示了在每种类别下客户流失情况。

    图3-1 地区分布和流失情况
    图3-2 性别分布和流失情况
    图3-3 信用卡分布和流失情况
    图3-4 活跃用户分布和流失情况
                图3 定类变量频数分布图

      图3-1中,在地区这个属性可以看出,样本数据集客户来自三个国家:法国、德国、西班牙。该银行法国用户最多,德国和西班牙客户数量几乎相等,但从流失情况来看,德国用户的流失率明显高过其他两国。从性别来看,图3-2显示,银行的男性用户高于女性,但是女性的流失率要高过男性。在客户是否拥有信用卡来看(图3-3),该银行70%的客户有信用卡,没有信用卡客户的流失率在20%以上,有信用卡的流失率在16%。使用该银行信用卡的用户忠诚度更高。由图3-4知,该银行的活跃客户稍高于非活跃用户,活跃用户的流失率明显低于非活跃用户,这也说明活跃客户较非活跃客户忠诚度更高。

    3、目标变量分布

    在这里插入图片描述              图4 客户流失情况
      从图4我们可以很明显看到,客户流失情况样本分布很不均衡,未流失与流失用户比例接近4:1。所以接下来在划分训练集和测试集之后,需要对不均衡的测试集样本进行均衡处理。

    数据预处理

    数据清洗

      因为样本不含缺失值,所以不用进行缺失值的处理。数据清洗在这里主要包括两步:删除无关变量和异常值处理。

    1、删除不相关变量

      我们将导入jupter notebook的数据集输出前5个数据观察变量,如图5。
    在这里插入图片描述              图5 数据集展示
      在所有变量中,RowNumber:用户编号,CustomerId:用户ID,Surname:用户姓名这三个变量明显是无关变量,我们将无关变量剔除。

    2、异常值处理

      首先根据变量的箱线图来判断是否存在异常值。

    图6-1 信用分
    图6-2年龄
    图6-3 使用年数
    图6-4 存贷款
    图6-5 产品数量
    图6-6 估计收入
                   图6 特征箱线图

      由图6可以看出,信用分、年龄、产品数量存在异常值。信用分低于400分是异常值,年龄大于60的属于异常值,产品数为4的也属异常值。处理异常值的方法有很多,其中忽略异常值也是一种方法。由于认为信用分、年龄、产品数量属于重要变量,这里选择不剔除异常值。

    数据变换

      对于该数据集的数据变换处理,主要包括对字符型变量的量化、对分类变量的处理、连续属性离散化、样本不均衡处理和数据标准化。

    1、字符型变量的量化

      由之前样本数据的观察了解到,地区、性别是字符型变量,无法进行分析,需要将这2个属性进行量化。这里使用转换数值工具包LabelEncoder进行转换。处理后,地区包含0、1、2三个值,性别包括0、1两个值。其中地区变量0代表France,1代表Germany,2代表Spain;性别变量0代表女,1代表男。图7展示了变化后的效果。
    在这里插入图片描述            图7 量化后数据集

    2、连续特征离散化。

      将连续特征离散化对异常数据有很强的鲁棒性,能够增强模型的稳定性,降低了模型过拟合的风险,并且能够提高迭代速度。在之前的异常值检测中我们已知CreditScore和Age两个变量存在异常值,这里将他们进行离散化,将信用得分划分为600分以下、600-650、650-700、700-750、750以上共5个分组,年龄划分为30岁以下、30-40、40-50、50-60、60-70、70-80、80岁以上7个分组。
      下面给出分组后的信用分和年龄分布。

    图8-1 信用分
    图8-2 信用分
                 图8 信用分、年龄属性离散化后分布

      在进行完这些处理后 ,接下来的工作就是对分类变量进行独热编码以及特征矩阵的标准化。但是,考虑到类标签流失与未流失的比例接近1:4,属于不均衡数据。样本不均衡会给结果带来很大影响。在样本不均衡情况下,即使我们最终得到的模型准确度很高也是不可信的。所以,在进行分类任务时,解决样本不均衡问题很重要。解决样本不均衡的方法有很多,目前用的较多的有欠采样、过采样、加权、One-class分类。这里选择过采样的改进方法SMOTE算法来平衡样本。

    3、样本不均衡处理

      在对样本处理前,首先简单介绍样本不均衡的处理方法SMOTE算法。
      SMOTE(Synthetic Minority Oversampling Technique),合成少数类过采样技术.它是基于随机过采样算法的一种改进方案,由于随机过采样采取简单复制样本的策略来增加少数类样本,这样容易产生模型过拟合的问题,即使得模型学习到的信息过于特别(Specific)而不够泛化(General),SMOTE算法的基本思想是对少数类样本进行分析并根据少数类样本人工合成新样本添加到数据集中,具体如下图所示,算法流程如下。
      (1)对于少数类中每一个样本x,以欧氏距离为标准计算它到少数类样本集中所有样本的距离,得到其k近邻。
      (2)根据样本不平衡比例设置一个采样比例以确定采样倍率N,对于每一个少数类样本x,从其k近邻中随机选择若干个样本,假设选择的近邻为xn。
      (3)对于每一个随机选出的近邻xn,分别与原样本按照如下的公式构建新的样本。
    xnew=x+rand(0,1)(x^x) x_{new}=x+rand(0,1)(\widehat{x}-x)
    在这里插入图片描述                图9 SMOTE算法示意图
      介绍完SMOTE原理后,我们对数据集进行处理。首先将数据集划分为训练集和样本集,这里将原始数据的80%作为训练集,20%作为样本集然后需要对训练集样本不均衡进行处理。这里随机产生的训练集正负样本比例接近1:5。我们通过SMOTE算法将正样本也就是流失样本扩展到和未流失样本一样多。我们可以看一下SMOTE处理后的效果。
    在这里插入图片描述              图10 均衡处理后训练样本数量
      图10中我们可以看出,SMOTE已经将训练集扩展成了正负样本一致。由于SMOTE算法是基于样本到k近邻之间的距离随机生成的样本,所以过采样后流失样本的离散变量会连续化。Geography、Gender、HasCrCard、IsActiveMember这四个变量是离散变量且为分类变量,但是Smote处理后将他们连续化了,因此需要进行离散化处理。这里划分数据离散化的标准就近原则,即样本数据离哪个指标近就划分到那一类中。

    4、虚拟变量变换

      处理完样本不均衡问题后,我们考虑到Geography变量是三分类变量,直接编码(0,1,2)会让计算机直接将变量作为有序数组处理。所以,真正分析的时候要把他们作为虚拟变量处理,性别就是二分类引入一个虚拟变量,(即我们知道一个是0,另一个就是1)可以不变,地区是三分类,要引入两个虚拟变量,即要知道其中两个的类,才能确定另一个,比如France和Spain是0,我们才能确定Germany是1。这里可以直接用sklearn里的OneHotEncoder进行处理。OneHotEncoder是将分类变量的类别生成3列。这里当France为真时(1,0,0),Germany为真时(0,1,0),Spain为真时(0,0,1)。同时,为了避免掉入“虚拟变量陷阱”,我们删除其中一列,这里选择剔除Spain这一列。由于OneHotEncoder工具包会将独热编码后的数据放在最前面,因此此时变量顺序已经发生改变。展示效果如图11。
    在这里插入图片描述            图11 虚拟变量变换后的数据集

    5、特征标准化

      数据的标准化处理,可以在保持列内数据多样性的同时,尽量减少不同类别之间差异的影响,可以让机器公平对待全部特征。同样用sklearn里StandardScaler包处理。这里训练集合测试集要进行相同的处理。

    特征选择

      在对单变量分析完之后,我们来观察下特征变量之间的相关性,当特征之间出现强相关性时,我们应该考虑剔除变量强相关性,下面给出了特征变量之间的相关系数矩阵。由于离散变量较多,因此这里采用相关系数矩阵展示。
                表2 特征变量相关关系表

    Geography Gender Tenure Balance NumOfProducts HasCrCard IsActiveMember EstimatedSalary CreditScore_range age_group
    1.000 0.005 0.004 0.069 0.004 -0.009 0.007 -0.001 -0.000 0.018
    1.000 0.015 0.012 -0.022 0.006 0.023 -0.008 -0.006 -0.022
    1.000 -0.012 0.013 0.023 -0.028 0.008 0.004 -0.010
    1.000 -0.304 -0.015 -0.010 0.013 0.010 0.027
    1.000 0.003 0.010 0.014 0.009253 -0.030
    1.000 -0.012 -0.010 -0.002 -0.015
    1.000 -0.011 0.020 0.088
    1.000 0.005 -0.003
    1.000 -0.003
    1.000

       由相关系数矩阵可以看出,特征变量之间相关关系较弱,因此我们可以将全部特征纳入。

    数据建模和调参

      关于分类算法,目前几大主流基学习器如决策树、支持向量机、朴素贝叶斯、逻辑回归都可以实现,集成学习效果一般较基学习器好,包括随机森林、GBDT、还有比赛使用最多的XGBoost。同时,深度学习也成为数据挖掘上的重要工具,如LM神经网络、BP神经网络、RNN、CNN等。这里使用了决策树、支持向量机、朴素贝叶斯、逻辑回归、XGBoost、LM神经网络进行建模,通过对不同模型的学习能力评估选择能够预测客户流失的最优模型。

    模型评价

      通过不同模型对样本训练集的学习,之后用测试集来验证模型的学习能力。这里选用了分类度量指标precision、recall、F1值、准确度、ROC曲线、AUC值、Kappa值来比较各模型。
      首先给出各模型的precision、recall、F1值对比,结果见下图。
                  表3 各模型性能度量

    measure SVM LR Tree RF XGB Bayes LM
    precision 0.79 0.80 0.80 0.85 0.86 0.80 0.84
    recall 0.78 0.70 0.79 0.86 0.87 0.73 0.81
    F1 0.78 0.73 0.79 0.85 0.86 0.75 0.82
    Accuracy 77.90% 70.25% 78.70% 85.65% 86.55% 73.15% 81.20%

      从上表我们可以看出,XGBoost、随机森林、LM神经网络三种模型所有评价指标都在0.8以上,性能较好。其中XGBoost的四个指标都是最大值,随机森林次之,两者precision、recall、F1值都很接近,准确率相差接近1%,之后是LM神经网络。其余的模型较XGBoost、随机森林、LM神经网络表现差。
    我们再来比较一下ROC曲线和AUC值。见图12
    在这里插入图片描述                图12 ROC曲线
      从ROC曲线可以看出,XGBoost包含了其他模型的ROC曲线曲线,最靠近ROC曲线的左上角。并且比较各模型的AUC值,XGBoost也是最大的。从ROC曲线结合AUC值,认为XGBoost表现最好。
      最后,再来比较一下各模型的kappa值。Kappa统计量比较的是分类器与仅仅基于随机的分类器的性能,它是根据混淆矩阵计算得出,和混淆矩阵评价保持一致。Kappa统计量的值在-1到1之间,小于0表示分类器性能不如随机分类,大于1表示比随机分类效果好,越接近于1表示分类性能越好。一般kappa值大于0.4才认为具有较好的分类性能。kappa统计量的计算公式是:
    k=p0pc1pc k=\frac{p_0-p_c}{1-pc}
      其中,
    p0=i=1rxiiNpc=i=12(xi.x.i)N2 p_0=\frac{\sum_{i=1}^rx_{ii}}{N},p_c=\frac{\sum_{i=1}^2(x_{i.}x_{.i})}{N^2}
      xij表示的是混淆矩阵对应的值。
      我们用图像直观地表现各模型的kappa表现。
    在这里插入图片描述              图13 各模型kappa值比较
      由kappa值的条形图可以看出,XGBoost、RF、LM的值都超过了0.4,可以认为是较好的分类器。其中XGBoost最大,我们认为XGBoost的分类性能最好。
      综合以上评价指标,XGBoost表现最好。下面将针对XGBoost学习器进行调参,使模型表现达到最优。

    XGBoost模型介绍、调参及评估

      由上一节的模型评价,确定了本次关于数据集预测银行流失客户的最优算法是XGBoost。同时,XGBoost也是目前在数据挖掘比赛中表现非常好的模型。在本节首先介绍XGBoost算法的原理以及优点,然后结合数据集对XGBoost模型进行调参,获取最优超参数,最后输出模型调参后的性能指标。

    XGBoost模型介绍

    XGBoost模型原理

      XGBoost是boosting算法的其中一种。Boosting算法的思想是将许多弱分类器集成在一起形成一个强分类器。因为XGBoost是一种提升树模型,所以它是将许多树模型集成在一起,形成一个很强的分类器。而所用到的树模型则是CART回归树模型。
      XGBoost的算法思想就是不断地添加树,不断地进行特征分裂来生长一棵树,每次添加一个树,其实是学习一个新函数,去拟合上次预测的残差。当我们训练完成得到k棵树,我们要预测一个样本的分数,其实就是根据这个样本的特征,在每棵树中会落到对应的一个叶子节点,每个叶子节点就对应一个分数,最后只需要将每棵树对应的分数加起来就是该样本的预测值。
      XGBoost的目标函数是:
    Obj(t)=i=1nl(yi,y^i(t1)+ft(xi))+k=1kΩ(fk)+constant  Obj (t)= \sum_{i=1}^n l(y_i,\widehat{y}_i^{(t-1)}+f_t(x_i))+\sum_{k=1}^k\Omega(f_k)+constant\,
      其中,
    ft(x)=wq(x),wRT,q:Rd(1,2,T) f_t(x)=w_q(x), w\in R^T,q:R^d\rightarrow{(1,2,\cdots T)}
    Ω(ft)=γT+12j=1Twj2 \Omega(f_t)=\gamma T+\frac{1}{2}\sum_{j=1}^Tw_j^2
      目标函数由两部分构成,第一部分用来衡量预测分数和真实分数的差距,另一部分则是正则化项。正则化项同样包含两部分,T表示叶子结点的个数,w表示叶子节点的分数,q是叶子的索引号。γ可以控制叶子结点的个数,λ可以控制叶子节点的分数不会过大,防止过拟合。
      用泰勒展开来近似目标函数:
    f(x+Δx)f(x)+f(x)Δx+12x2 f(x+\Delta x)\simeq f(x)+f^{'}(x)\Delta x+\frac{1}{2}x^2
      定义
    gi=y^(t1)l(yi,y^(t1)),hi=y^(t1)2l(yi,y^(t1)) g_i=\partial_{\widehat{y}^{(t-1)}}l(y_i,\widehat{y}^{(t-1)}),h_i=\partial_{\widehat{y}^{(t-1)}}^2l(y_i,\widehat{y}^{(t-1)})
      所以,目标函数在泰勒展开下近似为:
    Obj(t)i=1n[l(yi,y^i(t1))+gift(xi)+12hift2(xi)]+Ω(ft)+constant  Obj^{(t)}\simeq \sum_{i=1}^n[l(y_i,\widehat{y}_i^{(t-1)})+g_if_t(x_i)+\frac{1}{2}h_if_t^2(x_i)]+\Omega(f_t)+constant\,
      在对正则化新的定义下,我们可以将目标函数改写:
    Obj(t)i=1n[gift(xi)+12hift2(xi)]+Ω(ft)=i=1n[giwq(xi)+12hiwq(xi)]+γT+λ12j=1Twj2=j=1T[(iIjgi)wj+12(iIjhi+λ)wj2]+λT  Obj^{(t)}\simeq \sum_{i=1}^n[g_if_t(x_i)+\frac{1}{2}h_if_t^2(x_i)]+\Omega(f_t)\\=\sum_{i=1}^n[g_iw_{q(x_i)}+\frac{1}{2}h_iw_{q(x_i)}]+\gamma T+\lambda \frac{1}{2}\sum_{j=1}^Tw_j^2\\=\sum_{j=1}^T[(\sum_{i\in{I_j}}g_i)w_j+\frac{1}{2}(\sum_{i\in{I_j}}h_i+\lambda)w_j^2]+\lambda T\,
      其中,
    Ij={iq(xi)=j} I_j=\left \{i|q(x_i)=j\right\}
      为了简化,我们令
    Gj=iIjgi,Hj=iIjhi G_j=\sum_{i\in{I_j}}g_i,H_j=\sum_{i\in{I_j}}h_i
      最终公式可以化简为:
    Obj(t)=j=1T[(iIjgi)wj+12(iIjhi+λ)wj2]+λT=j=1T[Gjwj+12(Hj+λj2]+γT  Obj^{(t)}=\sum_{j=1}^T[(\sum_{i\in{I_j}}g_i)w_j+\frac{1}{2}(\sum_{i\in{I_j}}h_i+\lambda)w_j^2]+\lambda T\\=\sum_{j=1}^T[G_jw_j+\frac{1}{2}(H_j+\lambda_j^2]+\gamma T\,
      通过对wj求导等于0,可以得到
    wj=GjHj+λ w_j^*=-\frac{G_j}{H_j+\lambda}
      将最优解代入可得:
    Obj=12j=1TGj2Hj+λ+γT  Obj=-\frac{1}{2}\sum_{j=1}^T\frac{G_j^2}{H_j+\lambda}+\gamma T\,

    XGBoost模型优点

      (1)精度高。XGBoost的损失函数用到了二阶导数信息,而GBDT只用到一阶;
    在大多数情况,数据集都无法避免出现null值的情况,从而导致梯度稀疏,在这种情况下,XGBoost为损失函数指定了默认的梯度方向,间接提升了模型精度和速度。
      (2)速度快。XGBoost在生成树的最佳分割点时,放弃了贪心算法,而是采用了一种从若干备选点中选择出最优分割点的近似算法,而且可以多线程搜索最佳分割点。XGBoost还以块为单位优化了特征在内存中的存取,解决了Cache-miss问题,间接提高了训练效率。根据论文所说,通过这些方法优化之后,XGBoost的训练速度比scikit-learn快40倍。
      (3)可扩展性高。GBDT的基分类器是CART,而XGBoost的基分类器支持CART,Linear,LR;Xgboost的目标函数支持linear、logistic、softmax等,可以处理回归、二分类,多分类问题。另外,XGBoost还可以自定义损失函数。
      (4)防止过拟合。XGBoost在损失函数里加入了正则项,降低模型的方差,使模型更简单,防止过拟合,还能自动处理one-hot特征。

    XGBoost模型调参

      XGBoost的参数很多,我们主要调节的参数如下:
      n_estimators:迭代次数,也就是树的个数;
      max_depth:树的深度;
      min_child_weight:最小叶子节点样本权重和,值较大是可以避免过拟合;
      Gamma:指定了节点分裂所需的最小损失函数下降值;
      subsample, colsample_bytree:这两个值一般都是0.8;
      lambda, alpha:正则化参数;
      learning_rat:学习速率。
      调参方法选用网格搜索法,在内部设置好交叉验证的折数,这里设置为5,得分函数选择的是AUC,最后让模型输出最佳得分模型。
                      表4 最优参数

    learning_rat n_estimators max_depth min_child_weight Gamma subsample colsample_bytree lambda alpha score
    0.1 100 7 2 0 0.8 0.8 3 1 0.9637

    模型评估

      最后,对调整参数后的模型再次评估。这里采用仍采用原来的评价准则。最后基于评价准则生成了表5和图14。
              表5 precision、recall、F1、kappa、accurancy

    category precision recall F1 Kappa Accuracy
    total 0.86 0.87 0.86 0.55 86.95%
    0 0.90 0.94 0.92 - -
    1 0.70 0.57 0.62 - -

      从类别来看,模型对留存客户的预测更为准确,精确度、召回率、F1值都达到了90%左右,但是对已流失客户的表现不是很好。在所有判定为流失客户中有70%是真实流失的,在所有实际流失的客户中仅判定对了57%。但模型总体的精确度是86%、召回率87%、F1值86%、kappa统计量为0.55,准确度是86.95%。总体预测的准确度还是较高的,较未调参的模型提高了0.4个百分点。
      下面看一下ROC曲线。

    在这里插入图片描述             图14 XGBoost的ROC曲线
      观察ROC曲线图,曲线靠近左上角,即在假阳率很低的情况下真阳率较高,并且AUC面积为0.8756,表示模型效果良好,可用于预测。

    关于挽留和关怀客户的建议

      客户流失的根源在于通过建立客户流失模型使银行能从源头上控制客户流失,防患于未然从而有效的防止客户流失,在日益激烈的市场竞争中,防范客户流失不是“亡羊补牢式”的被动行为,而是贯穿银行经营管理始终的营销策略。防范客户流失、开发新市场、发展新客户都具有重要意义。从营销策略效率考虑,防范客户流失比发展新客户更加经济。主动式的防范客户流失即为银行客户关系管理中长期的策略,重点是通过挽留和关怀客户,提高客户的活跃程度来防止客户向流失状态的转换。由图3-4可知,银行活跃用户的流失率远低于非活跃用户。本节旨在通过分析影响客户流失模型的重要因子,结合之前的描述性分析,为银行挽留和关怀客户提出建议。

    特征重要性

      通过上一节建立的最佳模型,通过weight(权重:所有树中一个特征被用来分裂数据的次数)的方法来衡量特征的重要性,最后得出的特征重要性排序见图15。
    在这里插入图片描述               图15 特征重要性排序
      特征值越大,说明特征越重要。从图15可以清楚的显示出在XGBoost模型下特征的重要程度。其中,特征重要程度得分超过400的有EstimatedSalary(估计收入)、Balance(存贷款)、Tenure(用户使用年数)、CreditScore_range(信用分分组)、age_group(年龄分组)。400分以下的按得分排序分别是NumOfProducts(产品数量)、IsActiveMenber(是否为活跃用户)、Geography_Germany(地区_德国)、Gender(性别)、Geography_Franch(地区_法国),HasCrCard(是否有信用卡)。

    建议

      1、目标人群定位。根据特征重要性排序,特征估计收入和存贷款情况是影响预测客户流失的最重要因素。结合之前的特征频数分布图得知,该银行客户各收入层分布均匀,说明银行并没有重视目标收入客户定位,没有明确目标人群,更多客户选择存入较少的钱。目标客户定位有助于银行指定针对性策略,提供适合目标客户的商品和服务,提高银行的竞争力。例如中国工商银行以中等收入阶层为重点目标客户;中国农业银行重点服务“三农”,建立城乡一体化;中国银行更倾向于中高端客户和大型企业。因此,认为该银行有必要明确目标客户群。
      2、用户关怀策略。由图15知,客户使用该银行产品年数也是影响模型的重要因素。为了挽留老客户,推出相应的关怀和激励策略十分有必要。对于使用产品年数较长的客户,银行可以采用会员积分制、会员优先服务、老客户感恩回馈等活动,令客户感受到银行对客户的重视;对于银行新用户,可通过各种优惠活动吸引客户使用,如与政府和商家合作推出打折活动,同时也要通过会员积分制激励新用户向老用户的转换。
      3、高信用评级激励制度。信用得分也是预测客户流失的重要影响因素。信用得分是银行用来评估客户是否具有获得信用卡的资格。根据图3-3,可以得知该银行没有取得信用卡的用户流失率更高,但是有信用卡的用户流失率也达到了16%左右。因此,为了挽留银行高信用用户,银行可推出一系列信用卡激励策略,一来可以挽留更多高信用用户,二可以刺激其他未办理信用卡的高信用评级用户申请该银行信用卡。

    python编程

    import numpy as np
    import pandas as pd
    import matplotlib.pyplot as plt
    import scipy.stats as stats
    import seaborn as sns
    import math
    from sklearn.model_selection import train_test_split
    from sklearn.preprocessing import LabelEncoder, OneHotEncoder
    from imblearn.over_sampling import SMOTE
    from sklearn.metrics import classification_report
    from collections import Counter
    from sklearn import tree
    from sklearn import svm
    from sklearn.ensemble import RandomForestClassifier
    from sklearn.linear_model import LogisticRegression
    import tensorflow as tf
    import keras
    from keras.models import Sequential
    from keras.layers.core import Dense,Activation
    from sklearn.naive_bayes import GaussianNB
    from sklearn.metrics import roc_curve,roc_auc_score
    from sklearn.metrics import cohen_kappa_score
    from sklearn.metrics import confusion_matrix
    from sklearn.model_selection import GridSearchCV
    %matplotlib inline
    #导入数据
    customers_data = pd.read_csv('G://Churn_Modelling.csv')
    X = customers_data.loc[:,['CreditScore', 'Geography', 'Gender', 'Age', 'Tenure', 'Balance', 'NumOfProducts', 'HasCrCard', 'IsActiveMember', 'EstimatedSalary']]
    y=customers_data.loc[:,'Exited']
    print(Counter(y))#打印类标签
    #字符变量量化
    labelencoder1 = LabelEncoder()
    X.Geography= labelencoder1.fit_transform(X.Geography)
    labelencoder2 = LabelEncoder()
    X.Gender = labelencoder2.fit_transform(X.Gender)
    #变量频数分布和箱线图
    def visual_exploratory(x):
        
        for var in x.select_dtypes(include = [np.number]).columns :
            print( var + ' : ')
            descStats =x[var].describe()
            mu = "%.2e" % descStats['mean']
            std = "%.2e" % descStats['std']
            maxVal = "%.2e" % descStats['max']
            minVal = "%.2e" % descStats['min']
            x[var].plot('hist')
            titleText = 'Histogram of '+ var +'\n Mean ='+mu+ ' Std='+std+'\n Max='+maxVal+' Min='+minVal
            plt.show()
            
    visual_exploratory(customers_data )
    
    # ploting the box plot to visually inspect numeric data
    
    def boxPlot_exploratory(x):
        
        for var in x.select_dtypes(include = [np.number]).columns :
            print( var + ' : ')
            x.boxplot(column = var)
            plt.show()
            
    boxPlot_exploratory(X)
    Xgroup=X.copy()
    #将信用分进行分组
    # Helper function that will create and add a new column tof credit score range the data frame
    def creditscore(data):
        score = data.CreditScore
        score_range =[]
        for i in range(len(score)) : 
            if (score[i] < 600) :  
                score_range.append(1) # 'Very Bad Credit'
            elif ( 600 <= score[i] < 650) :  
                score_range.append(2) # 'Bad Credit'
            elif ( 650 <= score[i] < 700) :  
                score_range.append(3) # 'Good Credit'
            elif ( 700 <= score[i] < 750) :  
                score_range.append(4) # 'Very Good Credit'
            elif score[i] >= 750 : 
                score_range.append(5) # 'Excellent Credit'
        return score_range
    
    # converting the returned list into a dataframe
    CreditScore_category = pd.DataFrame({'CreditScore_range': creditscore(Xgroup)})
    #年龄分组
    def agegroup(data):
        age = data.Age
        age_range =[]
        for i in range(len(age)) : 
            if (age[i] < 30) :  
                age_range.append(1) # 'Between 18 and 30 year'   
            elif ( 30 <= age[i] < 40) :  
                age_range.append(2) # 'Between 30 and 40 year'
            elif ( 40 <= age[i] < 50) :  
                age_range.append(3) # 'Between 40 and 50 year'
            elif ( 50 <= age[i] < 60) :  
                age_range.append(4) # ''Between 50 and 60 year'
            elif ( 60 <= age[i] < 70) :  
                age_range.append(5) # 'Between 60 and 70 year'
            elif ( 70 <= age[i] < 80) :  
                age_range.append(6) # 'Between 70 and 80 year'
            elif age[i] >= 80 : 
                age_range.append(7) # ''Above 80 year'
        return age_range
    def visual_exploratory(x):
        
        for var in x.select_dtypes(include = [np.number]).columns :
            print( var + ' : ')
            descStats =x[var].describe()
            mu = "%.2e" % descStats['mean']
            std = "%.2e" % descStats['std']
            maxVal = "%.2e" % descStats['max']
            minVal = "%.2e" % descStats['min']
            x[var].plot('hist')
            titleText = 'Histogram of '+ var +'\n Mean ='+mu+ ' Std='+std+'\n Max='+maxVal+' Min='+minVal
            plt.show()
            
    visual_exploratory(AgeGroup_category )
    visual_exploratory(CreditScore_category)
    # converting the returned list into a dataframe
    AgeGroup_category = pd.DataFrame({'age_group': agegroup(X)})
    #插入Xgroup, CreditScore_category,删去'CreditScore','Age'
    Xgroup=pd.concat([Xgroup, CreditScore_category],axis=1)
    Xgroup=pd.concat([Xgroup,AgeGroup_category],axis=1)
    Xgroup=Xgroup.drop(['CreditScore','Age'],axis=1)
    #相关性检验
    Xcorr=Xgroup
    print(Xcorr.corr())
    g=sns.pairplot(Xcorr,vars=['Geography','Gender','Tenure', 'Balance', 'NumOfProducts', 'HasCrCard', 'IsActiveMember', 'EstimatedSalary','CreditScore_range','age_group'])
    #划分训练集、测试集
    Xgroup_train, Xgroup_test, ygroup_train, ygroup_test = train_test_split(Xgroup, y, test_size = 0.2, random_state =40)
    model_smote = SMOTE() # 建立SMOTE模型对象
    xgroup_smote, ygroup_smote = model_smote.fit_sample(Xgroup_train, ygroup_train) # 输入数据并作过抽样处理
    xgroup_smote = pd.DataFrame(xgroup_smote, columns=[ 'Geography', 'Gender', 'Tenure', 'Balance', 'NumOfProducts', 'HasCrCard', 'IsActiveMember', 'EstimatedSalary','CreditScore_range','age_group']) # 将数据转换为数据框并命名列名
    ygroup_smote = pd.DataFrame(ygroup_smote,columns=['Exited']) # 将数据转换为数据框并命名列名
    smotegroup_resampled = pd.concat([xgroup_smote, ygroup_smote],axis=1) # 按列合并数据框
    groupby_data_smote = smotegroup_resampled.groupby('Exited').count() # 对label做分类汇总
    #smote处理后分类变量离散化
    Xgroup_smote_train=pd.DataFrame(Xgroup_smote,columns=['Geography_Franch','Geography_Germany','Geography_Spain', 'Gender', 'Tenure', 'Balance', 'NumOfProducts', 'HasCrCard', 'IsActiveMember', 'EstimatedSalary','CreditScore_range','age_group'])
    length=len(xgroup_smote)
    for i in range(length):
        
        if xgroup_smote.Geography[i]>=1.5:
            xgroup_smote.Geography[i]=int(2)
        elif xgroup_smote.Geography[i]>=0.5 and xgroup_smote.Geography[i]<1.5:
            xgroup_smote.Geography[i]=int(1)
        else:
            xgroup_smote.Geography[i]=int(0)
        if xgroup_smote.Gender[i]>=0.5:
            xgroup_smote.Gender[i]=1
        else:
            xgroup_smote.Gender[i]=0
        if xgroup_smote.HasCrCard[i]>=0.5:
            xgroup_smote.HasCrCard[i]=1
        else:
            xgroup_smote.HasCrCard[i]=0
        if xgroup_smote.IsActiveMember[i]>=0.5:
            xgroup_smote.IsActiveMember[i]=1
        else:
            xgroup_smote.IsActiveMember[i]
    #独热编码
    Xgroup_smote=xgroup_smote.copy()
    onehotencoder = OneHotEncoder(categorical_features = [0])
    Xgroup_smote = onehotencoder.fit_transform(Xgroup_smote).toarray()
    Xgroup_smote_train=pd.DataFrame(Xgroup_smote,columns=['Geography_Franch','Geography_Germany','Geography_Spain', 'Gender', 'Tenure', 'Balance', 'NumOfProducts', 'HasCrCard', 'IsActiveMember', 'EstimatedSalary','CreditScore_range','age_group'])
    Xgrouphot_test=Xgroup_test.copy()
    onehotencoder = OneHotEncoder(categorical_features = [0])
    Xgrouphot_test = onehotencoder.fit_transform(Xgrouphot_test).toarray()
    Xgrouphot_test=pd.DataFrame(Xgrouphot_test,columns=['Geography_Franch','Geography_Germany','Geography_Spain', 'Gender', 'Tenure', 'Balance', 'NumOfProducts', 'HasCrCard', 'IsActiveMember', 'EstimatedSalary','CreditScore_range','age_group'])
    #虚拟变量处理
    Xgroup_smote_train=Xgroup_smote_train.drop(['Geography_Spain'],axis=1)
    Xgrouphot_test=Xgrouphot_test.drop(['Geography_Spain'],axis=1)
    
    #标准化
    from sklearn.preprocessing import StandardScaler
    sc = StandardScaler()
    Xgroup_smote_train = sc.fit_transform(Xgroup_smote_train)
    Xgrouphot_test = sc.transform(Xgrouphot_test)
    #tree
    clfgroup = tree.DecisionTreeClassifier()
    clfgroup = clfgroup.fit(Xgroup_smote_train,ygroup_smote)
    y_predgroup = clfgroup.predict(Xgrouphot_test)
    accuracy1 = accuracy_score(ygroup_test, y_predgroup)
    
    print("Accuracy: %.2f%%" % (accuracy1* 100.0))
    print(classification_report(ygroup_test, y_predgroup))
    #XGBoost
    xgb_model2= xgb.XGBClassifier(objective ='binary:logistic',learning_rat= 0.1, n_estimators=100, max_depth= 6, min_child_weight= 1, seed= 0,
                        subsample=0.8, colsample_bytree= 0.8, gamma=0., reg_alpha= 2, reg_lambda= 1)
                        xgb_model2.fit(Xgroup_smote_train,ygroup_smote)
    # make predictions for test data
    predictions2 = xgb_model2.predict(Xgrouphot_test)
    # evaluate predictions
    accuracy2 = accuracy_score(ygroup_test, predictions2)
    
    print("Accuracy: %.2f%%" % (accuracy2* 100.0))
    print(classification_report(ygroup_test, predictions2))
    #RF
    clfrf = RandomForestClassifier(n_estimators=10, max_depth=None,min_samples_split=2, random_state=0) 
    clfrf=clfrf.fit(Xgroup_smote_train,ygroup_smote)
    predictions_rf = clfrf.predict(Xgrouphot_test)
    # evaluate predictions
    accuracy_rf = accuracy_score(ygroup_test, predictions_rf)
    
    print("Accuracy: %.2f%%" % (accuracy_rf* 100.0))
    print(classification_report(ygroup_test, predictions_rf))
    #svm
    ygroup_smote_np=np.array(ygroup_smote).reshape(12694,)
    sv = svm.SVC(kernel='rbf', C=1, gamma=1,probability=True) 
    sv=sv.fit(Xgroup_smote_train,ygroup_smote_np)
    predictions_sv = sv.predict(Xgrouphot_test)
    # evaluate predictions
    accuracy_sv = accuracy_score(ygroup_test, predictions_sv)
    
    print("Accuracy: %.2f%%" % (accuracy_sv* 100.0))
    print(classification_report(ygroup_test, predictions_sv))
    print(confusion_matrix(ygroup_test, predictions_sv))
    #LR
    lr=LogisticRegression()
    lr=lr.fit(Xgroup_smote_train,ygroup_smote_np)
    predictions_lr = lr.predict(Xgrouphot_test)
    # evaluate predictions
    accuracy_lr = accuracy_score(ygroup_test, predictions_lr)
    
    print("Accuracy: %.2f%%" % (accuracy_lr* 100.0))
    print(classification_report(ygroup_test, predictions_lr))
    
    #bayes
    by=GaussianNB()
    by=by.fit(Xgroup_smote_train,ygroup_smote_np)
    predictions_by = by.predict(Xgrouphot_test)
    # evaluate predictions
    accuracy_by = accuracy_score(ygroup_test, predictions_by)
    
    print("Accuracy: %.2f%%" % (accuracy_by* 100.0))
    print(classification_report(ygroup_test, predictions_by))
    #LM
    net=Sequential()
    net.add(Dense(input_dim=11,output_dim=10))
    net.add(Activation('relu'))
    net.add(Dense(input_dim=10,output_dim=1))
    net.add(Activation('sigmoid'))
    net.compile(loss='mean_squared_error',optimizer='adam')
    net.fit(Xgroup_smote_train,ygroup_smote,epochs = 1000, batch_size = 200)
    pred_lm= net.predict_classes(Xgrouphot_test).reshape(len(ygroup_test))
    prob_lm= net.predict(Xgrouphot_test).reshape(len(ygroup_test))
    accuracy_lm = accuracy_score(ygroup_test, pred_lm)
    
    print("Accuracy: %.2f%%" % (accuracy_lm* 100.0))
    print(classification_report(ygroup_test, pred_lm)
    #ROC
    prob_sv=sv.predict_proba(Xgrouphot_test)
    prob_lr=lr.predict_proba(Xgrouphot_test)
    prob_by=by.predict_proba(Xgrouphot_test)
    prob_rf=clfrf.predict_proba(Xgrouphot_test)
    prob_xgb=xgb_model2.predict_proba(Xgrouphot_test)
    prob_tree=clfgroup.predict_proba(Xgrouphot_test)
    fpr_sv, tpr_sv, thresholds_sv = roc_curve(ygroup_test, prob_sv[:,1], drop_intermediate= True)
    fpr_lr, tpr_lr, thresholds_lr = roc_curve(ygroup_test, prob_lr[:,1], drop_intermediate= True)
    fpr_by, tpr_by, thresholds_by = roc_curve(ygroup_test, prob_by[:,1], drop_intermediate= True)
    fpr_rf, tpr_rf, thresholds_rf = roc_curve(ygroup_test, prob_rf[:,1], drop_intermediate= True)
    fpr_xgb, tpr_xgb, thresholds_xgb = roc_curve(ygroup_test, prob_xgb[:,1], drop_intermediate= True)
    fpr_tree, tpr_tree, thresholds_tree = roc_curve(ygroup_test, prob_tree[:,1], drop_intermediate= True)
    fpr_lm, tpr_lm, thresholds_lm= roc_curve(ygroup_test, prob_lm, drop_intermediate= True)
    auc_lm=roc_auc_score(ygroup_test,prob_lm)
    auc_sv=roc_auc_score(ygroup_test, prob_sv[:,1])
    auc_lr=roc_auc_score(ygroup_test, prob_lr[:,1])
    auc_by=roc_auc_score(ygroup_test, prob_by[:,1])
    auc_rf=roc_auc_score(ygroup_test, prob_rf[:,1])
    auc_xgb=roc_auc_score(ygroup_test, prob_xgb[:,1])
    auc_tree=roc_auc_score(ygroup_test, prob_tree[:,1])
    plt.plot(fpr_sv, tpr_sv,label= "SVM-AUC:%.4f"%auc_sv)
    plt.plot(fpr_lr, tpr_lr,label= "LR-AUC:%.4f"%auc_lr)
    plt.plot(fpr_by, tpr_by,label= "Bayes-AUC:%.4f"%auc_by)
    plt.plot(fpr_rf, tpr_rf,label= "RF-AUC:%.4f"%auc_rf)
    plt.plot(fpr_xgb, tpr_xgb,label= "XGB-AUC:%.4f"%auc_xgb)
    plt.plot(fpr_tree, tpr_tree,label= "Tree-AUC:%.4f"%auc_tree)
    plt.plot(fpr_lm, tpr_lm,label= "LM-AUC:%.4f"%auc_lm)
    plt.legend()
    plt.plot([0, 1], [0, 1])
    plt.xlim(0,1)
    plt.ylim(0,1)
    plt.title('Roc curve')
    plt.show()
    #kappa
    kappa_SVM=cohen_kappa_score(ygroup_test, predictions_sv)
    kappa_LR=cohen_kappa_score(ygroup_test, predictions_lr)
    kappa_Tree=cohen_kappa_score(ygroup_test, y_predgroup)
    kappa_RF=cohen_kappa_score(ygroup_test, predictions_rf)
    kappa_XGB=cohen_kappa_score(ygroup_test, predictions2)
    kappa_Bayes=cohen_kappa_score(ygroup_test, predictions_by)
    kappa_LM=cohen_kappa_score(ygroup_test, pred_lm)
    num_list=[kappa_SVM,kappa_LR,kappa_Tree,kappa_RF,kappa_XGB,kappa_Bayes,kappa_LM]
    name_list=['SVM','LR','Tree','RF','XGB','Bayes','LM']
    plt.bar(name_list,num_list)
    plt.title(u'各模型kappa值对比')
    plt.show()
    #调参
    #n_estimators
    cv_params = {'n_estimators': 80,100,120,140,160]}
    other_params = {'learning_rat': 0.1, 'n_estimators':100, 'max_depth':6, 'min_child_weight':1,
                        'subsample':0.8, 'colsample_bytree': 0.8, 'gamma':0., 'reg_alpha':2, 'reg_lambda': 1}
    optimized_GBM.fit(Xgroup_smote_train,ygroup_smote)
    print('参数的最佳取值:{0}'.format(optimized_GBM.best_params_))
    print('最佳模型得分:{0}'.format(optimized_GBM.best_score_))
    #max_depth、min_child_weight
    cv_params = { 'max_depth': [3, 4, 5, 6, 7, 8, 9, 10], 'min_child_weight': [1, 2, 3, 4, 5, 6]}
    other_params = {'learning_rat': 0.1, 'n_estimators':100, 'max_depth':6, 'min_child_weight':1,
                        'subsample':0.8, 'colsample_bytree': 0.8, 'gamma':0., 'reg_alpha':2, 'reg_lambda': 1}
                        model = xgb.XGBClassifier(**other_params)
    optimized_GBM = GridSearchCV(estimator=model, param_grid=cv_params, scoring='roc_auc', cv=5, verbose=1, n_jobs=4)
    optimized_GBM.fit(Xgroup_smote_train,ygroup_smote)
    print('参数的最佳取值:{0}'.format(optimized_GBM.best_params_))
    print('最佳模型得分:{0}'.format(optimized_GBM.best_score_))
    #gamma
    cv_params = {'gamma': [0,0.1, 0.2, 0.3, 0.4, 0.5, 0.6]}
    other_params = {'learning_rate': 0.1, 'n_estimators': 100, 'max_depth': 7, 'min_child_weight': 2, 'seed': 0,
                        'subsample': 0.8, 'colsample_bytree': 0.8, 'gamma': 0., 'reg_alpha':2, 'reg_lambda': 1}
                        model = xgb.XGBClassifier(**other_params)
    optimized_GBM = GridSearchCV(estimator=model, param_grid=cv_params, scoring='roc_auc', cv=5, verbose=1, n_jobs=2)
    optimized_GBM.fit(Xgroup_smote_train,ygroup_smote)
    print('参数的最佳取值:{0}'.format(optimized_GBM.best_params_))
    print('最佳模型得分:{0}'.format(optimized_GBM.best_score_))
    #reg_alpha、'reg_lambda
    cv_params = {'reg_alpha': [0.2,0.4,0.6,0.8,1], 'reg_lambda': [3,4,5]}
    other_params = {'learning_rate': 0.1, 'n_estimators': 100, 'max_depth': 7, 'min_child_weight': 2, 'seed': 0,
                        'subsample': 0.8, 'colsample_bytree': 0.8, 'gamma': 0., 'reg_alpha': 2, 'reg_lambda': 1}
                        model = xgb.XGBClassifier(**other_params)
    optimized_GBM = GridSearchCV(estimator=model, param_grid=cv_params, scoring='roc_auc', cv=5, verbose=1, n_jobs=2)
    optimized_GBM.fit(Xgroup_smote_train,ygroup_smote)
    print('参数的最佳取值:{0}'.format(optimized_GBM.best_params_))
    print('最佳模型得分:{0}'.format(optimized_GBM.best_score_))
    #learning_rate
    cv_params = {'learning_rate': [0.01, 0.05, 0.07, 0.1, 0.2]}
    other_params = {'learning_rate': 0.1, 'n_estimators': 100, 'max_depth': 7, 'min_child_weight': 2, 'seed': 0,
                        'subsample': 0.8, 'colsample_bytree': 0.8, 'gamma': 0., 'reg_alpha': 1, 'reg_lambda': 3}
                        model = xgb.XGBClassifier(**other_params)
    optimized_GBM = GridSearchCV(estimator=model, param_grid=cv_params, scoring='roc_auc', cv=5, verbose=1, n_jobs=2)
    optimized_GBM.fit(Xgroup_smote_train,ygroup_smote)
    print('参数的最佳取值:{0}'.format(optimized_GBM.best_params_))
    print('最佳模型得分:{0}'.format(optimized_GBM.best_score_))
    #模型评估
    xgb_model_xgb= xgb.XGBClassifier(objective ='binary:logistic',learning_rate=0.1, n_estimators=100, max_depth=7, min_child_weight=2, seed=0,
                        subsample=0.8, colsample_bytree=0.8, gamma=0., reg_alpha=1, reg_lambda=3)
    xgb_model_xgb.fit(Xgroup_smote_train,ygroup_smote)
    # make predictions for test data
    predictions_xgb = xgb_model_xgb.predict(Xgrouphot_test)
    # evaluate predictions
    accuracy_xgb = accuracy_score(ygroup_test, predictions_xgb)
    
    print("Accuracy: %.2f%%" % (accuracy_xgb* 100.0))
    prob_xgb=xgb_model_xgb.predict_proba(Xgrouphot_test)
    fpr, tpr, thresholds = roc_curve(ygroup_test, prob_xgb[:,1], drop_intermediate= True)
    plt.plot(fpr, tpr,label= "auc")
    auc=roc_auc_score(ygroup_test, prob_xgb[:,1])
    plt.legend()
    plt.plot([0, 1], [0, 1])
    plt.xlim(0,1)
    plt.ylim(0,1)
    plt.title(auc)
    plt.show()
    xgb.plot_importance(xgb_model_xgb)
    plt.show()
    kappa=cohen_kappa_score(ygroup_test, pxgb)
    
    展开全文
  • 银行客户流失分析预测

    千次阅读 2020-09-27 17:22:20
    毫无疑问,客户流失会给银行带来大量损失。考虑到避免一位客户流失的成本很可能远低于挖掘一位新客户,对客户流失情况的分析预测至关重要。本文分析的数据为某银行10000条客户信息,有14个字段。 ...

    客户流失意味着客户终止了和银行的各项业务,毫无疑问,一定量的客户流失会给银行带来巨大损失。考虑到避免一位客户流失的成本很可能远低于挖掘一位新客户,因此对客户流失情况的分析预测至关重要。本文分析了某银行10000条客户信息,含14个字段,接下来我们将从这些数据中探索客户流失特征和原因,推测目前客户管理、业务等方面可能存在的问题,建立预测模型预警客户流失情况,为制定挽留策略提供依据。

    1. 探索性分析

    1.1 认识字段

    首先,初步了解下这些数据。

    在这里插入图片描述
    字段依次表示行号、客户Id、姓氏、信用积分、地理位置、性别、年龄、开户时长、账户余额、产品数量、有无信用卡、是否活跃、收入估计、是否流失(即目标变量)。为了便于后续处理,对字段重新排列,将目标变量放在首列,并去除无用字段。显然其中行号、客户Id、姓氏对流失情况的分析预测意义不大,可以忽略,其余字段可分为分类变量和数值变量,在数据处理环节再做变换等操作。另外,剔除少量异常数据。

    1.2 流失率与分类变量

    从数据中不难得知流失率为20.4%,接下来先对分类变量做初步探索,为了便于观察流失占比,这里采用饼图,饼图大小代表用户数量,橙、蓝分别代表流失、留存用户:
    在这里插入图片描述

    按性别分,女性用户少于男性用户数量,但流失率更高。建议银行提升女性的用户体验,考虑到女性消费能力很强,可以考虑和商家联手推出倾向女性顾客的优惠活动。按有无信用卡分,有信用卡者明显居多,有信用卡的两类群体中流失率略低。按国家分,法国用户数量最多,德国用户与西班牙用户数量相近,但德国用户流失率远高于其他两国,建议对德国的客户管理情况做进一步深入调研。按照活跃度分,不活跃用户流失率远高于活跃用户,因此活跃度是个需要重点关注和提升的指标。

    1.3 流失率与数值变量

    下面继续观察下数值变量的分布情况,如图:
    在这里插入图片描述
    从账户余额看,余额在10万-15万之间的较高余额用户流失率很高,这势必会对银行业务造成不小损失,值得重点关注。从年龄看,用户多分布在年龄在25-50岁之间,年龄较长的客户(45-65岁)流失率非常高,因而在采取挽留策略时需要视不同的年龄划分而定。从信用积分看,信用积分分布在600-700分之间的客户最多,流失的客户数量也最多。从开户时长看,用户的开户时长主要为1-9年,且开户1-9年的这些客户开户时长频数分布比较均匀,流失率没有明显差异;然而开户时间短于1年或者高于9年的用户有更高的流失风险,可以考虑优化新客户的开发和挽留策略。从产品数量看,拥有产品数为1、2的客户数量最多,且有2个产品的用户流失率低于拥有1个产品的用户,可以考虑提升产品的吸引力,增强用户黏度。从收入看,用户收入多在2万-18万间,且用户数量和流失率在此区间分布较为均匀,事实上社会中人群的收入分布并不是如此均匀,这表明有大量的潜在客户可以开发。

    2.数据处理

    2.1 删除字段

    前文已提到行号、客户Id、姓氏可以忽略,这里我们将这三个字段剔除。

    2.2 新增字段

    除了存款和收入,客户将多少比例的收入存到银行同样是个重要的考量参数,因此新增一个特征:存款收入比,即存款/收入。所有客户的存款收入比均大于零,且存款收入比越高,越容易流失。
    在这里插入图片描述

    2.3 处理缺失值、异常值

    数据无需处理缺失值,省略。至于异常值不止一两个,且对流失情况预测可能有一定意义,选择暂不处理。

    2.4 转换分类变量

    分类变量的种类较少,可直接选用独热编码来处理性别和国家,所用函数为pd. get_dummies()。

    2.5 归一化数值变量

    在归一化数值变量前先随机将80%数据划分为训练集,余下20%为测试集。然后对数值变量分别归一化,变换方法如下:
    df_train[col1] = (df_train[col1]-col1_min)/(col1_max-col1_min)
    顺便提下,在模型训练时,测试集属于外来数据,应该和训练集完全分开,如果直接对所有数据进行归一化,测试集的数据可能给训练集的特征范围等带来影响,进而影响模型效果。

    3. 模型拟合和选取

    3.1 模型调用

    将目标变量提取出来,命名为y_train, y_test,余下变量则命名为X_train,X_test。采用决策树、KNN近邻、支持向量机(SVM)几种常用的算法训练模型,并选取最优算法。
    这里涉及参数多采用sklearn中的默认参数,以简化工作。当然,随着对机器学习的了解深入,可以通过调参、模型融合等方式进一步提升预测效果。

    from sklearn.neighbors import KNeighborsClassifier
    from sklearn.svm import SVC
    from sklearn.tree import DecisionTreeClassifier
    from sklearn.metrics import accuracy_score
    
    model = [KNeighborsClassifier(), SVC(), DecisionTreeClassifier()]
    model_names = ["KNN", "SVM", "Decision tree"]
    for i in range(0, 3):
        y_pred = model[i].fit(X_train, y_train).predict(X_test)
        accuracy = accuracy_score(y_pred, y_test)*100
        print(model_names[i], ":", accuracy, "%")
    
    3.2 最佳预测模型

    三种模型在测试集中的预测准确率分别如下:
    在这里插入图片描述

    发现支持向量机SVM模型的效果最好,故选取该模型。对测试集的预测准确率达84.2%,可以推测用该模型对银行客户流失情况预测具有一定准确率,预测结果具备重要的参考价值。

    3.3 预测模型意义

    银行客户流失情况预测模型的建立,能够给银行业务提供有效的预警。对有流失倾向的客户做出标记,能使银行在客户管理和策略制定上更有针对性,用较低成本实现客户挽留率的改善,减少客户流失带来的损失。

    展开全文
  • 数据挖掘大作业文档,对银行客户流失进行分析,随着互联⽹⾦融的异军突起,银⾏业的竞争愈加激烈,防⽌客户流失和挽留⽼客户成为各⼤银⾏ 关⼼的重要问题。本⽂⾸先根据已有数据集对各特征进⾏描述性统计分析,初步...
  • 针对客户流失分析中实际客户样本数据量大、流失与未流失客户样本分布不平衡的特点,提出一种基于Boos- ting与代价敏感决策树的集成方法,并将其应用于商业银行个人理财业务的客户流失分析。通过实际商业银行客户数据集...
  • 电信客户流失分析

    2021-07-03 11:37:32
    若想做到客户流失前预防、流失后召回,就必须通过数据分析和建模来总结经验、预测未来。因此,本文选取了Kaggle中一个现实世界里的电信公司的客户流失数据,用其来探究电信客户流失背后的主要原因,并建立客户流失...

    1.分析目的

    现如今,在电信行业蓬勃发展的同时,电信市场也趋于饱和,获取一个新客户的难度要远远高于维系一个老客户的难度,而老客户的流失意味着收益的流失和市场占有率的下降。可以说,电信运营商的竞争就是针对客户资源的竞争。然而,客户流失从来都是无法避免的,客户流失的原因也不尽相同。若想做到客户流失前预防、流失后召回,就必须通过数据分析和建模来总结经验、预测未来。因此,本文选取了Kaggle中一个现实世界里的电信公司的客户流失数据,用其来探究电信客户流失背后的主要原因,并建立客户流失预警模型,帮助电信公司为优化业务、提高客户留存、减少流失制定策略。无论是互联网行业还是传统行业,所有产品都需要关注用户流失,用户流失原因的拆解思路也都大致相同。因此,本文的探究思路是比较具有现实的推广意义的。

    2.分析思路

    首先,将数据集中的标签属性划分为不同的维度。在每一个维度下,通过数据处理和数据可视化的手段,逐个探究各个因素与客户流失率之间是否具备相关性。

    其次,处理数据并建模。通过手工筛选方式对数据做特征工程处理,然后选取逻辑回归模型、随机森林模型建立电信客户流失预警机制。

    最后,结合以上分析结果,分别从业务角度和用户角度为电信公司加强客户黏性、减少客户流失提出一些可供落地的建议。

    3.探索数据集

    3.1去除重复值

    每条数据记录代表一位客户,使用字段customerID对数据进行去重

    #保证数据完整性先备份数据
    data_cleaner = data.copy()
    
    #重复值处理
    data_cleaner = data_cleaner.drop_duplicates(['customerID'])
    print('去重前的数据形状为:{},去重后的数据形状为{}'.format(data.shape,data_cleaner.shape))
    
    去重前的数据形状为:(7043, 21),去重后的数据形状为(7043, 21)
    

    可见在本数据集中,没有重复值

    3.2变量类型转换

    TotalCharges为总计缴费,应转变为数值变量

    TotalCharges存在11个空字段,无法直接转化为浮点数类型,将空字段填充为np.nan进行处理

    data_cleaner['TotalCharges'] = data_cleaner['TotalCharges'].replace(' ',np.nan).astype(float)
    

    3.3缺失值处理

    TotalCharges 字段是客户的总计缴费,总计缴费=留存月数(tenure)×月度缴费(MonthlyCharges)

    data_cleaner['TotalCharges'].fillna(data_cleaner['tenure']*data_cleaner['MonthlyCharges']
                                        ,inplace=True)
    

    3.4异常值处理

    本数据集的数据主要有两种类型,一种是以str类型为主的离散型数据,二是以int,float为主的连续型数值数据。

    # 离散型数据
    for i in data_cleaner.select_dtypes(include='object'):
        print('\n','-'*20,i,'的值有')
        print(data_cleaner[i].value_counts())
    

    观察离散数据的分布情况,没有明显的异常值,各值均正常。

    绘制连续型数值MonthlyChargesTotalCharges 的箱型图与分布图,观察是否有异常值

    月度收费 年度收费

    由上图可知,字段MonthlyChargesTotalCharges不存在异常值情况。

    4.流失率的影响因素分析

    4.1客户个人特征属性

    客户的个人特征属性包括Gender(性别)、SeniorCitizen(是否为老年人)、Partner(是否有配偶)、Dependents(是否有子女)。

    4.1.1 性别维度

    总体样本中,女性与男性客户比例接近1:1,女性客户3488人,占比49.52%;男性客户3555人,占比50.48%

    男女人数比例 男女流失人数占比
    性别 gender_loss

    将客户以性别分群,从上述环形图可知,该电信业务的男性客户稍多一些,但男女占比基本为1:1;客户流失方面,男女比例也基本持平,男女客户流失情况大致相同

    男女流失率

    作图并计算流失率,男性客户流失率在26.2%,女性客户流失率在26.9%,二者相差无几。因此可以断定,客户的性别与其是否流失并无直接联系。

    4.1.2 年龄维度

    将客户以是否为老年人进行分群,从客户体量来说,客户群体偏向年轻化,老年客户只占到所有客户的16.21%,仅为1142人;非老年人占83.79%,有5901人。非老年人与老年人用户占比接近5.2:1。老年人是样本中的小群体,大部分客户是非老年人

    年龄维度

    再用流失率对两组客户进行细分,作图并计算流失率,发现老年客户的流失率在41.68%,而非老年客户的流失率在23.61%。可以认为,客户的年龄与其是否流失有直接联系,老年人群相对于非老年人群更容易流失。

    年龄维度流失占比 年龄维度流失率
    老年人-流失

    探究造成老年客户高流失率的原因。根据现实经验,老年人比较容易在电子产品上出现使用困难,尤其是在使用网络服务的时候。因此提取出老年客户的主要业务开通情况数据(包括电话业务、互联网业务以及技术支持服务)

    Phone Service服务中的老年人订购情况 流失率
    电话业务老年人比例 电话业务中的老年人流失
    Internet Service服务中的老年人订购情况 流失率
    电信服务老年人 电信老年人流失

    由上图可知,90%以上的老年客户都开通了电话业务和互联网业务,其中互联网业务的覆盖率更是达到了95%以上。

    从电话业务上看,无论是否开通此项业务,老年客户的流失率都是比较高的,均达到了40%+,猜测可能是老年客户的需求还没有被完全满足,具体是什么需求可以通过问卷、走访等形式进行调研。此外也不排除是由于死亡等不可抗力因素导致的流失。

    然而,开通了互联网服务的老年客户的流失率却相当高,达到了43.21%,几乎是未开通这项服务的老年客户的流失率的5倍。这个数据在一定程度上验证了上文的猜想,老年客户确实存在上网困难的问题,这最终导致了他们的流失。另一方面,也可能是互联网业务本身存在一定的问题,比如网络不稳定或者价格与服务质量不相匹配等等。在后文会对此展开讨论。

    对于使用上存在的问题,可以通过开通技术支持服务这项附加服务来解决。观察老年客户开通技术支持服务的情况。

    Tech Support服务中的老年人订购情况 流失率
    技术支持人数 技术支持流失

    大多数老年客户都开通了技术支持服务。通过计算发现,没有开通技术支持服务的老年客户的流失率在48.19%,而开通了技术支持服务的老年客户的流失率仅在19.62%。可以认为,开通技术支持服务能够有效缓解老年客户的流失情况。

    4.1.3 亲属维度

    Dependents Dep_loss Dep_loss_rate
    有无子女 image-20210703000333046 有无子女流失率
    Partner Par_loss Par_loss_rate
    已婚未婚 已婚未婚流失人数 已婚未婚流失率

    从图上可以发现,无配偶、无子女客户的流失率更高。

    根据现实经验,有配偶或子女的人群在日常生活中使用手机的时长更长,他们更倾向于长期固定使用一个电话号码,而不是频繁更换移动运营商,因此他们流失率也相对较低。

    将是否有配偶和是否有子女两列合并为是否有亲属列(有配偶或有子女),画出是否有亲属与在网时长、月租费、总费用之间的箱线图。

    xiangxiangtuqinshu

    上图呈现出的结果与经验相符,有亲属客户的在网时长集中在20-60个月之间,而大多数无亲属客户的在网时长不到40个月。两类客户的月租费分布比较相似,有亲属客户的月租费整体相对高一些。然而总费用就呈现出显著的差异。有亲属客户的总费用集中在800-5000元之间,而无亲属客户的总费用集中在200-2400元之间。

    从上图还可以观察到,一些客户虽然无配偶或子女,但他们已经成长为超级忠诚客户,在网时长、月租费和总费用都相当高。此外,上图也证明了,在网时长和总费用之间呈现出显著的正相关关系。

    4.1.4 小结

    • 性别与是否流失无关;年龄和亲属关系与是否流失有关。
    • 老年客户的流失率更高,造成这种高流失率的原因可能是老年客户存在上网困难问题,而开通技术支持服务可以在一定程度上减少流失,也可能是互联网业务本身存在一定问题,更有可能是两者的共同作用
    • 有亲属(即有配偶或子女)的客户流失率较低,相比无亲属的客户,他们的平均在网时长更长,月租费和总费用都要更高

    4.2电信业务服务属性

    包括PhoneService(电话业务)、InternetService(互联网业务)、MultipleLines(多线业务)、OnlineSecurity(在线安全业务)、OnlineBackup(在线备份业务)、DeviceProtection(设备保护业务)、TechSupport(技术支持业务)、StreamingTV(网络电视)、StreamingMovies(网络电影)

    4.2.1电话业务

    PhoneService Ph_loss Ph_loss_rate
    image-20210703103202562 image-20210703103139194 image-20210703102958185

    90%以上的客户都开通了电话业务。开通了电话业务的客户流失率为26.71%,未开通电话业务的客户流失率为24.93%,二者差异不大。这说明从业务角度看,电话服务并不是导致流失的原因之一

    4.2.2互联网业务

    InternetService In_loss In_loss_rate
    image-20210703105256661 image-20210703105244326 image-20210703105203063

    约有78%的客户开通了互联网服务,其中使用Fiber optic的客户占比较高,但其流失率也相当高,超过40%,差不多是DSL客户流失率的两倍。

    根据现实经验,Fiber optic属于光纤,网速快且稳定,使用体验比DSL更好。 然而它却带来了相当惊人的客户流失率,因此可以断定Fiber optic存在一定的问题,必须尽快改进以提升这项业务的质量。

    image-20210703105502762

    画出DSL和Fiber optic与月租费之间的箱型图,不难看出,开通了Fiber optic的客户的月租费几乎是开通DSL客户的月租费的两倍。很明显,高费用的Fiber optic并没有给客户带来相匹配的使用体验,优化该项业务迫在眉睫。

    4.2.3附加业务

    Multiple Lines loss_number loss_rate
    image-20210703105821848 image-20210703105945514 image-20210703110035518
    OnlineSecurity loss_number loss_rate
    image-20210703110240748 image-20210703110427613 image-20210703110438667
    OnlineBackup loss_number loss_rate
    image-20210703110630699 image-20210703110614660 image-20210703110543301
    DeviceProtection loss_number loss_rate
    image-20210703110748678 image-20210703110803975 image-20210703110820604
    TechSupport loss_number loss_rate
    image-20210703110951186 image-20210703110921666 image-20210703110936880
    StreamingTV loss_number loss_rate
    image-20210703111235602 image-20210703111323654 image-20210703111258261
    StreamingMovies loss_number loss_rate
    image-20210703111538868 image-20210703111503800 image-20210703111522810

    从环形图来看,基本上每一项附加业务的开通客户数都在总体的30%左右,多线业务的开通人数比较多,在40%左右。而开通了在线安全、在线备份、设备保护和技术支持这四项附加业务的客户的流失率都比较低。

    其中,未开通在线安全服务或技术支持服务的客户的流失率大概是已开通客户的流失率的两倍,说明这两项附加业务的开通确实可以显著地减少客户流失。

    此外,涉及互联网服务支持的网络电视、电影业务的流失率相对较高,又一次印证了公司的基础互联网服务的确存在着比较大的问题。

    4.2.4小结

    • 从公司的两项主要业务来看,目前基础的互联网服务存在着比较大的问题。这之中使用Fiber optic的客户最多,但其带来的流失率也高得惊人。这可能是因为高额月费并没有给客户带来高质量的体验,因此优化业务迫在眉睫。依托互联网服务的网络电视和网络电影业务上的高流失率也证明了这一点
    • 虽然公司的基础网络业务确实有一定问题,但是通过多项附加业务的补充,该问题带来的流失率可以被有效降低。其中,应当将在线安全服务和技术支持服务作为推荐给客户的附加业务的优先级

    4.3客户消费属性

    包括PaperlessBilling(账单形式)、PaymentMethod(支付方式)、Contract(合同签订方式)、tenure(在网时长)、MonthlyCharges(月租费)、TotalCharges(总费用)。

    4.3.1账单形式

    PaperlessBilling loss_number loss_rate
    image-20210703152924939 image-20210703153014023 image-20210703152954208

    PaperlessBilling中,yes代表电子计费,No代表纸质化计费。从上图中可以得知,使用电子账单服务的客户流失率更高。这可能是因为,电子账单比较容易被忽视,带给客户的体验不佳

    4.3.2支付方式

    PaymentMethod loss_number loss_rate
    image-20210703154101859 image-20210703154139536 image-20210703154118664

    使用电子支票支付的客户的流失率要远远高于其他方式,这可能是因为客户更习惯于传统的支票、银行卡支付方式,对电子支付的接纳度不高。

    对比之下,使用银行或信用卡自动转账的客户的流失率最低,推测是因为无需人工操作,对于客户来说更加便捷,自动支付带来的消费惯性也会使得客户不会轻易更换运营商。因此应当引导客户采用其他三种方式进行支付,尤其是自动转账方式

    4.3.3合同签订方式

    Contract loss_number loss_rate
    image-20210703154453324 image-20210703154535157 image-20210703154514469

    从环形图上可以看出,目前大多数客户仍然采用按月签订的方式;通过柱形图可以明显看到,合同签订方式对客户的流失率有较大的影响,合同期限越长,客户的流失率越低,客户的黏性也越大。按月签订合同的客户流失率非常高,达到42.71%。 当合同期限为1年时,流失率陡降至11.27%,合同期限为2年时,流失率仅有2.83%

    image-20210703155007039

    画出合同签订期限与总费用之间的环形图。可以明显看到,签订长期合同(1年或2年)的用户贡献了60%以上的收入,也再一次表明,将月租客户逐渐发展为年租客户应当成为公司的一项重要发展策略

    4.3.4在网时长

    下载

    密度曲线表明,客户的流失率随着在网时长的增加而逐渐减少,在网时间越长,说明客户黏性越大,更不容易流失。此外,在网的第20个月是客户流失与否的分水岭。在20个月之前,尤其是前3个月,客户有着较高的流失率,因此应当在入网的前三个月尽可能让新客户感受到业务的价值所在。而在第20个月以后,客户的流失率越来越低,此时公司已经拥有了稳定的客户群。

    image-20210703162037965

    从以上箱型图也可以看出,在网时间越长的客户,流失的可能性越低。 大多数未流失客户的在网时长都在16个月以上。客户的在网时长超过30个月的,其流失的可能性则比较小

    还能观察到,有个别在网时间很长的客户也已经流失,这反映出公司对忠诚客户的关怀不足。

    这里将在网时长作为整个客户生命。根据描述性统计,所有客户的平均生命周期大约为32个月,大多数客户的生命周期集中在8-56个月之间。画出所有客户的生命周期直方图

    image-20210703162316946

    这是双峰趋势图。可以观察到,大量客户的生命周期集中在1-3个月。再一次证明,应当在前3个月尽量引导客户进行消费。此外,高质量的客户集中在67个月之后,他们属于忠诚用户。

    4.3.5月度与年度费用

    image-20210703162730120

    画出月租费的区间分布直方图。从图上可以直观地看到所有客户的月租费分布情况,以及每个费用区间的客户流失情况。显然,月租费在70-100元内的客户更容易流失。画出月租费在这一区间的客户流失率柱形图

    image-20210703163053498

    计算得到,月租费在70-80元的客户流失率为39.82%,80-90元的客户流失率为36.12%,90-100元的客户流失率为37.80%。对于这部分客户,可以通过调研形式询问流失原因,若是由于月费价格高昂,则可以通过优惠券或减免形式对这一区间的在网客户进行补贴,以降低流失率

    image-20210703163406996 image-20210703163259203

    同样画出总费用的区间分布直方图,可以看到,流失率随着总费用的增长而不断降低,低消费人群更容易流失,总费用在2000元以内的客户的流失率最高,达到31.98%。这再一次证明,引导客户提高消费额度、延长客户合同存续期是提高客户留存、减少流失的不二之选。

    4.3.6小结

    • 客户的消费行为背后隐藏着他们的消费偏好,通过数据发现,客户更习惯纸质的账单形式和自动转账的支付方式,而使用电子账单和电子支付的客户的流失率则比较高
    • 客户在网时间越长,总费用越高,流失率越低,客户黏性越大。月租费在70-100元间的客户、总费用低于2000元的客户更容易流失。易流失客户的生命周期通常为1-3个月,而生命周期达到67个月的则为高度忠诚用户,对这两种客户应当采取不同的留存和维系策略
    • 延长客户的合同期限,推动客户从月签转向年签,引导客户提高消费额度,是提高留存的重要策略

    5.流失预警模型构建

    5.1 特征工程

    根据上述分析,本文选取了以下17个与流失率较为相关的特征

    df = data.loc[:,['SeniorCitizen','Partner','Dependents','tenure','PaperlessBilling','Contract','PaymentMethod'
                     ,'InternetService','OnlineSecurity','OnlineBackup','DeviceProtection','StreamingTV','StreamingMovies'
                     ,'TechSupport','MonthlyCharges','TotalCharges','Churn']]
    

    创造新特征Families

    df['P_trans'] = df['Partner'].map({'Yes':1,'No':0})
    df['D_trans'] = df['Dependents'].map({'Yes':1,'No':0})
    df['F_trans'] = df['P_trans'] + df['D_trans']
    df['Families'] = df['F_trans'].apply(lambda x: 0 if x==0 else 1)
    

    对分类型变量进行0-1编码处理

    columns = ['PaperlessBilling','OnlineSecurity','OnlineBackup','DeviceProtection'
              ,'TechSupport','StreamingTV','StreamingMovies','']
    for i in columns:
        df[i] = df[i].apply(lambda x:1 if x=='Yes' else 0)
    

    对标签进行编码处理

    from sklearn.preprocessing import LabelEncoder
    df['Churn'] = LabelEncoder().fit_transform(df['Churn'])
    

    对连续性变量进行分箱处理

    from sklearn.preprocessing import OrdinalEncoder
    for i in ['tenure','MonthlyCharges','TotalCharges']:
        df[i] = pd.cut(df[i],bins=10,right=False)
        df[i] = df[i].astype(str)
        df[i] = OrdinalEncoder().fit_transform(df[i][:,np.newaxis])
    

    删除相关虚拟变量,减少多重共线性

    df = df.drop(columns=['Contract_Month-to-month','PaymentMethod_Mailed check','InternetService_No','Partner'
    		    ,'Dependents','P_trans','D_trans','F_trans'])
    

    5.2 模型构建

    划分测试集与训练集

    x = df.drop(columns=['Churn'])
    y = df['Churn']
    from sklearn.model_selection import train_test_split
    xtrain,xtest,ytrain,ytest = train_test_split(x,y,train_size=0.8,random_state=0)
    

    使用逻辑回归进行建模

    from sklearn.linear_model import LogisticRegression
    lr = LogisticRegression().fit(xtrain,ytrain)
    y_prod = lr.predict_proba(xtest)[:,1]
    y_predict = lr.predict(xtest)
    

    查看score与auc值

    from sklearn.metrics import roc_curve,auc,accuracy_score
    fpr_lr,tpr_lr,threshold_lr = roc_curve(ytest,y_prod)
    auc_lr = auc(fpr_lr,tpr_lr)
    score_lr = accuracy_score(ytest,y_predict)
    print('准确率为{0},AUC值为{1}'.format(score_lr,auc_lr))
    
    准确率为0.7856635911994322,AUC值为0.8160918640103579
    

    通过建立流失预警模型,可以提前预判哪些客户存在流失风险,并及时采取措施对这些客户进行挽留。相比流失后召回,流失预警的成本低、召回难度也相对小。

    6.结论与建议

    1.根据客户个人属性分群制定策略
    从数据来看,老年客户很可能因为在手机服务使用上存在一定困难而流失,而技术支持这项附加业务则可以有效改善这种状况,因此可以向老年客户推出包含电话、互联网和技术支持业务的温暖套餐,承诺定期线上回访。此外,还可以开通老年客户电信服务专线,将“送温暖”服务理念深入人心,进一步拉近与老年客户群之间的距离,提高留存率,也有利于吸纳更多的老年客户。

    对于有亲属的客户,可以推出包含主要业务和所有附加业务的家庭年费套餐,给予一定的折扣优惠或者免费赠送三个月的影视会员,针对网络电视和电影服务,承诺在合同期限内提供免费的技术升级。也可以开发仅限配偶和子女之间的“亲人圈”业务,合同以年计,同城的亲属之间通话免费,以此作为“亲人圈”的卖点,吸引客户的亲属入网。

    针对地点变动比较频繁的客户,推出“全国通”电话和流量套餐,力求减少这种不必要的流失。

    2.设立客户忠诚计划,引入积分和会员体系
    引入积分制度,建立积分商城。客户的任意电信消费都可以等额地兑换成积分,积累到地一定额度即可在积分商城内兑换虚拟或实物礼品,商城内也可以设置轮盘抽奖活动,增加趣味性、积分每年年底清零,公司也要定期向客户发送短信提醒其完成积分兑换。

    建立会员体系,进行差异化的运营和管理。客户连续使用本公司电信服务达到三个月即自动入会,成为普通会员。再根据总消费金额向上划分会员等级,例如铂金、黄金、钻石会员等等。同时要为不同等级的会员,配置不同层次的权益。比如专属客服、积分翻倍、赠送节日礼包、纪念品等等。

    3.吸纳新客户,维系老客户,召回流失客户

    • 新客策略
      根据图表分析,新客户在入网的前三个月最容易流失,而在短时间内留存客户的最有效手段就是让客户迅速感受并认同业务的价值。通过分析发现,电信附加业务可以在一定程度上弥补基础业务的不足。
      因此,对于新客户,除了入网即赠送消费券、给予折扣等常规促销手段,还可以开展附加业务首月免费体验活动。让新客户通过体验增值服务,感受到本公司电信业务的便捷、快捷与安全性。另外可以规定,新客户签订的合同期限越长,其获得的折扣和福利也会越多。因为比起说服老用户转变月签方式,培养新的长约客户显然来得更加高效。
      此外,开展老带新活动也是吸纳新客户的重要手段。经老客户推荐来的新客可以享受折上折,新客入网后老客即可获得积分,且拉新人数越多,积分越多。
    • 老客策略
      根据前文分析,在网时长超过20个月即可视为忠诚客户。对于忠诚客户,要定期维系和关怀,比如向客户发送生日短信、节日礼等等,忠诚客户的个性化需求也要满足。此外,也要定期对忠诚客户进行线上和线下回访,开展客户满意度调研,在优化当前业务的同时,发掘新的业务需求点。
    • 流失客户策略
      对于普通流失客户,可以通过批量发送短信或邮件的方式召回,内容中注明重新入网可享受的折扣优惠以及优惠的时限。
      对于高价值流失客户,可以通过电话回访、赠送伴手礼的方式进行召回。此外,也可以通过举办优惠活动大面积召回流失客户。

    4.提高业务服务质量,精准定位客户需求
    从客户的消费行为数据来看,多数客户尚未习惯使用电子支付,他们更倾向于传统的纸质账单形式和支票、银行卡的付款方式。因此要大力推荐并引导新老客户使用这两种方式,进而延长客户的存续期。

    从电信业务数据来看,目前互联网业务尤其是光纤方面仍然存在缺陷,对其进行技术改造和升级迫在眉睫。

    此外,开通在线安全、在线备份等附加业务对流失率的有效降低反映出客户对安全、便捷的业务的日益增长的需求。但在目前来说,开通附加业务的客户数量还比较少,因此要向客户大力宣传推广这些业务,唤醒他们的需求意识。

    展开全文
  • 【原创】Python中用PyTorch机器学习分类预测银行客户流失模型 数据分析报告论文(代码数据).docx
  • 银行客户流失预测

    2017-07-19 21:45:00
    针对银行客户流失预测,主要流程分为:特征预处理、特征选择,分类模型选择与训练。主要工作如下: 1:特征预处理与选择 对性别进行哑变量处理; 对是否有****信息将布尔值转换01表示; 画出年龄直方图可以看出...

    针对银行客户流失预测,主要流程分为:特征预处理、特征选择,分类模型选择与训练。主要工作如下:

    1:特征预处理与选择

    对性别进行哑变量处理;

    对是否有****信息将布尔值转换01表示;

    画出年龄直方图可以看出大致呈正态分布,对年龄分段处理后缺失值采用插补方式;

    资产当前总额=存储类资产当前总额=本币存储当前总金额   月日均余额=存储类资产月日均余额=本币存储月日均余额  分别删除其中两项;

    针对*NUM,*DUR,*AMT,*BAL字段分别进行特征提取(SelectKBest)达到降维效果;

    最后整合数据,特征标准化处理最终为44个特征(StandardScaler)。

      2:分类模型选择与训练

    数据集划分:采用K折交叉验证,train_test_split自主切分数据集

    模型选择:采用了决策树,提升树(GBDT/XGBoost),SVM(libsvm)神经网络(多层感知器算法)分别训练模型

    3:对应python主要代码:

    • decisiontree.py

    from sklearn.metrics import accuracy_score,precision_score,recall_score,f1_score
    
    X_train,X_test,y_train,y_test=train_test_split(StS,y,test_size=0.4,random_state=0)
    clf = tree.DecisionTreeClassifier()
    clf = clf.fit(X_train, y_train)
    pre_labels = clf.predict(X_test)
    print('accuracy score:',accuracy_score(y_test,pre_labels,normalize=True))
    print('recall score:',recall_score(y_test,pre_labels))
    print('precision score:',precision_score(y_test,pre_labels))
    print('f1  score:',f1_score(y_test,pre_labels))
    • XGBoost.py
    import xgboost as xgb
    from sklearn.preprocessing import StandardScaler
    #记录程序运行时间
    import time
    start_time = time.time()
    from xgboost.sklearn import XGBClassifier
    from sklearn.model_selection import train_test_split
    from sklearn.metrics import accuracy_score,precision_score,recall_score,f1_score,classification_report,roc_auc_score
    bankChurn = pd.read_csv('D:/work/lost data and dictionary/test/bankChurn.csv')#原始数据
    bankChurn_data = pd.read_csv('D:/work/lost data and dictionary/test/bankChurn_data.csv')#预处理数据
    Y_train=bankChurn['CHUR0_CUST_I0D']#标签
    StS=StandardScaler().fit_transform(bankChurn_data)
    X_train,X_test,y_train,y_test=train_test_split(StS,Y_train,test_size=0.4,random_state=None)
    print(X_train.shape, X_test.shape)
    #模型参数设置
    xlf = xgb.XGBClassifier(max_depth=10,
                            learning_rate=0.1,
                            n_estimators=10,
                            silent=True,
                            objective='binary:logistic',
                            nthread=-1,
                            gamma=0,
                            min_child_weight=1,
                            max_delta_step=0,
                            subsample=0.85,
                            colsample_bytree=0.7,
                            colsample_bylevel=1,
                            reg_alpha=0,
                            reg_lambda=1,
                            scale_pos_weight=1,#这个值是因为类别十分不平衡。
                            seed=1440)
    
    xlf.fit(X_train, y_train, eval_metric='error', verbose = True, eval_set = [(X_test, y_test)],early_stopping_rounds=100)
    # 计算 auc 分数、预测
    preds = xlf.predict(X_test)
    pre_pro = xlf.predict_proba(X_test)[:,1]
    print('accuracy score:',accuracy_score(y_test,preds ,normalize=True))
    print('classification report:',classification_report(y_test,preds ))
    print('precision score:',precision_score(y_test,preds ))
    print('roc_auc_score:%f' % roc_auc_score(y_test,pre_pro))
    #输出运行时长
    cost_time = time.time()-start_time
    print("xgboost success!",'\n',"cost time:",cost_time,"(s)......")
    • libsvm.py

    import os
    os.chdir('C:\libsvm-2.81\python')
    from svmutil import *
    from sklearn.metrics import accuracy_score,classification_report
    y,x=svm_read_problem('bankchurnLibsvm.txt')#转换成libsvm格式
    # print(type(x))
    x=np.array(x)
    y=np.array(y)
    stratified_folder=StratifiedKFold(n_splits=4,random_state=0,shuffle=True)
    for train_index,test_index in stratified_folder.split(x,y):
        print('shuffled train index:',train_index)
        print('shuffled test index:', test_index)
        print('shuffled x_train:', x[train_index])
        print('shuffled x_test:', x[test_index])
        print('shuffled y_train:', y[train_index])
        print('shuffled y_test:', y[test_index])
        print('.......')
    y_train=list(y[train_index])
    y_test=list(y[test_index])
    x_train=list(x[train_index])
    x_test=list(x[test_index])
    m=svm_train( y_train,x_train,'-c 4  -g 2')
    p_label,p_acc,p_val=svm_predict(y_test,x_test,m)
    print('accuracy score:',accuracy_score(y_test,p_label ,normalize=True))
    print('classification report:',classification_report(y_test,p_label ))
    • BPtest

      import pandas as pd
      import numpy as np
      from sklearn.model_selection import cross_val_score
      from sklearn.neural_network import MLPClassifier
      from sklearn.metrics import accuracy_score,roc_auc_score
      from sklearn.metrics import accuracy_score,precision_score,recall_score,f1_score,classification_report
      bankChurn = pd.read_csv('D:/work/lost data and dictionary/test/bankChurn.csv')
      X_data = pd.read_csv('D:/work/lost data and dictionary/test/bankChurn_data.csv')
      X_data=X_data.values[:,:]
      Y_label=bankChurn['CHUR0_CUST_I0D']
      Y_label=Y_label.values[:]
      data=np.hstack((X_data,Y_label.reshape(Y_label.size,1)))##将样本集与标签合并
      np.random.shuffle(data)##混洗数据
      X=data[:,:-1]
      Y=data[:,-1]
      train_x=X[:-8620]
      test_x=X[-8620:]
      train_y=Y[:-8620]
      test_y=Y[-8620:]#数据5:5
      ######mlpclassifier_data():###多层感知机算法,BP算法
      classifier=MLPClassifier(hidden_layer_sizes=(30,),activation='logistic',max_iter=1000)
      clf=classifier.fit(train_x,train_y)
      train_score=classifier.score(train_x,train_y)
      test_score=classifier.score(test_x,test_y)
      print('train_score:',train_score)
      print('test_score:',test_score)
      ####得到其他分类效果####
      pre_labels = clf.predict(test_x)
      pre_pro = clf.predict_proba(test_x)[:,1]
      print('accuracy score:',accuracy_score(test_y,pre_labels,normalize=True))
      print('recall score:',recall_score(test_y,pre_labels))
      print('classification report:',classification_report(test_y,pre_labels))
      print('precision score:',precision_score(test_y,pre_labels))
      print('f1  score:',f1_score(test_y,pre_labels))
      print('roc_auc_score:%f' % roc_auc_score(test_y,pre_pro))

      运行结果比较:
        DT XGBoost Libsvm BP
      Accuracy 0.856 0.91 0.894 0.90
      Precision 0.86 0.89 0.84 0.88
      Recall 0.86 0.91 0.89 0.90
      F1 score 0.86 0.89 0.85 0.87

       

     

    转载于:https://www.cnblogs.com/xyd134/p/7208404.html

    展开全文
  • 某家银行发现进来他们的客户流失比较严重,处于业务需要,现在他们将收集的客户资料,希望通过分析得到未来一段时间内客户断掉跟这家银行的业务来往的可能性,从而做出适当的挽留措施。 现在他们收集了客户的ID、...
  • 2. 从用户生命周期分析客户流失的可能影响因素 5 3. 确定初选变量 6 (二) 预测变量与目标变量的相关性 7 1. 分类变量的直方图检验 7 2. 分类变量的卡方检验 11 3. 连续变量与目标变量的相关性...
  • 流失预警模型(二分类),代码原型为本人在某银行做的流失模型,AUC:83%、召回率:19.4%,精确率:85%(数据是外部数据/代码已脱敏) 你将习得:数据的处理、LightGBM、sklearn包(里面含有:GridSearchCV寻找最优...
  • 数据挖掘技术下的银行客户流失决策树预测算法.pdf
  • 银行流失用户分析及预测模型

    万次阅读 多人点赞 2018-08-24 22:20:08
    银行客户流失是指银行的客户终止在该行的所有业务,并销号。但在实际运营中,对于具体业务部门,银行客户流失可以定位为特定的业务终止行为。 商业银行的客户流失较为严重,流失率可达20%。而获得新客的成本是维护...
  • 文章目录引言一、银行业客户群体与产品的类别二、客户流失预警模型的业务意义三、数据介绍与描述1.单因子分析之连续变量2.单因子分析之类别变量3.多因子分析 引言 下面使用的是jupyter notebook,完整代码解析见...
  • 分类问题属于机器学习...在本文中,鉴于银行客户的某些特征,我们将预测客户在6个月后是否可能离开银行。客户离开组织的现象也称为客户流失。因此,我们的任务是根据各种客户特征预测客户流失。 $ pip ins...
  • Churn-Prediction-of-Bank-Customers:预测银行客户流失
  • 电信客户流失数据分析

    千次阅读 2020-08-21 14:24:32
    这里写自定义目录标题电信客户流失数据分析研究背景提出问题数据集描述特征工程1、数据预处理1.1、特征类型处理1.2、缺失值处理1.3、异常值处理1.4、分类变量标签整理2、特征选择2.1、方差过滤2.2、卡方检验过滤2.3...
  • 基于数据挖掘的客户流失分析案例

    千次阅读 2015-12-05 22:08:41
    要做客户挽留就需要对客户流失进行预警、客户流失原因分析、客户满意度或忠诚度研究、客户生命周期研究等相关问题进行深入而全面的分析。例如,对客户的行为特征进行分析,可以了解有多少客户流失,客户是什么时候...
  • 银行客户数据进行数据分析,以预测客户流失的原因,并提供防止这种情况的建议。 数据集:Churn_Modelling.csv 记录数:10,000 功能数量:14 使用的图书馆:pandas,matplotlib,numpy,seaborn
  • 基于分类方法的银行客户流失预测

    千次阅读 2020-08-16 19:12:19
    技术栈:Excel、SPSS、SQL SERVER BI
  • 总体流失分析3.2.用户属性分析3.3.服务属性分析3.4.合同属性分析4.高流失率用户画像5.结论和建议 1.数据集说明 每一行代表一个客户,每一列包含列元数据中描述的客户属性。原始数据包含7043行(客户)和21列(特性)。...
  • 本行业案例课程为Python 3 数据分析系列课程的行业案例部分,学员请务必先观看课程介绍免费视频,确认已学习本课程所需Python分析技能。2. 本课程的核心目的是协助学员学习具体业务场景下的解决方案,为降低学员...
  • 项目描述: ...将前36列作为自变量想,37列的是否流失作为因变量y,并把原始数据按4:1分成训练集和测试集,再从sklearn.linear_model导入LogisticRegression模型,然后用fit(x,y)方法来拟合。用lr.s...
  • 通过分析银行客户流失的原因,提出了建立客户流失预测模型的方法。利用模型,发现预测流失 群体,预测流失趋势,进而制定有效的控制策略,最大限度地降低客户流失率。为客户流失预测提供了一种新的研究思路和分析...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,578
精华内容 1,431
关键字:

银行客户流失分析