精华内容
下载资源
问答
  • 那么,如何有效的减少客户流失率的问题,就成了很多企业都想要解决的难题。 针对这一问题,CRM客户管理系统就能很有效降低客户流失率?下面我们一起来了解下吧! 1、客户信息储存 销售人员自己获得的客户资源、公司...

    客户是企业盈利的来源,对于客户资源的竞争在逐渐增加,但是很多企业都面临着客户流失的问题。

    对于企业客户而言,有一定的的流失率是必然的。那么,如何有效的减少客户流失率的问题,就成了很多企业都想要解决的难题。

    针对这一问题,CRM客户管理系统就能很有效降低客户流失率?下面我们一起来了解下吧!

    1、客户信息储存

    销售人员自己获得的客户资源、公司营销活动获得的客户资源以及官网访问获得的客户资源,都被录入在CRM客户管理系统之中,存放在终端数据库。不仅可以让管理员方便快速的查看客户的信息,而且还有效的防止客户信息的遗漏、丢失等安全问题。即使员工离职,客户信息还是在系统之中,后续接替的销售人员也能继续跟进。

    2、客户关系维护

    CRM客户管理系统在售前、售中、售后,也就是从前期客户的开发到后期客户的维护都有一套完整的流程管理,可有效避免客户的流失。系统时刻关注客户动态,对客户反馈的问题进行记录,并且相关人员可以对客户反馈的问题进行量化的处理,再根据客户反馈问题的类型以及难易程度自动分配给相关的负责人。

    3、人员权限设置

    企业管理员拥有CRM客户管理系统的最高权限,系统也可设置分级管理的权限,比如可以让某些业务人员拥有查看、编辑和删除的权限,这些都可以在系统的管理权限中进行设置,这样在很大程度上可以有效保障客户数据的安全性。

    企业上线CRM系统,通过系统存放客户的资料,可以有效保障客户数据的安全性,同时避免销售独自占有客户资源,更好的分配客户资源,很好的解决了客户流失的难题,实现高效率的管理企业客户资源。

    展开全文
  • 客户流失率是评估不断增长的业务的最重要指标之一。 虽然这不是最幸福的衡量标准,但它可以使您的公司了解保留客户的事实。 Heroku应用程序My Heroku应用程序: : 生存分析 生存分析是统计的一个分支,用于分析直到...
  • 企业在业务拓展过程中,最为头疼的即为降低“客户流失率”。即便用户与企业已经进行过多次互动,依然会在不同时刻,毫无缘由的默默离开。而用户的离开真的是“毫无缘由”吗?下面来看看品牌EDM服务商Focussend就...

    企业在业务拓展过程中,最为头疼的即为降低“客户流失率”。即便用户与企业已经进行过多次互动,依然会在不同时刻,毫无缘由的默默离开。而用户的离开真的是“毫无缘由”吗?下面来看看品牌EDM服务商Focussend就“客户流失”如何快速补救,进行五步法剖析,有效降低客户流失率,提高忠诚度。

    一.用户激励强度升级,刺激用户持续活跃

    用户的长期购买建立于与品牌的持续积极互动。通过短信服务、邮件服务,对客户进行正激励,推送VIP升级、积分余额兑换、免费福利等,增强客户活跃度。

     

    二.  明确客户流失预警指标,高效识别“危险客户”

    越早与处于“危险边缘”客户互动,再次激活可能性越高。所以识别用户流失风险点,设定一个健全的识别体系尤为重要。

    1. 通过邮件、短信发送报告,找出点击率下降、互动减少情况客户。
    2. 通过分析生命周期里的里程碑事件,找准“危险”节点。例如客户会员即将到期,潜在的未续约风险。
    3. 账户支付或充值等行为迟缓、推延。

    三.以多样化调查问卷,及时收集用户反馈

    保持与活跃用户的密切联系至关重要,通过日常问卷调查,往往能更快更准地找到客户流失原因。如通过发送生日祝福等服务类邮件,同时可以附上一张愿望清单供客户填写,以此来收集客户反馈,及时调整。

    四.搭建预测模型,活跃度下降迅速出击

    对短信营销、邮件营销中,浏览率、频次等设定相应基数,当用户指标低于即有行为轨迹时,迅速触发一封新品推荐、或促销活动邮件,同时发送一条营销短信,提高阅读率。

    如对打折敏感客户,浏览率下降,系统自动触发一封近期优惠邮件,同时搭配促销短信,进行二次激活。 

    五.提高用户参与度,增强产品粘性。

    很多用户的流失是因为缺乏与自己个性息息相关的产品信息,感知不到产品对自己的价值所在。将产品优势与用户痛点的完美契合,才能真正达到共赢状态。 

    如针对客户对于短信营销、邮件营销的需求,Focussend推出万元短信送万元邮件,有效降低用户营销成本;而客户对于短信营销不知如何策划问题,也有免费的万元服务礼包,帮助客户从前期策划到后期发送报告分析进行一站式服务。

    转载于:https://www.cnblogs.com/EDM-DATA/p/6039576.html

    展开全文
  • 1 定义问题 客户流失率问题是电信运营商面临得一项重要课题,也是一个较为流行的案例。根据测算,招揽新的客户比保留住既... 本文希望通过一个公开数据的客户流失率问题分析,能够带着大家理解如何应用机器学...
    
    本文转自:https://my.oschina.net/sizhe/blog/1594791
    
    

    1 定义问题

    客户流失率问题是电信运营商面临得一项重要课题,也是一个较为流行的案例。根据测算,招揽新的客户比保留住既有客户的花费大得多(通常5-20倍的差距)。因此,如何保留住现在的客户对运营商而言是一项非常有意义的事情。 本文希望通过一个公开数据的客户流失率问题分析,能够带着大家理解如何应用机器学习预测算法到实际应用中。

    当然, 实际的场景比本文例子复杂的多,如果想具体应用到项目, 还需要针对不同的场景和数据进行具体的分析。

    从机器学习的分类来讲, 这是一个监督问题中的分类问题。 具体来说, 是一个二分类问题。 所有的数据中包括一些特征, 最后就是它的分类:流失或者在网。接下来我们就开始具体的处理。

    2 分析数据

    首先我们来导入数据, 然后查看数据的基本情况。

    2.1 数据导入

    通过pandas来导入csv, 然后我们来查看一下数据的基本情况

    from __future__ import division
    import pandas as pd
    import numpy as np
    
    ds = pd.read_csv('./churn.csv')
    col_names = ds.columns.tolist()
    print "Column names:"
    print col_names
    print(ds.shape)
    

    输出:

    Column names:
    ['State', 'Account Length', 'Area Code', 'Phone', "Int'l Plan", 'VMail Plan', 'VMail Message', 'Day Mins', 'Day Calls', 'Day Charge', 'Eve Mins', 'Eve Calls', 'Eve Charge', 'Night Mins', 'Night Calls', 'Night Charge', 'Intl Mins', 'Intl Calls', 'Intl Charge', 'CustServ Calls', 'Churn?']
    (3333, 21)
    

    可以看到, 整个数据集有3333条数据, 20个维度, 最后一项是分类。

    2.2 基本信息以及类型

    我们可以打印一些数据, 对数据和取值有一个基本的理解。

    peek = data.head(5)
    print(peek)
    

    输出:

       State  Account Length  Area Code     Phone Int'l Plan VMail Plan  \
    0     KS             128        415  382-4657         no        yes   
    1     OH             107        415  371-7191         no        yes   
    2     NJ             137        415  358-1921         no         no   
    3     OH              84        408  375-9999        yes         no   
    4     OK              75        415  330-6626        yes         no   
        Eve Charge  Night Mins  Night Calls  Night Charge  Intl Mins  Intl Calls  \
    0        16.78       244.7           91         11.01       10.0           3   
    1        16.62       254.4          103         11.45       13.7           3   
    2        10.30       162.6          104          7.32       12.2           5   
    3         5.26       196.9           89          8.86        6.6           7   
    4        12.61       186.9          121          8.41       10.1           3   
        Intl Charge  CustServ Calls  Churn?  
    0          2.70               1  False.  
    1          3.70               1  False.  
    2          3.29               0  False.  
    3          1.78               2  False.  
    4          2.73               3  False.  
    

    我们可以看到, 数据集有20项特征,分别是州名, 账户长度, 区号, 电话号码, 国际计划,语音邮箱, 白天通话分钟数, 白天电话个数, 白天收费, 晚间通话分钟数,晚间电话个数, 晚间收费, 夜间通话分钟数,夜间电话个数, 夜间收费, 国际分钟数, 国际电话个数, 国际收费, 客服电话数,流失与否。

    1. 可以看到这里面有个人信息,应该可以看到有些信息与流失与否关系不大。 州名, 区号可以指明客户的位置, 和流失有关系么, 不知道, 具体位置如果不分类, 应该完全没有关系。 而州名, 也许某个州有了某个强劲的竞争对手? 这也是瞎猜, 暂时意义不大, 删除。
    2. 账号长度, 电话号码, 不需要
    3. 国际计划, 语音邮箱。 可能有关系, 先留着吧。
    4. 分别统计了白天, 晚间, 夜间的通话分钟, 电话个数, 收费情况。 这是重要信息保留
    5. 客服电话, 客户打电话投诉多那流失率可能会大。 这个是重要信息保留。
    6. 流失与否。 这是分类结果。

    然后我们可以看一下数据的类型, 如下:

    ds.info()
    

    输出:

    RangeIndex: 3333 entries, 0 to 3332
    Data columns (total 21 columns):
    State             3333 non-null object
    Account Length    3333 non-null int64
    Area Code         3333 non-null int64
    Phone             3333 non-null object
    Int'l Plan        3333 non-null object
    VMail Plan        3333 non-null object
    VMail Message     3333 non-null int64
    Day Mins          3333 non-null float64
    Day Calls         3333 non-null int64
    Day Charge        3333 non-null float64
    Eve Mins          3333 non-null float64
    Eve Calls         3333 non-null int64
    Eve Charge        3333 non-null float64
    Night Mins        3333 non-null float64
    Night Calls       3333 non-null int64
    Night Charge      3333 non-null float64
    Intl Mins         3333 non-null float64
    Intl Calls        3333 non-null int64
    Intl Charge       3333 non-null float64
    CustServ Calls    3333 non-null int64
    Churn?            3333 non-null object
    dtypes: float64(8), int64(8), object(5)
    memory usage: 546.9+ KB
    

    看见, 有int, float, object。 对于不是数据型的数据, 后面除非决策树等算法, 否则应该会转化成数据行。 所以我们把churn? 结果转化, 以及"Int’l Plan",“VMail Plan”, 这两个参数只有yes, no 两种, 所以也进行转化成01值。

    2.3 描述性统计

    describe() 可以返回具体的结果, 对于每一列。

    数量 平均值 标准差 25% 分位 50% 分位数 75% 分位数 最大值 很多时候你可以得到NA的数量和比例。

    TODO 对于非数据性的是没有返回的的

     Account Length    Area Code  VMail Message     Day Mins    Day Calls  \
    count     3333.000000  3333.000000    3333.000000  3333.000000  3333.000000   
    mean       101.064806   437.182418       8.099010   179.775098   100.435644   
    std         39.822106    42.371290      13.688365    54.467389    20.069084   
    min          1.000000   408.000000       0.000000     0.000000     0.000000   
    25%         74.000000   408.000000       0.000000   143.700000    87.000000   
    50%        101.000000   415.000000       0.000000   179.400000   101.000000   
    75%        127.000000   510.000000      20.000000   216.400000   114.000000   
    max        243.000000   510.000000      51.000000   350.800000   165.000000   
    
            Day Charge     Eve Mins    Eve Calls   Eve Charge   Night Mins  \
    count  3333.000000  3333.000000  3333.000000  3333.000000  3333.000000   
    mean     30.562307   200.980348   100.114311    17.083540   200.872037   
    std       9.259435    50.713844    19.922625     4.310668    50.573847   
    min       0.000000     0.000000     0.000000     0.000000    23.200000   
    25%      24.430000   166.600000    87.000000    14.160000   167.000000   
    50%      30.500000   201.400000   100.000000    17.120000   201.200000   
    75%      36.790000   235.300000   114.000000    20.000000   235.300000   
    max      59.640000   363.700000   170.000000    30.910000   395.000000   
    
           Night Calls  Night Charge    Intl Mins   Intl Calls  Intl Charge  \
    count  3333.000000   3333.000000  3333.000000  3333.000000  3333.000000   
    mean    100.107711      9.039325    10.237294     4.479448     2.764581   
    std      19.568609      2.275873     2.791840     2.461214     0.753773   
    min      33.000000      1.040000     0.000000     0.000000     0.000000   
    25%      87.000000      7.520000     8.500000     3.000000     2.300000   
    50%     100.000000      9.050000    10.300000     4.000000     2.780000   
    75%     113.000000     10.590000    12.100000     6.000000     3.270000   
    max     175.000000     17.770000    20.000000    20.000000     5.400000   
    
           CustServ Calls  
    count     3333.000000  
    mean         1.562856  
    std          1.315491  
    min          0.000000  
    25%          1.000000  
    50%          1.000000  
    75%          2.000000  
    max          9.000000  
    

    2.4 图形化理解你的数据

    之前的一些信息, 只是一些很初步的理解, 但是对于机器学习算法来讲是不够的。 下面我们从几个维度去进一步理解你的数据。工具可以用数字表格, 也可以用图形(matplotlib) 这里画图较多。

    1. 特征自己的信息
    2. 特征和分类之间的关系
    3. 特征和特征之间的关系 – 这里鉴于时间的关系, 有些关系并没有直接应用于算法本身, 但是在进一步的算法提升中是很有意义的, 这里更多的是一种展示。

    2.4.1 特征本身的信息

    我们先来看一下流失比例, 以及关于打客户电话的个数分布

    import matplotlib.pyplot as plt
    %matplotlib inline
    fig = plt.figure()
    fig.set(alpha=0.2)  # 设定图表颜色alpha参数
    
    plt.subplot2grid((2,3),(0,0))             # 在一张大图里分列几个小图
    ds['Churn?'].value_counts().plot(kind='bar')# plots a bar graph of those who surived vs those who did not. 
    plt.title(u"stat for churn") # puts a title on our graph
    plt.ylabel(u"number")  
    
    plt.subplot2grid((2,3),(0,2))            
    ds['CustServ Calls'].value_counts().plot(kind='bar')# plots a bar graph of those who surived vs those who did not. 
    plt.title(u"stat for cusServCalls") # puts a title on our graph
    plt.ylabel(u"number")  
    
    plt.show()
    

    在这里插入图片描述
    很容易理解。

    然后呢, 我们的数据的特点是对白天, 晚上, 夜间,国际都有分钟数, 电话数, 收费三种维度。 那么我们拿白天的来举例。

    import matplotlib.pyplot as plt
    %matplotlib inline
    fig = plt.figure()
    fig.set(alpha=0.2)  # 设定图表颜色alpha参数
    
    plt.subplot2grid((2,5),(0,0))             # 在一张大图里分列几个小图
    ds['Day Mins'].plot(kind='kde')    # plots a kernel desnsity estimate of customer 
    plt.xlabel(u"Mins")# plots an axis lable
    plt.ylabel(u"density") 
    plt.title(u"dis for day mins")
    
    plt.subplot2grid((2,5),(0,2))            
    ds['Day Calls'].plot(kind='kde')    # plots a kernel desnsity estimate of customer 
    plt.xlabel(u"call")# plots an axis lable
    plt.ylabel(u"density") 
    plt.title(u"dis for day calls")
    
    plt.subplot2grid((2,5),(0,4))           
    ds['Day Charge'].plot(kind='kde')   # plots a kernel desnsity estimate of customer 
    plt.xlabel(u"Charge")# plots an axis lable
    plt.ylabel(u"density") 
    plt.title(u"dis for day charge")
    
    plt.show()
    

    在这里插入图片描述
    可以看到分布基本上都是高斯分布, 这也符合我们的预期, 而高斯分布对于我们后续的一些算法处理是个好消息。

    2.4.2 特征和分类的关联

    我们来看一下一些特征和分类之间的关联。 比如下面int plan

    import matplotlib.pyplot as plt
    fig = plt.figure()
    fig.set(alpha=0.2)  # 设定图表颜色alpha参数
    
    int_yes = ds['Churn?'][ds['Int\'l Plan'] == 'yes'].value_counts()
    int_no = ds['Churn?'][ds['Int\'l Plan'] == 'no'].value_counts()
    df_int=pd.DataFrame({u'int plan':int_yes, u'no int plan':int_no})
    df_int.plot(kind='bar', stacked=True)
    plt.title(u"statistic between int plan and churn")
    plt.xlabel(u"int or not") 
    plt.ylabel(u"number")
    
    plt.show()
    

    在这里插入图片描述
    我们可以看到, 有国际电话的流失率较高。 猜测也许他们有更多的选择, 或者对服务有更多的要求。 需要特别对待。 也许你需要电话多收集一下意见了。

    再来看一下

    #查看客户服务电话和结果的关联
    fig = plt.figure()
    fig.set(alpha=0.2)  # 设定图表颜色alpha参数
    
    cus_0 = ds['CustServ Calls'][ds['Churn?'] == 'False.'].value_counts()
    cus_1 = ds['CustServ Calls'][ds['Churn?'] == 'True.'].value_counts()
    df=pd.DataFrame({u'churn':cus_1, u'retain':cus_0})
    df.plot(kind='bar', stacked=True)
    plt.title(u"Static between customer service call and churn")
    plt.xlabel(u"Call service") 
    plt.ylabel(u"Num") 
    
    plt.show()
    

    在这里插入图片描述
    基本上可以看出, 打客户电话的多少和最终的分类是强相关的, 打电话3次以上的流失率比例急速升高。 这是一个非常关键的指标。

    3 准备数据

    好的, 我们已经看了很多,对数据有了一定的理解。 下面我们开始具体对数据进行操作。

    3.1 去除无关列

    首先, 根据对问题的分析, 我们做第一件事情, 去除三列无关列。 州名, 电话, 区号。

    我们和下一步一起做

    3.2 转化成数值类型

    对于有些特征, 本身不是数值类型的, 这些数据是不能被算法直接使用的, 所以我们来处理一下

    # Isolate target data
    ds_result = ds['Churn?']
    Y = np.where(ds_result == 'True.',1,0)
    
    dummies_int = pd.get_dummies(ds['Int\'l Plan'], prefix='_int\'l Plan')
    dummies_voice = pd.get_dummies(ds['VMail Plan'], prefix='VMail')
    
    ds_tmp=pd.concat([ds, dummies_int, dummies_voice], axis=1)
    
    # We don't need these columns
    to_drop = ['State','Area Code','Phone','Churn?', 'Int\'l Plan', 'VMail Plan']
    df = ds_tmp.drop(to_drop,axis=1)
    
    print "after convert "
    print df.head(5)
    

    输出:

    after convert 01
       Account Length  VMail Message  Day Mins  Day Calls  Day Charge  Eve Mins  \
    0             128             25     265.1        110       45.07     197.4   
    1             107             26     161.6        123       27.47     195.5   
    2             137              0     243.4        114       41.38     121.2   
    3              84              0     299.4         71       50.90      61.9   
    4              75              0     166.7        113       28.34     148.3   
    
       Eve Calls  Eve Charge  Night Mins  Night Calls  Night Charge  Intl Mins  \
    0         99       16.78       244.7           91         11.01       10.0   
    1        103       16.62       254.4          103         11.45       13.7   
    2        110       10.30       162.6          104          7.32       12.2   
    3         88        5.26       196.9           89          8.86        6.6   
    4        122       12.61       186.9          121          8.41       10.1   
    
       Intl Calls  Intl Charge  CustServ Calls  _int'l Plan_no  _int'l Plan_yes  \
    0           3         2.70               1               1                0   
    1           3         3.70               1               1                0   
    2           5         3.29               0               1                0   
    3           7         1.78               2               0                1   
    4           3         2.73               3               0                1   
    
       VMail_no  VMail_yes  
    0         0          1  
    1         0          1  
    2         1          0  
    3         1          0  
    4         1          0 
    

    我们可以看到结果, 所有的数据都是数值型的, 而且除去了对我们没有意义的列。

    3.3 scale 数据范围

    我们需要做一些scale的工作。 就是有些属性的scale 太大了。

    1. 对于逻辑回归和梯度下降来说, 个属性的scale 差距太大, 会对收敛速度有很大的影响。
    2. 我们这里对所有的都做, 其实可以对一些突出的特征做这种处理。
    #scale
    X = df.as_matrix().astype(np.float)
    
    # This is important
    from sklearn.preprocessing import StandardScaler
    scaler = StandardScaler()
    X = scaler.fit_transform(X)
    
    print "Feature space holds %d observations and %d features" % X.shape
    print "Unique target labels:", np.unique(y)
    

    输出:

    Feature space holds 3333 observations and 19 features
    Unique target labels: [0 1]
    

    其他的呢, 还可以考虑降维等各种方式。 但是再实际使用中, 我们往往首先做出一个模型, 得到一个参考结果, 然后逐步优化。 所以我们准备数据就到这里。

    4 评估算法

    我们会使用多个算法来计算结果, 然后选择较好的。 如下

    # prepare models
    models = []
    models.append(('LR', LogisticRegression()))
    models.append(('LDA', LinearDiscriminantAnalysis()))
    models.append(('KNN', KNeighborsClassifier()))
    models.append(('CART', DecisionTreeClassifier()))
    models.append(('NB', GaussianNB()))
    models.append(('SVM', SVC()))
    # evaluate each model in turn
    results = []
    names = []
    scoring = 'accuracy'
    for name, model in models:
        kfold = KFold(n_splits=10, random_state=7)
        cv_results = cross_val_score(model, X, Y, cv=kfold, scoring=scoring)
        results.append(cv_results)
        names.append(name)
        msg = "%s: %f (%f)" % (name, cv_results.mean(), cv_results.std())
        print(msg)
    # boxplot algorithm comparison
    fig = pyplot.figure()
    fig.suptitle('Algorithm Comparison')
    ax = fig.add_subplot(111)
    pyplot.boxplot(results)
    ax.set_xticklabels(names)
    pyplot.show()
    LR: 0.860769 (0.021660)
    LDA: 0.852972 (0.021163)
    KNN: 0.896184 (0.016646)
    CART: 0.920491 (0.012471)
    NB: 0.857179 (0.015487)
    SVM: 0.921091 (0.016828)
    

    在这里插入图片描述
    可以看到什么呢, 看到SVM 和 CART 效果相对较好。

    5 提升结果

    提升的部分, 如何使用提升算法。 比如随机森林: xgboost

    from sklearn.ensemble import RandomForestClassifier
    num_trees = 100
    max_features = 3
    kfold = KFold(n_splits=10, random_state=7)
    model = RandomForestClassifier(n_estimators=num_trees, max_features=max_features)
    results = cross_val_score(model, X, Y, cv=kfold)
    print(results.mean())
    # 0.954696013379
    from sklearn.ensemble import GradientBoostingClassifier
    seed = 7
    num_trees = 100
    kfold = KFold(n_splits=10, random_state=seed)
    model = GradientBoostingClassifier(n_estimators=num_trees, random_state=seed)
    results = cross_val_score(model, X, Y, cv=kfold)
    print(results.mean())
    # 0.953197209185
    

    可以看到, 这两种算法对单个算法的提升还是很明显的。 进一步的, 也可以继续调整tree的数目, 但是效果应该差不多了。

    6 展示结果

    这里展示了如何保存这个算法, 以及如何取出然后应用。

    #store
    from sklearn.model_selection import train_test_split
    from pickle import dump
    from pickle import load
    
    X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.33, random_state=7)
    from sklearn.ensemble import GradientBoostingClassifier
    seed = 7
    num_trees = 100
    kfold = KFold(n_splits=10, random_state=seed)
    model = GradientBoostingClassifier(n_estimators=num_trees, random_state=seed)
    model.fit(X_train, Y_train)
    # save the model to disk
    filename = 'finalized_model.sav'
    dump(model, open(filename, 'wb'))
    # some time later...
    # load the model from disk
    loaded_model = load(open(filename, 'rb'))
    result = loaded_model.score(X_test, Y_test)
    print(result)
    

    7 后记

    本文展示了通过用户流失率问题, 如何把机器学习的预测过程应用到实际项目中。

    从业务的角度, 这个只是一个demo性质的应用, 实际场景可能复杂的多。
    从流程的角度, 通过对数据的分析可以进一步提升算法的性能, 对于某些的特征, 可以采取不同的处理方式。 比如缺失值的处理, 这里很完整, 就省去了这个步骤。




    更多案例及完整代码请关注“思享会Club”公众号或者关注思享会博客:http://gkhelp.cn/

    在这里插入图片描述

    展开全文
  • 总体流失率分析3.2.用户属性分析3.3.服务属性分析3.4.合同属性分析4.高流失率用户画像5.结论和建议 1.数据集说明 每一行代表一个客户,每一列包含列元数据中描述的客户属性。原始数据包含7043行(客户)和21列(特性)。...

    1.数据集说明

    每一行代表一个客户,每一列包含列元数据中描述的客户属性。原始数据包含7043行(客户)和21列(特性)。

    字段 字段 字段说明
    customerID: 用户ID 身份标识
    gender 性别 (male,female )
    SeniorCitizen 是否老年人 (0, 1 )
    Partner 是否有伴侣 (No, Yes )
    Dependents 是否有抚养人 (No, Yes )
    tenure 客户入网时长(月) (连续值 0-72 )
    PhoneService 是否有电话服务 (Yes, No)
    MultipleLines 是否有多线服务 (Yes, No, No phone service)
    InternetService 客户互联网服务提供商 (No, DSL数字网络,fiber optic光纤网络 )
    OnlineSecurity 是否有在线安全 (Yes, No, No internet service)
    OnlineBackup 是否在线备份 (Yes, No, No internet service)
    DeviceProtection 设备保护策略 (Yes, No, No internet service)
    TechSupport 技术支持 (Yes, No, No internet service)
    StreamingTV 在线电视 (Yes, No, No internet service)
    StreamingMovies 在线电影 (Yes, No, No internet service)
    Contract 合同 (month-to-month, two year, One year)
    PaperlessBilling 无纸账单 (Yes, No)
    PaymentMethod 支付方式 (Electronic check, Mailed check, Bank transfer (automatic), Credit card (automatic))
    MonthlyCharges 每月服务费 (连续值)
    TotalCharges 总话费 (连续值)
    Churn 流失标签 (No, Yes)

    2.分析思路

    寻找与流失率有关的特征,进一步分析这些特征如何影响流失率,刻画高流失率用户画像,对高流失率用户提供建议。

    3.数据预处理

    import numpy as np
    import pandas as pd
    import seaborn as sns
    import matplotlib.pyplot as plt
    from datetime import datetime
    
    %matplotlib inline
    plt.style.use('ggplot')
    plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
    plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号
    
    df = pd.read_csv('电信运营商客户数据集.csv')
    df.head()
    
    customerID gender SeniorCitizen Partner Dependents tenure PhoneService MultipleLines InternetService OnlineSecurity ... DeviceProtection TechSupport StreamingTV StreamingMovies Contract PaperlessBilling PaymentMethod MonthlyCharges TotalCharges Churn
    0 7590-VHVEG Female 0 Yes No 1 No No phone service DSL No ... No No No No Month-to-month Yes Electronic check 29.85 29.85 No
    1 5575-GNVDE Male 0 No No 34 Yes No DSL Yes ... Yes No No No One year No Mailed check 56.95 1889.5 No
    2 3668-QPYBK Male 0 No No 2 Yes No DSL Yes ... No No No No Month-to-month Yes Mailed check 53.85 108.15 Yes
    3 7795-CFOCW Male 0 No No 45 No No phone service DSL Yes ... Yes Yes No No One year No Bank transfer (automatic) 42.30 1840.75 No
    4 9237-HQITU Female 0 No No 2 Yes No Fiber optic No ... No No No No Month-to-month Yes Electronic check 70.70 151.65 Yes

    5 rows × 21 columns

    #查看数据信息
    df.info()
    
    <class 'pandas.core.frame.DataFrame'>
    RangeIndex: 7043 entries, 0 to 7042
    Data columns (total 21 columns):
     #   Column            Non-Null Count  Dtype  
    ---  ------            --------------  -----  
     0   customerID        7043 non-null   object 
     1   gender            7043 non-null   object 
     2   SeniorCitizen     7043 non-null   int64  
     3   Partner           7043 non-null   object 
     4   Dependents        7043 non-null   object 
     5   tenure            7043 non-null   int64  
     6   PhoneService      7043 non-null   object 
     7   MultipleLines     7043 non-null   object 
     8   InternetService   7043 non-null   object 
     9   OnlineSecurity    7043 non-null   object 
     10  OnlineBackup      7043 non-null   object 
     11  DeviceProtection  7043 non-null   object 
     12  TechSupport       7043 non-null   object 
     13  StreamingTV       7043 non-null   object 
     14  StreamingMovies   7043 non-null   object 
     15  Contract          7043 non-null   object 
     16  PaperlessBilling  7043 non-null   object 
     17  PaymentMethod     7043 non-null   object 
     18  MonthlyCharges    7043 non-null   float64
     19  TotalCharges      7043 non-null   object 
     20  Churn             7043 non-null   object 
    dtypes: float64(1), int64(2), object(18)
    memory usage: 1.1+ MB
    

    没有数据缺失。

    #是否有重复数据
    sum(df.duplicated())
    
    0
    
    sum(df.customerID.duplicated())
    
    0
    

    一共有7043名用户的数据。

    #将TotalCharges(总消费额)转换为浮点型,错误充为nan值
    df['TotalCharges'] = pd.to_numeric( df['TotalCharges'],errors=  'coerce' )
    df.info()
    
    <class 'pandas.core.frame.DataFrame'>
    RangeIndex: 7043 entries, 0 to 7042
    Data columns (total 21 columns):
     #   Column            Non-Null Count  Dtype  
    ---  ------            --------------  -----  
     0   customerID        7043 non-null   object 
     1   gender            7043 non-null   object 
     2   SeniorCitizen     7043 non-null   int64  
     3   Partner           7043 non-null   object 
     4   Dependents        7043 non-null   object 
     5   tenure            7043 non-null   int64  
     6   PhoneService      7043 non-null   object 
     7   MultipleLines     7043 non-null   object 
     8   InternetService   7043 non-null   object 
     9   OnlineSecurity    7043 non-null   object 
     10  OnlineBackup      7043 non-null   object 
     11  DeviceProtection  7043 non-null   object 
     12  TechSupport       7043 non-null   object 
     13  StreamingTV       7043 non-null   object 
     14  StreamingMovies   7043 non-null   object 
     15  Contract          7043 non-null   object 
     16  PaperlessBilling  7043 non-null   object 
     17  PaymentMethod     7043 non-null   object 
     18  MonthlyCharges    7043 non-null   float64
     19  TotalCharges      7032 non-null   float64
     20  Churn             7043 non-null   object 
    dtypes: float64(2), int64(2), object(17)
    memory usage: 1.1+ MB
    

    TotalCharges(总消费额)有缺失值。

    df[df.TotalCharges.isin([np.NaN])]
    
    customerID gender SeniorCitizen Partner Dependents tenure PhoneService MultipleLines InternetService OnlineSecurity ... DeviceProtection TechSupport StreamingTV StreamingMovies Contract PaperlessBilling PaymentMethod MonthlyCharges TotalCharges Churn
    488 4472-LVYGI Female 0 Yes Yes 0 No No phone service DSL Yes ... Yes Yes Yes No Two year Yes Bank transfer (automatic) 52.55 NaN No
    753 3115-CZMZD Male 0 No Yes 0 Yes No No No internet service ... No internet service No internet service No internet service No internet service Two year No Mailed check 20.25 NaN No
    936 5709-LVOEQ Female 0 Yes Yes 0 Yes No DSL Yes ... Yes No Yes Yes Two year No Mailed check 80.85 NaN No
    1082 4367-NUYAO Male 0 Yes Yes 0 Yes Yes No No internet service ... No internet service No internet service No internet service No internet service Two year No Mailed check 25.75 NaN No
    1340 1371-DWPAZ Female 0 Yes Yes 0 No No phone service DSL Yes ... Yes Yes Yes No Two year No Credit card (automatic) 56.05 NaN No
    3331 7644-OMVMY Male 0 Yes Yes 0 Yes No No No internet service ... No internet service No internet service No internet service No internet service Two year No Mailed check 19.85 NaN No
    3826 3213-VVOLG Male 0 Yes Yes 0 Yes Yes No No internet service ... No internet service No internet service No internet service No internet service Two year No Mailed check 25.35 NaN No
    4380 2520-SGTTA Female 0 Yes Yes 0 Yes No No No internet service ... No internet service No internet service No internet service No internet service Two year No Mailed check 20.00 NaN No
    5218 2923-ARZLG Male 0 Yes Yes 0 Yes No No No internet service ... No internet service No internet service No internet service No internet service One year Yes Mailed check 19.70 NaN No
    6670 4075-WKNIU Female 0 Yes Yes 0 Yes Yes DSL No ... Yes Yes Yes No Two year No Mailed check 73.35 NaN No
    6754 2775-SEFEE Male 0 No Yes 0 Yes Yes DSL Yes ... No Yes No No Two year Yes Bank transfer (automatic) 61.90 NaN No

    11 rows × 21 columns

    总消费额缺失的用户,入网时长为0,但显示没有流失,可能是数据记录错误,也可能是免费合约。没有分析意义,应该剔除。

    df.dropna(inplace = True)
    df.info()
    
    <class 'pandas.core.frame.DataFrame'>
    Int64Index: 7032 entries, 0 to 7042
    Data columns (total 21 columns):
     #   Column            Non-Null Count  Dtype  
    ---  ------            --------------  -----  
     0   customerID        7032 non-null   object 
     1   gender            7032 non-null   object 
     2   SeniorCitizen     7032 non-null   int64  
     3   Partner           7032 non-null   object 
     4   Dependents        7032 non-null   object 
     5   tenure            7032 non-null   int64  
     6   PhoneService      7032 non-null   object 
     7   MultipleLines     7032 non-null   object 
     8   InternetService   7032 non-null   object 
     9   OnlineSecurity    7032 non-null   object 
     10  OnlineBackup      7032 non-null   object 
     11  DeviceProtection  7032 non-null   object 
     12  TechSupport       7032 non-null   object 
     13  StreamingTV       7032 non-null   object 
     14  StreamingMovies   7032 non-null   object 
     15  Contract          7032 non-null   object 
     16  PaperlessBilling  7032 non-null   object 
     17  PaymentMethod     7032 non-null   object 
     18  MonthlyCharges    7032 non-null   float64
     19  TotalCharges      7032 non-null   float64
     20  Churn             7032 non-null   object 
    dtypes: float64(2), int64(2), object(17)
    memory usage: 1.2+ MB
    
    df.describe()
    
    SeniorCitizen tenure MonthlyCharges TotalCharges
    count 7032.000000 7032.000000 7032.000000 7032.000000
    mean 0.162400 32.421786 64.798208 2283.300441
    std 0.368844 24.545260 30.085974 2266.771362
    min 0.000000 1.000000 18.250000 18.800000
    25% 0.000000 9.000000 35.587500 401.450000
    50% 0.000000 29.000000 70.350000 1397.475000
    75% 0.000000 55.000000 89.862500 3794.737500
    max 1.000000 72.000000 118.750000 8684.800000

    目前还有7032个客户信息。

    3.数据分析及可视化

    3.1.总体流失率分析

    plt.figure(figsize = (12,6))
    plt.subplot(121)
    plt.pie(df['Churn'].value_counts(),labels=df['Churn'].value_counts().index,autopct='%1.2f%%',explode=(0.1,0))
    plt.title('用户流失和非流失占比')
    
    plt.subplot(122)
    x, y = df['Churn'].value_counts().index, df['Churn'].value_counts().values
    plt.bar(x, y, width=0.35)
    plt.title('用户流失和非流失人数')
    for a,b in zip(x,y):
        plt.text(a, b-0.3,'%.3f'%b, ha = 'center',va = 'bottom',fontsize=15)
    
    

    在这里插入图片描述
    流失人数为1869人,占比26.58%。

    3.2.用户属性分析

    性别、是否老年人、是否有伴侣、是否有抚养人、入网时长与流失率的关系

    df.iloc[:,1:5].columns
    
    Index(['gender', 'SeniorCitizen', 'Partner', 'Dependents'], dtype='object')
    
    def churn_rate(data,column):  #流失率函数
        
        df1 = data[data[column] == data[column].unique()[0]]
        df2 = data[data[column]  == data[column].unique()[1]]
        df_Churn1 = len(df1.query('Churn == "Yes"'))/len(df1)
        df_Churn2 = len(df2.query('Churn == "Yes"'))/len(df2)
        p = plt.bar( [data[column].unique()[0],data[column].unique()[1]],[df_Churn1,df_Churn2])
        plt.ylabel('流失率')
        return plt
    
    
    plt.figure(figsize = (12,8))
    plt.subplot(221)
    churn_rate(df,'gender')
    plt.title('性别与流失率')
    
    plt.subplot(222)
    churn_rate(df,'SeniorCitizen')
    plt.title('年龄与流失率')
    plt.xticks([0,1],['非老年人','老年人'])
    
    plt.subplot(223)
    churn_rate(df,'Partner')
    plt.title('伴侣与流失率')
    plt.xticks([0,1],['有伴侣','无伴侣'])
    
    plt.subplot(224)
    churn_rate(df,'Dependents')
    plt.title('抚养人与流失率')
    plt.xticks([0,1],['无抚养人','有抚养人'])
    
    ([<matplotlib.axis.XTick at 0x1ae1c0b7148>,
      <matplotlib.axis.XTick at 0x1ae1c0ac248>],
     <a list of 2 Text xticklabel objects>)
    

    在这里插入图片描述

    性别与流失率无关。
    老年用户比非老年用户流失率高,流失率分别为42%,24%。
    无伴侣用户比有伴侣用户流失率高,流失率分别为34%,20%。
    无抚养人用户比有抚养人用户的流失率高,流失率分别为32%,16%。

    #入网时长与流失率
    plt.figure(figsize = (12,6))
    df.query('Churn == "Yes"').tenure.plot(kind = 'kde')
    plt.title('不同入网时长的流失密度图')
    plt.xlabel('入网时长')
    plt.ylabel('密度')
    plt.axvline(3,color = 'b')
    plt.axvline(6,color = 'b')
    plt.axvline(16,color = 'b')
    plt.text(1,0.01,3,fontsize=15)
    plt.text(7,0.01,6,fontsize=15)
    plt.text(17,0.01,16,fontsize=15)
    

    在这里插入图片描述

    入网6个月内的用户流失概率最高,其中入网三个月的用户流失概率达到峰值。总体来看,入网时间越长,流失概率越小。
    入网第3个月是用户流失的高发期,在前15个月内,提高用户入网时长,对于降低流失率作用显著。建议通过一些策略将用户入网时间提高到3-6个月以后,比如三个月优惠期、入网满三个月发放优惠或奖励等。

    3.3.服务属性分析

    是否有电话服务(Yes, No)
    是否有多线服务(Yes, No, No phone service)
    客户互联网服务提供商 (No, DSL数字网络,fiber optic光纤网络)
    是否有在线安全(Yes, No, No internet service)
    是否在线备份(Yes, No, No internet service)
    设备保护策略(Yes, No, No internet service)
    技术支持(Yes, No, No internet service)
    在线电视(Yes, No, No internet service)
    在线电影(Yes, No, No internet service)

    df.iloc[:,6:15].columns
    
    Index(['PhoneService', 'MultipleLines', 'InternetService', 'OnlineSecurity',
           'OnlineBackup', 'DeviceProtection', 'TechSupport', 'StreamingTV',
           'StreamingMovies'],
          dtype='object')
    
    #互联网服务提供商与流失率
    def service_churn_rate(data,column):
        service = pd.DataFrame(data.groupby([column,'Churn']).customerID.count())
        service_sum = service.groupby(column).customerID.transform(sum)   
        service_churn_rate1 = (service['customerID']/service_sum).xs('Yes',level = 1)
       
        return service_churn_rate1
    
    plt.figure(figsize = (8,4))
    service_churn_rate(df,'InternetService').plot(kind = 'bar')
    plt.title('互联网服务提供商与流失率')
    plt.xticks([0,1,2],['No','DSL数字网络','光纤网络'])
    

    在这里插入图片描述
    数字网络和光纤网络的用户流失率分别为0.19、0.42,没有互联网服务的用户流失率为0.07。
    可以看出没有互联网服务的用户,其流失率远低于有互联网服务的用户;数字网络DSL的流失率高于光纤网络。

    column = list(df.iloc[:,6:15].columns)
    column.remove('InternetService')
    other_service_churn = pd.Series()
    for i in column:
        a = service_churn_rate(df,i)
        other_service_churn = pd.concat([other_service_churn,a],axis =1)
    
    other_service_churn = other_service_churn.drop(0,axis = 1)
    other_service_churn.columns = ['电话服务','多线服务','在线安全','在线备份','设备保护策略','技术支持','在线电视','在线电影']
    other_service_churn
    
    
    C:\ProgramData\Anaconda3\lib\site-packages\ipykernel_launcher.py:3: DeprecationWarning: The default dtype for empty Series will be 'object' instead of 'float64' in a future version. Specify a dtype explicitly to silence this warning.
      This is separate from the ipykernel package so we can avoid doing imports until
    
    电话服务 多线服务 在线安全 在线备份 设备保护策略 技术支持 在线电视 在线电影
    No 0.250000 0.250812 0.417787 0.399417 0.391403 0.416475 0.335351 0.337289
    Yes 0.267475 0.286485 0.146402 0.215670 0.225393 0.151961 0.301147 0.299524
    No phone service NaN 0.250000 NaN NaN NaN NaN NaN NaN
    No internet service NaN NaN 0.074342 0.074342 0.074342 0.074342 0.074342 0.074342
    #其他服务与流失率 =
    plt.rcParams['xtick.labelsize']=18
    plt.rcParams['axes.labelsize'] = 25
    plt.rcParams['axes.titlesize'] = 20
    
    other_service_churn.T.plot(kind = 'bar',figsize = (18,8),)
    plt.title('其他服务与流失率')
    plt.ylabel('流失率')
    

    在这里插入图片描述

    电话服务、多线服务、在线电视、在线电影与流失率关系不大。
    没有互联网服务的用户流失率在0.07左右,远低于有互联网服务的用户。
    在线安全、在线备份、设备保护策略、在线技术支持与流失率关系较大。在这四个服务中,有服务的用户流失低于没有服务的用户,没有这四个服务的用户流失率均在0.4左右,其中开通在线安全和技术支持的用户流失率最低。
    虽然没有互联网服务的用户流失率低,但由于开通服务能得到更多收益,应该使用户在开通服务的前提下减少流失率。

    3.4.合同属性分析

    合同期 (month-to-month, two year, One year)
    无纸账单(Yes, No)
    支付方式(Electronic check电子支付, Mailed check邮件支付, Bank transfer (automatic)银行自动转账, Credit card (automatic)信用卡自动支付)
    每月服务费
    总消费额

    plt.rcParams['xtick.labelsize']=15
    plt.rcParams['axes.labelsize'] = 12
    plt.rcParams['axes.titlesize'] = 16
    plt.figure(figsize = (18,14))
    
    plt.subplot(221)
    service_churn_rate(df,'Contract').plot(kind = 'bar')
    plt.title('合同期与流失率')
    plt.xticks([0,1,2],['每月到期续签','一年','两年'])
    
    plt.subplot(222)
    service_churn_rate(df,'PaperlessBilling').plot(kind = 'bar')
    plt.title('有纸账单与流失率')
    plt.xticks([0,1],['有纸账单','无纸账单'])
    
    plt.subplot(212)
    service_churn_rate(df,'PaymentMethod').plot(kind = 'bar')
    plt.title('支付方式与流失率')
    plt.xticks([0,1,2,3],['银行自动转账','信用卡自动支付','电子支付','邮件支付'])
    

    在这里插入图片描述
    合同期、有纸账单、支付方式均对流失率有影响。
    合同期越短,流失率越高。每月到期续签的流失率达到0.4,远高于一年期和两年期的用户。
    电子账单的流失率(0.33)高于有纸账单(0.17)。
    电子支付的流失率高于其他支付方式。电子支付的流失率是0.48,其他支付方式的流失率在0.15-0.2之间。

    #支付费用与流失率
    plt.figure(figsize = (12,12))
    plt.subplot(221)
    df.query('Churn == "Yes"').MonthlyCharges.plot(kind = 'kde')
    plt.title('不同月服务费的流失密度图')
    plt.xlabel('每月服务费')
    plt.ylabel('密度')
    plt.axvline(80,color = 'b')
    plt.axvline(60,color = 'b')
    plt.axvline(118,color = 'b')
    plt.text(82,0.01,80,fontsize=15)
    plt.text(62,0.01,60,fontsize=15)
    plt.text(120,0.01,118,fontsize=15)
    plt.subplot(222)
    df.query('MonthlyCharges > 100').query('Churn == "Yes"').MonthlyCharges.plot(kind = 'kde')
    plt.title('月费>100的流失密度图')
    
    plt.subplot(212)
    df.query('Churn == "Yes"').TotalCharges.plot(kind = 'kde')
    plt.title('不同总消费额的流失密度图')
    plt.xlabel('总消费额')
    plt.ylabel('密度')
    plt.axvline(250,color = 'b')
    plt.text(270,0.0001,250,fontsize=15)
    

    在这里插入图片描述

    月费在18-118之间。月费70-100的用户流失概率较高,60以下的用户流失概率较低,80达到峰值。对于月费小于80的用户,提高月费会增加流失概率,尤其是月费在60-80的用户;对于月费大于80的用户,提高月费会降低流失概率,尤其是月费大于100的用户。
    总消费额在18-8700之间。总消费额为250的用户流失概率最高,大于或小于250的流失概率迅速减小,总消费额在2000以上的流失概率较小。一般来说,总消费越高,表明留存时间越长,流失概率也就越小。

    4.高流失率用户画像

    在这里插入图片描述

    5.结论和建议

    1. 老年人、无伴侣、无抚养人的用户流失率更高,建议针对这部分用户的特点,制定专属套餐,推荐合理的套餐组合。

    2. 总体来看,入网时间越长,流失概率越小。入网第3个月是用户流失的高发期,在前15个月内,提高用户入网时长,对于降低流失率作用显著。建议通过一些策略将用户入网时间提高到3-6个月以后,比如三个月优惠期、入网满三个月发放优惠或奖励等。

    3. 数字网络DSL的流失率高于光纤网络。建议对数字网络用户情况和服务作进一步分析,以确定具体原因。

    4. 没有互联网服务的用户流失率在0.07左右,远低于有互联网服务的用户。虽然没有互联网服务的用户流失率低,但由于开通服务能得到更多收益,应该在用户开通服务的前提下,减少流失率。

    5. 在线安全、在线备份、设备保护策略、在线技术支持与流失率关系较大。在这四个服务中,有服务的用户流失低于没有服务的用户,其中开通在线安全和技术支持的用户流失率最低。对于有互联网服务的用户,建议让其开通在线安全或技术支持服务,可降低60%左右的流失概率。

    6. 合同期越短,流失率越高;电子账单的流失率高于有纸账单;电子支付的流失率高于其他支付方式。延长用户合同期有利于降低流失率,建议通过优惠吸引用户签订一年期、两年期,比如前几个月减免、赠送其他服务等。

    7. 月费70-100的用户流失概率较高,60以下的用户流失概率较低,80达到峰值。对于月费小于80的用户,提高月费会增加流失概率,尤其是60-80的用户应该谨慎提高其月费;对于月费大于80的用户,提高月费会降低流失概率,尤其是大于100的用户增加其月费,或增加服务,可以降低流失概率。

    展开全文
  • 以电信运营商客户流失率问题为例,从问题的提出, 数据的分析, 算法的评估, 到最终的结果展示, ,一步步介绍机器学习基本流程。 用户数据来源于互联网。 1 定义问题 客户流失率问题是电信运营商面临得一项重要...
  • 橙色电信的客户流失数据集将用于开发预测模型,该数据集由清除的客户活动数据(功能)以及指定客户是否取消订阅的客户流失标签组成。这里提供了两个数据集:可以下载churn-80和churn-20数据集。 telecom_churn.csv

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 543
精华内容 217
关键字:

客户流失率