美文网首页
用户流失预警

用户流失预警

作者: 浮尘琐事 | 来源:发表于2020-04-15 20:46 被阅读0次

    对于互联网公司而言,用户流失是个不可避免的问题,据统计,获取一个新用户的成本比召回一个新用户的成本要大得多。如果能降低用户的流失率,那么公司的成本会显著降低。

    本文通过分析一个电信公司流失用户分析的案例,将机器学习应用到实际工作中。

    一、导入数据

    from __future__ import division # 精确除法,“/”操作执行的是截断除法
    import pandas as pd
    import numpy as np
    
    churn_df = pd.read_csv('Desktop/用户流失预警/churn.csv')#导入数据
    col_names = churn_df.columns.tolist() #所有的列名展示出来
    
    print("Column names:")
    print(col_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:流失与否
    to_show = col_names[:6] + col_names[-6:] #前6列和后6列
    
    print("\nSample data:")
    churn_df[to_show].head(6)
    
    image.png

    当我们拿到数据时,需要先对根据数据列名进行分析,这个数据共有21列,分别是用户信息和特征,最后一列就是他们的分类:流失与否,也是我们本次判断的目标。我们需要根据这些特征,训练一个准确率较高的模型。从机器学习的分类来讲, 这是一个监督问题中的分类问题,具体来说, 是一个二分类问题。

    二,数据清洗

    当我们拿到数据,了解此次分析的目标之后,便可以对数据进行处理了。

    churn_df.info() # 查看数据整体情况,看是否有缺失值。
    
    image.png

    在处理缺失值的过程中,我们一般有这样的思路:如果是连续值,那么可以使用均数、众数、中位数来代替。如果是str类型的数据,那么可以使用频次最高的数据代替。这份数据比较完整,因此也不需要处理缺失值。

    churn_df.describe()  #describe() 展示每一列数据的描述性统计信息
    
    Account Length  Area Code   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
    count   3333.000000 3333.000000 3333.000000 3333.000000 3333.000000 3333.000000 3333.000000 3333.000000 3333.000000 3333.000000 3333.000000 3333.000000 3333.000000 3333.000000 3333.000000 3333.000000
    mean    101.064806  437.182418  8.099010    179.775098  100.435644  30.562307   200.980348  100.114311  17.083540   200.872037  100.107711  9.039325    10.237294   4.479448    2.764581    1.562856
    std 39.822106   42.371290   13.688365   54.467389   20.069084   9.259435    50.713844   19.922625   4.310668    50.573847   19.568609   2.275873    2.791840    2.461214    0.753773    1.315491
    min 1.000000    408.000000  0.000000    0.000000    0.000000    0.000000    0.000000    0.000000    0.000000    23.200000   33.000000   1.040000    0.000000    0.000000    0.000000    0.000000
    25% 74.000000   408.000000  0.000000    143.700000  87.000000   24.430000   166.600000  87.000000   14.160000   167.000000  87.000000   7.520000    8.500000    3.000000    2.300000    1.000000
    50% 101.000000  415.000000  0.000000    179.400000  101.000000  30.500000   201.400000  100.000000  17.120000   201.200000  100.000000  9.050000    10.300000   4.000000    2.780000    1.000000
    75% 127.000000  510.000000  20.000000   216.400000  114.000000  36.790000   235.300000  114.000000  20.000000   235.300000  113.000000  10.590000   12.100000   6.000000    3.270000    2.000000
    max 243.000000  510.000000  51.000000   350.800000  165.000000  59.640000   363.700000  170.000000  30.910000   395.000000  175.000000  17.770000   20.000000   20.000000   5.400000    9.000000
    

    三,探索性数据分析

    • Step1.特征自己的信息
    #我们先来看一下流失比例, 以及关于打客户电话的个数分布
    import matplotlib.pyplot as plt # 仿真
    %matplotlib inline
    
    fig = plt.figure()
    fig.set(alpha=0.3)  # 设定图表颜色alpha参数
    #subplot2grid(shape , loc )
    plt.subplot2grid((1,2),(0,0))# 图像几行几列,从第0行第0列,
    
    # line bar barsh kde
    churn_df['Churn?'].value_counts().plot(kind='bar') #把用户是否流失分组起来,流失的有多少人,没有流失的有多少人
    
    plt.title(u"stat for churn") # 设置标题
    
    plt.ylabel(u"number")  #流失与否的数量,一共3333行,没有流失的约占2700 ,流失的占500左右
    
    plt.subplot2grid((1,2),(0,1))            
    churn_df[u'CustServ Calls'].value_counts().plot(kind='bar') # 客服电话, 客户打电话投诉多那流失率可能会大
    plt.title("stat for cusServCalls") # 标题
    plt.ylabel(u"number") #客户打1个客服电话的有1400个左右,客户.....总计加起来有3333个 
    
    plt.show()
    
    image.png
    • 一共3333个样本,False代表没有流失2700个左右 , True代表流失的为400个左右
    • 客户打1个客服电话的有1400个左右,客户打2个客服电话的有760个人个左右,客户.....总计加起来有3333个
    import matplotlib.pyplot as plt
    
    %matplotlib inline
    fig = plt.figure()
    fig.set(alpha=0.2)  # 设定图表颜色alpha参数
    
    plt.subplot2grid((1,3),(0,0)) # 在一张大图里分列几个小图
    churn_df['Day Mins'].plot(kind='kde') # 白天通话分钟数,图用的kde的图例
    plt.xlabel(u"Mins")# 横轴是分钟数
    plt.ylabel(u"density")  # density:密度
    plt.title(u"dis for day mins") #标题
    
    plt.subplot2grid((1,3),(0,1))            
    churn_df['Day Calls'].plot(kind='kde')# 白天打电话个数
    plt.xlabel(u"call")# 客户打电话个数
    plt.ylabel(u"density") #密度
    plt.title(u"dis for day calls") #标题
    
    plt.subplot2grid((1,3),(0,2))           
    churn_df['Day Charge'].plot(kind='kde') # 白天收费情况
    plt.xlabel(u"Charge")# 横轴是白天收费情况
    plt.ylabel(u"density") #密度
    plt.title(u"dis for day charge")
    
    plt.show()
    
    image.png
    • Step.2 特征和分类的关联
    fig = plt.figure()
    fig.set(alpha=0.2)  # 设定图表颜色alpha参数
    
    #查看流失与国际漫游之间的关系
    int_yes = churn_df['Churn?'][churn_df['Int\'l Plan'] == 'yes'].value_counts() # 分组,yes:参与了有国际漫游需求的统计出来
    int_no = churn_df['Churn?'][churn_df['Int\'l Plan'] == 'no'].value_counts() #分组:no:没有参与国际漫游的统计出来
    
    #用DataFrame做图例上的标签 ,在右上角
    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()
    
    image.png

    参与国际漫游的的用户流失率较高。 猜测也许他们有更多的选择, 或者对服务有更多的要求。 需要特别对待。

    #查看客户服务电话和结果的关联
    fig = plt.figure()
    fig.set(alpha=0.2)  # 设定图表颜色alpha参数
    
    cus_0 = churn_df['CustServ Calls'][churn_df['Churn?'] == 'False.'].value_counts()
    cus_1 = churn_df['CustServ Calls'][churn_df['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()
    
    image.png

    四,特征筛选

    • 根据对问题的分析, 我们做第一件事情, 去除三列无关列。 州名, 电话, 区号
    • 转化成数值类型:对于有些特征, 本身不是数值类型的, 这些数据是不能被算法直接使用的, 所以我们来处理一下
    # 对于标签数据需要整合
    ds_result = churn_df['Churn?']
    
    #shift+tab:condition是布尔类型的数组,每个条件都和x ,y 对应
    #等于True为1 ,等于False为0
    
    Y = np.where(ds_result == 'True.',1,0) 
    
    dummies_int = pd.get_dummies(churn_df['Int\'l Plan'], prefix='_int\'l Plan') #prefix:前缀
    # VMail Plan:某个策划活动  prefix:前缀
    dummies_voice = pd.get_dummies(churn_df['VMail Plan'], prefix='VMail')
    
    #concat:用来合并2个或者2个以上的数组
    ds_tmp=pd.concat([churn_df, dummies_int, dummies_voice], axis=1)
    
    # 删除州名、地区编号、手机号、用户是否流失、各种策略活动
    to_drop = ['State','Area Code','Phone','Churn?', 'Int\'l Plan', 'VMail Plan']
    df = ds_tmp.drop(to_drop,axis=1)
    
    print("after convert ")
    df.head(5)
    
    image.png
    # 整理好的数据拿过来
    churn_result = churn_df['Churn?']
    y = np.where(churn_result == 'True.',1,0)
    to_drop = ['State','Area Code','Phone','Churn?']
    churn_feat_space = churn_df.drop(to_drop,axis=1)
    yes_no_cols = ["Int'l Plan","VMail Plan"]
    churn_feat_space[yes_no_cols] = churn_feat_space[yes_no_cols] == 'yes'
    features = churn_feat_space.columns
    X = churn_feat_space.as_matrix().astype(np.float)
    from sklearn.preprocessing import StandardScaler
    scaler = StandardScaler()
    X = scaler.fit_transform(X)
    
    print("Feature space holds %d observations and %d features" % X.shape)
    print("---------------------------------")
    print("Unique target labels:", np.unique(y))
    print("---------------------------------")
    print(X[0])#第1行
    print("---------------------------------")
    print(len(y[y == 0]))
    
    image.png

    五 建立多种基础模型,尝试多种算法

    # 调入工具包
    from sklearn.linear_model import LogisticRegression
    from sklearn.svm import SVC
    from sklearn.model_selection import cross_val_score,KFold
    from sklearn.neighbors import KNeighborsClassifier 
    import matplotlib.pyplot as plt
    
    
    # 初始化模型
    models = []
    models.append(('KNN', KNeighborsClassifier()))
    
    
    models.append(('LR', LogisticRegression()))
    
    models.append(('SVM', SVC()))
    
    # 初始化
    results = []
    names = []
    scoring = 'accuracy' # 准确率
    for name, model in models:
        
        #random_state = 0 
        kfold = KFold(5,shuffle=True,random_state = 0) # 5折
        cv_results = cross_val_score(model, X, Y, cv=kfold)#scoring=scoring 默认为None
        results.append(cv_results)#交叉验证给的结果分
        names.append(name)
        #模型的标准差,体现模型的分值的波动,std越小越稳定
        msg = "%s: %f (%f)" % (name, cv_results.mean(), cv_results.std())
        print(msg)
        print("------------------------------")
    # boxplot algorithm comparison
    fig = plt.figure()
    fig.suptitle('Algorithm Comparison')
    ax = fig.add_subplot(111)
    
    plt.boxplot(results)
    ax.set_xticklabels(names)
    plt.show()
    
    # 总结:SVM的效果比较好
    
    image.png image.png

    六 模型调参/提升模型

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

    0.9522954091816367

    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.9525966085846325

    七 评估测试/结论汇报

    def run_prob_cv(X, y, clf_class, **kwargs):
        kf = KFold(5,True)
        y_prob = np.zeros((len(y),2))
        for train_index, test_index in kf.split(X):
            X_train, X_test = X[train_index], X[test_index]
            y_train = y[train_index]
            clf = clf_class(**kwargs)
            clf.fit(X_train,y_train)
            # Predict probabilities, not classes
            y_prob[test_index] = clf.predict_proba(X_test) #返回的是概率值 ,属于0的概率多少,属于1的概率是多少
        return y_prob
    
    import warnings
    warnings.filterwarnings('ignore')
    
    # Use 10 estimators so predictions are all multiples of 0.1
    pred_prob = run_prob_cv(X, y, RF, n_estimators=10)
    #print pred_prob[0]
    pred_churn = pred_prob[:,1]#只要属于1的概率是多少 ,因为咱们关注的是流失的
    is_churn = y == 1
    
    # Number of times a predicted probability is assigned to an observation
    counts = pd.value_counts(pred_churn) # 属于1的概率多少进行分组统计 , 即:pred_prob count
    #print counts
    
    # calculate true probabilities
    true_prob = {}
    for prob in counts.index:
        true_prob[prob] = np.mean(is_churn[pred_churn == prob]) 
        true_prob = pd.Series(true_prob)
    
    # pandas-fu
    counts = pd.concat([counts,true_prob], axis=1).reset_index()
    counts.columns = ['pred_prob', 'count', 'true_prob']
    counts
    
    image.png

    相关文章

      网友评论

          本文标题:用户流失预警

          本文链接:https://www.haomeiwen.com/subject/fpgtvhtx.html