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

    2020-11-26 16:20:19
    电信客户流失分析前言一、获取和检查数据1.获取数据2.检查各个属性是否有缺失值和异常值二、数据探索(EDA)1.上个月流失的用户留存天数2.流失用户的男女、老少比例3.用户流失流失与费用的关系4.用户家人与流失关系...

    一、获取和检查数据

    用到的库

    import pandas as pd
    import pandas_profiling as pp # 可视化数据的分布
    import numpy as np
    import eli5
    import matplotlib.pyplot as plt
    from sklearn.pipeline import Pipeline
    from sklearn.model_selection import cross_val_score, KFold, train_test_split, GridSearchCV
    from sklearn.preprocessing import OneHotEncoder, StandardScaler
    from sklearn.compose import ColumnTransformer
    from sklearn.impute import SimpleImputer
    from sklearn.svm import SVC
    from sklearn.tree import DecisionTreeClassifier
    from sklearn.ensemble import RandomForestClassifier
    from sklearn.linear_model import LogisticRegression
    

    1.获取数据

    本次用的是Kaggle的Telco Customer Churn数据集
    链接:地址
    主要的几个属性解释:
    tenure:用户存呆在公司的天数
    Churm:是否流失的标志
    TotalCharges: 总费用
    MonthlyCharges: 月费用

    2.检查各个属性是否有缺失值和异常值

    读取数据

    data = pd.read_csv("./data/WA_Fn-UseC_-Telco-Customer-Churn.csv")
    data.head()
    

    在这里插入图片描述
    经过探索发现"TotalCharges"有一些异常空值

    len(data[data['TotalCharges'] == " "])
    

    在这里插入图片描述
    将空值替换为0,然后用pandas_profiling(可视化每个属性的分布、均值
    最大值、特殊值)对数据进行探索
    在这里插入图片描述
    其中的一个相关系数矩阵,可以发现tenure于MonthlyCharges和TotalCharges的相关性很高

    二、数据探索(EDA)

    1.上个月流失的用户留存天数

    # Customers who left within the last month 
    customers_leave = data[data['Churn'] == "Yes"]
    counts = pd.DataFrame(customers_leave['tenure'].value_counts())
    ax = counts.plot.bar()
    ax._layout['xaxis']['title']['text'] = 'tenure'
    ax._layout['yaxis']['title']['text'] = 'Churn counts'
    ax
    

    在这里插入图片描述

    2.流失用户的男女、老少比例

    流失用户的老少比例

    senior_customer = data[(data['SeniorCitizen'] == 1) & (data['Churn'] == 'Yes')]
    young_customer = data[(data['SeniorCitizen'] == 0) & (data['Churn'] == 'Yes')]
    df = pd.DataFrame({'count': [len(senior_customer), len(young_customer)], 'age': ['senior', 'young']})
    ax = df.plot.bar(x='age', y='count', color=['senior', 'young'], title="the age proportion of Churn")
    ax._layout['yaxis']['title']['text'] = 'counts'
    ax._data_objs[0]['text'] = int(df['count'].values[0])
    ax._data_objs[1]['text'] = int(df['count'].values[1])
    ax
    

    在这里插入图片描述
    流失老年人用户中的男女比例

    senior_male = senior_customer[senior_customer['gender'] == 'Male']
    senior_female = senior_customer[senior_customer['gender'] == 'Female']
    df = pd.DataFrame({'count': [len(senior_male), len(senior_female)], 'gender': ['Male', 'Female']})
    ax = df.plot.bar(x='gender', y='count', color=['Male', 'Female'], title="the gender proportion of senior Churn")
    ax._layout['yaxis']['title']['text'] = 'counts'
    ax._data_objs[0]['text'] = int(df['count'].values[0])
    ax._data_objs[1]['text'] = int(df['count'].values[1])
    ax
    

    在这里插入图片描述
    流失年轻人用户中的男女比例

    young_male = young_customer[young_customer['gender'] == 'Male']
    young_female = young_customer[young_customer['gender'] == 'Female']
    df = pd.DataFrame({'count': [len(young_male), len(young_female)], 'gender': ['Male', 'Female']})
    ax = df.plot.bar(x='gender', y='count', color=['Male', 'Female'], title="the gender proportion of young Churn")
    ax._layout['yaxis']['title']['text'] = 'counts'
    ax._data_objs[0]['text'] = int(df['count'].values[0])
    ax._data_objs[1]['text'] = int(df['count'].values[1])
    ax
    

    在这里插入图片描述
    老年和年轻流失用户中男女比例差不多,可以判断流失用户与性别无关

    3.用户流失流失与费用的关系

    customers_leave[['MonthlyCharges']].hist(title="the hist of MonthlyCharges")
    

    在这里插入图片描述

    daily_charges = customers_leave['TotalCharges'] / customers_leave['tenure']
    pd.DataFrame(daily_charges).hist(title="the hist of DailyCharges")
    

    在这里插入图片描述

    4.用户家人与流失关系

    mask = (data['Partner'] == "Yes") | (data['Dependents'] == "Yes")
    family = data.copy()
    family['with_family'] = 0
    family.loc[mask, ['with_family']] = 1
    
    counts = family[['with_family', 'Churn']].groupby(['with_family', 'Churn']).size()
    index = ['with_family=0', 'with_family=1']
    count = pd.DataFrame(counts.values.reshape(-1, 2), columns=['Churn=No', 'Churn=Yes'], index=index)
    ax = count.plot.bar(title="The relationship between family and churn")
    prob = count.values / np.sum(count.values, axis=1).reshape(-1, 1) * 100
    ax._data_objs[0]['text'] = prob[:, 0]
    ax._data_objs[0]['texttemplate'] = "%{text:.4s}%"
    ax._data_objs[1]['text'] = prob[:, 1]
    ax._data_objs[1]['texttemplate'] = "%{text:.4s}%"
    ax
    

    在这里插入图片描述

    三、预测用户是否流失

    1.运用各种分类器进行交叉验证选出最好的分类器

    剔除性别和ID属性,将其他类别属性进行One-Hot编码,然后数据进行标准化,经行4折交叉验证训练

    cat_features = ['Partner', 'Dependents', 'PhoneService', 'MultipleLines', 
                    'InternetService', 'OnlineSecurity', 'OnlineBackup', 'DeviceProtection', 
                    'TechSupport', 'StreamingTV', 'StreamingMovies', 'Contract', 
                    'PaperlessBilling', 'PaymentMethod']
    num_features = ['SeniorCitizen', 'tenure', 'MonthlyCharges', 'TotalCharges']
    # separate features and pridicted value
    features = num_features + cat_features
    X = data.drop(['gender', 'customerID', 'Churn'], axis=1)[features]
    y = data['Churn']
    
    #preprocessing for numerical features:
    num_transformer = Pipeline(steps=[('scaler', StandardScaler())])
    
    # preprocessing for categorical features:
    cat_transformer = Pipeline(steps=[('onehot', OneHotEncoder())])
    
    # Bundle preprocessing for numerical and categorical features:
    preprocessor = ColumnTransformer(transformers=[('cat', cat_transformer, cat_features), 
                                                   ('num', num_transformer, num_features)])
    # define models to test
    base_models = [("SVC_model", SVC(random_state=42)),
                  ("DT_model", DecisionTreeClassifier(random_state=42)),
                  ("RF_model", RandomForestClassifier(random_state=42)),
                  ("LR_model", LogisticRegression(random_state=42))]
    
    # split data into 'kflods' parts for cross validation:
    split = KFold(n_splits=4, shuffle=True, random_state=42)
    # preprocessing, fitting, making predictions and scoring for every model:
    for name, model in base_models:
        # pack preprossing of the data and model in a pipeline:
        model_steps = Pipeline(steps=[('preprocessor', preprocessor),
                                      ('model', model)])
        # get cross validation score for every model:
        cv_result = cross_val_score(model_steps,
                                    X, y,
                                    cv=split,
                                    scoring="accuracy",
                                    n_jobs=-1)
        # oupts:
        min_score = round(min(cv_result), 4)
        max_score = round(max(cv_result), 4)
        mean_score = round(np.mean(cv_result), 4)
        std_dev = round(np.std(cv_result), 4)
        print(f"{name} cross validation score: {mean_score} +/- {std_dev} (std) min:{min_score} max:{max_score}")                                             
    

    在这里插入图片描述
    看起来逻辑斯蒂回归的效果最好

    2.参数调优

    parameters = {'C' : [0.1, 0.3, 1, 3]}  # 正则化参数
    lr = LogisticRegression(random_state=42, max_iter=1000)
    lr_pipe = Pipeline(steps=[('preprocessor', preprocessor),
                              ('model', GridSearchCV(lr, 
                                                     param_grid=parameters, 
                                                     cv=split, 
                                                     scoring="accuracy", 
                                                     n_jobs=-1))])
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
    lr_pipe.fit(X_train, y_train)
    print(lr_pipe.score(X_test, y_test))
    

    在这里插入图片描述
    看起来效果不错,基本在0.8左右

    3.属性的权重

    利用eli5这个库进行分类器权重的解析

    onehot_columns = list(lr_pipe.named_steps['preprocessor']\
                                 .named_transformers_['cat']\
                                 .named_steps['onehot']\
                                 .get_feature_names(input_features=cat_features))
    
    features_list = num_features + onehot_columns
    
    # Top 10 weights of features
    feat_imp_df = eli5.formatters.as_dataframe.explain_weights_df(
        lr_pipe.named_steps['model'].best_estimator_, 
        feature_names=features_list)
    feat_imp_df['weight'] = feat_imp_df['weight'].abs()
    feat_imp_df.sort_values(by='weight', ascending=False)[1:].head(10)
    

    在这里插入图片描述
    根据分类器的不同,结果也不同,可以看出支付方法和在线TV服务权重较大
    对着两个属性分析一下:

    1.支付模式对客户流失的影响

    data[['Churn', 'PaymentMethod']].groupby(['PaymentMethod', 'Churn']).size()
    counts = data[['Churn', 'PaymentMethod']].groupby(['PaymentMethod', 'Churn']).size()
    index = ['Bank transfer (automatic)', 'Credit card (automatic)', 'Electronic check', 'Mailed check']
    df = pd.DataFrame(data=counts.values.reshape(-1,2), columns=['Churn=No', 'Churn=Yes'], index=index)
    ax = df.plot.bar(title="the impact of different PaymentMethod")
    prob = df.values / np.sum(df.values, axis=1).reshape(-1, 1) * 100
    ax._data_objs[0]['text'] = prob[:, 0]
    ax._data_objs[0]['texttemplate'] = "%{text:.4s}%"
    ax._data_objs[1]['text'] = prob[:, 1]
    ax._data_objs[1]['texttemplate'] = "%{text:.4s}%"
    ax
    

    在这里插入图片描述

    2.是否订购在线TV服务对客户流失的影响

    counts = data[['Churn', 'StreamingTV']].groupby(['StreamingTV', 'Churn']).size()
    index = ['No', 'No internet service', 'Yes']
    df = pd.DataFrame(data=counts.values.reshape(-1,2), columns=['Churn=No', 'Churn=Yes'], index=index)
    ax = df.plot.bar(title="the different type of StreamingTV influence Churn")
    prob = df.values / np.sum(df.values, axis=1).reshape(-1, 1) * 100
    ax._data_objs[0]['text'] = prob[:, 0]
    ax._data_objs[0]['texttemplate'] = "%{text:.4s}%"
    ax._data_objs[1]['text'] = prob[:, 1]
    ax._data_objs[1]['texttemplate'] = "%{text:.4s}%"
    ax
    

    在这里插入图片描述

    data[['StreamingTV', 'MonthlyCharges']].groupby(['StreamingTV'])\
                                           .mean().plot.bar(color=['No', 'No internet service', 'Yes'],
                                                            title="the MonthlyCharges of different type of StreamingTV")
    

    在这里插入图片描述

    总结:如何更好的留住用户

    综上分析,用户留存与费用的相关性很大,流失用户多为年轻用户和新用户,所以要留住客户,可以适当降低费用,推出一些新产品吸引新用户和年轻用户,对老客户也可以有一定的福利回报,对有家庭的用户可以推出家庭套餐保留用户。可以预测客户是否将要流失,并执行一定的挽回策略

    展开全文
  • 电信客户流失 重点客户保留计划 WA_Fn-UseC_-Telco-Customer-Churn.csv
  • 电信客户流失数据分析

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

    研究背景

    用户流失率的下降能够提高公司利润,了解用户倾向有利于提高用户黏性,延长用户生命周期。面对如今高昂的获客成本,也可以相对地降低营销投入,做到精准营销。

    提出问题

    1、流失客户有哪些显著性特征?
    2、尝试找到合适的模型预测流失用户
    3、针对性给出增加用户黏性、预防流失的建议

    数据集描述

    数据集来自DataFountain,该数据集有21个变量,7043个数据点,每条记录包含了唯一客户的特征。
    用户属性
    customerID :用户ID。
    gender:性别。(Female & Male)
    SeniorCitizen :老年人 (1表示是,0表示不是)
    Partner :是否有配偶 (Yes or No)
    Dependents :是否有家属 (Yes or No)
    tenure :客户存留时长(0-72个月)
    服务需求
    PhoneService :是否开通电话服务业务 (Yes or No)
    MultipleLines:是否开通了多线业务(Yes 、No or No phoneservice 三种)
    InternetService:是否开通互联网服务 (No, DSL数字网络,fiber optic光纤网络 三种)
    OnlineSecurity:是否开通网络安全服务(Yes,No,No internetserive 三种)
    OnlineBackup:是否开通在线备份业务(Yes,No,No internetserive 三种)
    DeviceProtection:是否开通了设备保护业务(Yes,No,No internetserive 三种)
    TechSupport:是否开通了技术支持服务(Yes,No,No internetserive 三种)
    StreamingTV:是否开通网络电视(Yes,No,No internetserive 三种)
    StreamingMovies:是否开通网络电影(Yes,No,No internetserive 三种)
    交易倾向
    Contract:签订合同方式 (按月,一年,两年)
    PaperlessBilling:是否开通电子账单(Yes or No)
    PaymentMethod:付款方式(bank transfer,credit card,electronic check,mailed check)
    MonthlyCharges:月费用
    TotalCharges:总费用
    研究对象
    Churn:该用户是否流失(Yes or No)

    #导入数据集
    options(scipen = 200)
    df <- read.csv("F:/telco-customer-churn/WA_Fn-UseC_-Telco-Customer-Churn.csv")`
    

    特征工程

    1、数据预处理

    1.1、特征类型处理

    #SeniorCitizen转换为因子变量
    df <- within(df,{
      SeniorCitizen <- factor(SeniorCitizen,levels = c(0,1),labels = c("No","Yes"))
    })
    

    1.2、缺失值处理

    colSums(is.na(df))
    mean(is.na(df$TotalCharges))
    library(VIM)
    opar <- par(no.readonly = T)
    par(cex=0.72,font.axis=3)
    aggr(df,prop=T,numbers=T)
    par(opar)
    #只有TotalCharges列有11个缺失值,占比大约0.156%
    

    各列缺失值数量和缺失值占比在这里插入图片描述

    hist(df$TotalCharges,breaks = 50,prob=T,main = "histogram of TotalCharges")
    df$TotalCharges[is.na(df$TotalCharges)] <- median(df$TotalCharges,na.rm = T)
    

    在这里插入图片描述
    TotalCharges是数值型数据,从直方图可以看到该列数据是偏态分布。根据正态分布选均值、中位数填充,偏态分布选中位数填充的原则,我选择用TotalCharges列的中位数去填充这11个缺失值。

    1.3、异常值处理

    layout(matrix(c(1,2),1,2,byrow = T))
    boxplot(df$MonthlyCharges,xlab="MonthlyCharges")
    boxplot(df$TotalCharges,xlab="TotalCharges")
    

    在这里插入图片描述
    通过箱线图可以看到MonthlyCharges、TotalCharges两个特征无极端异常值,但总费用量纲差异大,对特征进行分箱离散化处理

    library(Hmisc)
    describe(df[c("MonthlyCharges","TotalCharges")])
    #根据描述性统计量将变量按0.25,0.5,0.75分位数分成4份
    c_u_t <- function(x,n=1) {
      result <- quantile(x,probs = seq(0,1,1/n))
      result[1] <- result[1]-0.001
      return(result)
    }
    df <- transform(df,
                    MonthlyCharges_c = cut(df$MonthlyCharges,
                             breaks = c_u_t(df$MonthlyCharges,n=4),labels = c(1,2,3,4)),
                    TotalCharges_c = cut(df$TotalCharges,
                         breaks = c_u_t(df$TotalCharges,n=4),labels = c(1,2,3,4)))
    df <- within(df, {
      MonthlyCharges_c <- relevel(MonthlyCharges_c, ref = 1)
      TotalCharges_c <- relevel(TotalCharges_c, ref = 1)
    })
    

    1.4、分类变量标签整理

    for (i in 10:15) {
      print(prop.table(xtabs(~Churn+get(names(df)[i]),data = df)))
    }
    

    在这里插入图片描述
    通过OnlineSecurity、OnlineBackup、DeviceProtection、TechSupport、StreamingTV、StreamingMovies这6个变量分别和Churn生成二维列连表可以看到,"No internet service"这个标签的总数占比都是一致的,可以认为这个标签不影响流失率,所以把这个标签并入“No"标签

    df <- within(df,{
      levels(OnlineSecurity)[2] <- "No"
      levels(OnlineBackup)[2] <- "No"
      levels(DeviceProtection)[2] <- "No"
      levels(TechSupport)[2] <- "No"
      levels(StreamingTV)[2] <- "No"
      levels(StreamingMovies)[2] <- "No"
    })
    

    2、特征选择

    2.1、方差过滤

    df <- df[c(1,6,2:5,7,10:15,17,22,23,8,9,16,18,21,19,20)]  #变量位置重排
    library(caret)
    nearZeroVar(df[c(3:14)],freqCut = 90/10,saveMetrics = T)
    df$PhoneService <- NULL
    

    在这里插入图片描述
    对二分类变量进行方差过滤,变量PhoneService的nzv为TRUE即方差接近于零,代表这个变量中其中一类非常少,占比少于10%,则它变异程度小,提供的信息少,应该被筛选掉。

    2.2、卡方检验过滤

    sapply(df[c(3:19)], function(x){
      ch <- chisq.test(x,df$Churn,simulate.p.value = T)
      list(chi_v=ch$statistic,p=ch$p.value)
    })
    df$gender <- NULL
    

    在这里插入图片描述
    分类变量和目标变量Churn的卡方检验中,除了gender的P值为0.494,其他特征的P值都远小于0.01,所以不能拒绝gender和Churn相互独立的原假设,应被筛选掉。

    2.3、模型整体效果过滤

    (mydata <- df[c(2:19)])
    lg <- glm(Churn~.,family = binomial(),data = mydata)
    lg_back <- step(lg,direction = "backward")
    summary(lg_back)
    anova(lg_back,lg,test = "Chisq")
    

    在这里插入图片描述
    通过AIC向后法筛选出的特征筛除掉了Partner、DeviceProtection、MonthlyCharges_c三个变量。用anova函数对两个模型进行卡方检验P=0.99,表明两个模型拟合度一样好,有理相信这三个变量不会显著提高方程的预测精度。暂时先不删除这些变量,稍后再详细分析Logistic回归。

    2.4、利用随机森林进行特征重要性筛选

    library(randomForest)
    set.seed(123)
    (rf <- randomForest(Churn~.,data=mydata,importance=T,ntree=100))
    (imp <- importance(rf,type=2))
    imp[order(-imp),]
    

    在这里插入图片描述
    随机森林模型显示出tenure、Contract、PaymentMethod这三个特征重要性最高,Dependents、StreamingTV、StreamingMovies这三个特征重要性最低。这些低重要性特征暂时还是保留。

    相关回归分析

    1、二联列联表的相关性度量

    attach(df)
    library(vcd)
    for (i in 3:18) {
     print(assocstats(table(get(names(df)[i]),Churn))) 
    }
    

    在这里插入图片描述
    分别度量了每个分类特征与目标变量Churn的相关性强弱,Contract、InternetService、PaymentMethod与目标变量Churn的相关性较强。由于其他变量也通过了卡方检验,所以和Churn也有相关性,只是弱于这三个变量。

    2、连续型变量的差异检验

    #由于三个连续型变量都是偏态分布,所以用非参数检验
    by(tenure,Churn,median)
    wilcox.test(tenure~Churn,data=df)
    
    by(MonthlyCharges,Churn,median)
    wilcox.test(MonthlyCharges~Churn,data=df)
    
    by(TotalCharges,Churn,median)
    wilcox.test(TotalCharges~Churn,data=df)
    
    library(ggplot2)
    ggplot(df,aes(x=tenure,fill=Churn))+
      geom_bar(position = "dodge")+
      labs(title = "Churn BY tenure")
    

    在这里插入图片描述
    在这里插入图片描述
    这三个连续变量wilcox非参数检验P值都远小于0.01,所以认为流失和非流失的用户在这三个变量间都有差异。tenure这个变量,非流失用户存留时长的中位数为38个月,流失用户的存留时长为10个月,通过图形也可以看出前几个月尤其前6个月的流失率较高。

    3、多重对应分析

    library(plyr)
    df1 <- rename(df,c(SeniorCitizen="SC",Partner="P",Dependents="D",
                       OnlineSecurity="OS",OnlineBackup="OB",DeviceProtection="DP",
                       TechSupport="TS",StreamingTV="ST",StreamingMovies="SMo",
                       PaperlessBilling="PB",MonthlyCharges_c="MC",TotalCharges_c="TC",
                       MultipleLines="ML",InternetService="IS",Contract="Ctr",
                           PaymentMethod="PM")) #对变量进行重命名,使类别图更清晰
    library(ade4)
    mca <- dudi.acm(df1[3:19],scann = FALSE, nf = 2)
    co <- mca$co
    library(ggplot2)
    library(ggrepel)
    windows()
    ggplot(data=co,aes(x=Comp1,y=Comp2))+geom_point(shape=21,size=2.2,color="red")+
      theme(panel.background = element_rect(fill ="white",colour = "black"))+
      geom_vline(xintercept = 0, color = "gray", size = 0.5)+
      geom_hline(yintercept = 0, color = "gray", size = 0.5)+
      geom_text_repel(aes(Comp1,Comp2, label=rownames(co)),box.padding = unit(0.5,'lines'))+
      labs(x = "MCA1: 63.7%", y = "MCA2: 16.4%")  
    

    在这里插入图片描述
    用多重对应分析对各变量进行降维,降维信息浓缩后,相同方位距离近的特征可能有关联:
    ①、先看距离原点较远的聚集点:月费用最低等级(MC.1)和没有开通互联网服务(IS.No)相近有关联,确实符合实际情况逻辑;开通在线备份业务(OB.Yes)、开通设备保护业务(DP.Yes)、开通技术支持服务(TS.Yes)、开通网络安全服务(OS.Yes)相近,说明有一类客户很重视通信安全和通信数据保存,这类用户可能是商务用途,可以做一个商务组合套餐。
    ②、再看距离特别近的团簇:没有开通网络电视(ST.No)、没有开通网络电影(SMo.No)、第二等级总费用(TC.2)、没有开通多线业务(ML.No)相近,此类客户可能只开通了基础的功能。
    ③、接着看离Churn较近的点:银行自动转账付款方式(PM.Bank.transfer…automatic)、信用卡自动转账付款方式(PM.Credit.card…automatic)、有配偶(P.Yes)、按一年签订合同(Crt.One.year)、开通数字网络(IS.DSL)、有家属(D.Yes)和用户没有流失(Churn.No)相近,说明有这类特征的客户黏性高,较稳定,需要继续做好这类客户的维护和开发。
    ④、最后流失用户(Churn.Yes)周围没有很相近的点,而相对较近的特征有按月签订合同(Crt.Month.to.monthr)、电子支票付款方式(PM.Electronic.check)、老年人(SC.Yes),这类客户有流失的风险,可以出针对性的活动方案,提高这类客户的黏性。

    4、Logistic回归分析

    fit.full <- glm(Churn~.,data = mydata,family = binomial())
    summary(fit.full)
    fit.both <- step(fit.full,direction = "both")
    summary(fit.both)
    anova(fit.both,fit.full,test = "Chisq")
    

    数据集的自变量数目较多,为了使建立的Logistic回归模型比较稳定和便于解释,应尽可能地将回归效果不显著的自变量排除在外。用逐步法筛选变量后的模型和前面模型整体过滤时用的向后法结果是一样的

    coefficients(fit.both)
    exp(coef(fit.both))
    

    在这里插入图片描述
    用筛选后的变量建立逻辑回归模型。系统输出的是Churn=Yes时的概率模型,结果中Exp在0.5~1.5之间的弱影响因素暂时忽略。可以看到保持其他变量不变:
    ①、开通光纤互联网服务的客户流失风险是数字网络互联网服务的2.6倍,没有开通互联网服务的流失风险是数字网络互联网服务的0.3倍,所以没有开通互联网服务的客户流失风险最低。
    ②、按一年签订合同的客户流失风险是按月签订合同的0.4倍,按两年签订合同的客户流失风险是按月签订合同的0.2倍,所以签订合同的期限越长,客户流失的概率越小。
    ③、总费用第2等级的客户流失风险是第一等级(18.8,402] 的0.49倍,第三等级是第一等级的0.44倍,第四等级是第一等级的0.49倍,说明总费用第一等级,流失风险最大。

    小结:

    1. tenure前6个月是高流失区域,后面月份的用户流失逐渐减少。
    2. Contract签订合同方式对用户留存影响较大,流失风险按月签订>按一年签订>按两年签订,签订合同的期限越长,客户流失的概率越小。
    3. TotalCharges_c总费用等级高的用户是稳定的,花费较低的用户容易流失。
    4. PaymentMethod付款方式方面,电子支票付款方式流失率较高,自动化付款方式流失率低。
    5. InternetService网络服务对用户留存也影响较大,流失风险光纤互联网服务>数字网络服务>没有开通互联网。
    6. 一类客户倾向于开通在线备份业务、设备保护业务、技术支持服务、网络安全服务这些相关的安全服务

    分类与模型预测有效性

    set.seed(123)
    #建立训练集和测试集,用来建立模型和评估模型的有效性
    train <- sample(nrow(mydata),0.7*nrow(mydata))
    mydata.train <- mydata[train,]
    mydata.test <- mydata[-train,]
    table(mydata.train$Churn)
    table(mydata.test$Churn)
    #逻辑回归混淆矩阵
    prob <- predict(fit.both,mydata.test,type="response")
    logit.pred <- factor(prob>0.5,levels = c(FALSE,TRUE),labels = c("No","Yes"))
    logit.perf <- table(mydata.test$Churn,logit.pred)
    

    这分别是训练集和测试集的Churn分类,还有逻辑回归模型的混淆矩阵
    在这里插入图片描述
    在这里插入图片描述

    1、决策树和随机森林

    library(rpart)
    set.seed(123)
    dtree <- rpart(Churn~.,data = mydata.train,method = "class",
                   parms = list(split="information"))
    dtree$cptable
    plotcp(dtree)
    #按3次分割对应的复杂度参数0.01剪枝
    dtree.pruned <- prune(dtree,cp=0.01)
    
    library(partykit)
    plot(as.party(dtree.pruned),main="Decision Tree")
    dtree.pred <- predict(dtree.pruned,mydata.test,type="class")
    dtree.perf <- table(mydata.test$Churn,dtree.pred)
    

    在这里插入图片描述
    决策树的用纯度最大化法进行的变量重要性排序依次是:Contract、InternetService、tenure,按一年两年签订合同的客户稳定性更强;开通数字网络和没有开通网络服务的客户稳定性更强,存留时长大于13.5个月的客户稳定性更强。综合起来就是按月签订合同、开通光纤网络服务,存留时长小于13.5个月的客户容易流失

    #生成100棵决策树的随机森林
    fit.forest <- randomForest::randomForest(Churn~.,data = mydata.train,ntree=100)
    forest.pred <- predict(rf,mydata.test)
    (forest.perf <- table(mydata.test$Churn,forest.pred))
    

    在这里插入图片描述
    随机森林属于黑箱操作,无法做到和单一决策树一样的可解释性。

    2、支持向量机

    library(e1071)
    set.seed(123)
    fit.svm <- svm(Churn~.,data=mydata.train)
    fit.svm
    svm.pred <- predict(fit.svm,mydata.test)
    (svm.perf <- table(mydata.test$Churn,svm.pred))
    

    在这里插入图片描述
    同样缺乏可解释性,而且准确率较低

    3、模型预测有效性

    #定义分类器性能标准
    performance <- function(table,n=2) {
      if(!all(dim(table)==c(2,2)))
        stop("Must be a 2 x 2 table")
      tn = table[1,1]
      fp = table[1,2]
      fn = table[2,1]
      tp = table[2,2]
      sensitivity = tp/(tp+fn)
      specificity = tn/(tn+fp)
      ppp = tp/(tp+fp)
      npp = tn/(tn+fn)
      hitrate = (tn+tp)/(tn+fp+fn+tp)
      result <- cat(" Sensitivity =",round(sensitivity,n),
                      "\n","Specificity =",round(specificity,n),
                      "\n","Positive Predictive Value =",round(ppp,n),
                      "\n","Negative Predictive Value =",round(npp,n),
                    "\n","Accuracy=",round(hitrate,n),"\n")
      }
    performance(logit.perf)
    performance(dtree.perf)
    performance(forest.perf)
    performance(svm.perf)
    

    在这里插入图片描述
    这几个分类器的准确率都达到80%以上,但由于测试样本中流失率只有24%,所以是就算只有截距的机械模型,准确率也高达76%。从流失判断这个角度来说Sensitivity敏感度(即成功鉴别流失样本的概率)这一指标格外重要,逻辑回归这一指标数值稍高,达到54%,说明有54%的流失客户被判别出来了。

    总结与建议

    在这里插入图片描述

    1. 用户方面:针对老年用户、无亲属、无伴侣用户的特征推出定制服务如老年朋友套餐、温暖套餐等。鼓励用户加强关联,推出各种亲子套餐、情侣套餐等,满足客户的多样化需求。针对新注册用户,推送半年优惠如赠送消费券,以度过用户流失高峰期。
    2. 服务方面:针对光纤用户可以推出光纤和通讯组合套餐,对于连续开通半年以上的用户给与优惠减免。开通网络电视、电影的用户容易流失,需要研究这些用户的流失原因,是服务体验如观影流畅度清晰度等不好还是资源如片源等过少,再针对性的解决问题。针对在线安全、在线备份、设备保护、技术支持等增值服务,应重点对用户进行推广介绍,如首月/半年免费体验,使客户习惯并受益于这些服务
    3. 交易倾向方面:针对单月合同用户,建议推出年合同付费折扣活动,将月合同用户转化为年合同用户,提高用户存留时长,以减少用户流失。 针对采用电子支票支付用户,建议定向推送其它支付方式的优惠券,引导用户改变支付方式。对于开通电子账单的客户,可以在电子账单上增加等级积分等显示,等级升高可以免费享受增值服务,积分可以兑换某些日用商品。
    展开全文
  • 基于粒子分类优化的BP网络在电信客户流失预测中的应用
  • 针对电信客户流失数据集存在的数据维度过高及单一分类器预测效果较弱的问题,结合过滤式和封装式特征选择方法的优点及组合分类器的较高预测能力,提出了一种基于Fisher比率与预测风险准则的分步特征选择方法结合组合...
  • 原标题:实例 | 教你用Python写一个电信客户流失预测模型【导读】今天教大家如何用Python写一个电信用户流失预测模型。之前我们用Python写了员工流失预测模型,这次我们试试Python预测电信用户的流失。01 商业理解...

    原标题:实例 | 教你用Python写一个电信客户流失预测模型

    【导读】

    今天教大家如何用Python写一个电信用户流失预测模型。

    之前我们用Python写了员工流失预测模型,这次我们试试Python预测电信用户的流失。

    01 商业理解

    流失客户是指那些曾经使用过产品或服务,由于对产品失去兴趣等种种原因,不再使用产品或服务的顾客。

    电信服务公司、互联网服务提供商、保险公司等经常使用客户流失分析和客户流失率作为他们的关键业务指标之一,因为留住一个老客户的成本远远低于获得一个新客户。

    预测分析使用客户流失预测模型,通过评估客户流失的风险倾向来预测客户流失。由于这些模型生成了一个流失概率排序名单,对于潜在的高概率流失客户,他们可以有效地实施客户保留营销计划。

    下面我们就教你如何用Python写一个电信用户流失预测模型,以下是具体步骤和关键代码。

    02 数据理解

    此次分析数据来自于IBM Sample Data Sets,统计自某电信公司一段时间内的消费数据。共有7043笔客户资料,每笔客户资料包含21个字段,其中1个客户ID字段,19个输入字段及1个目标字段-Churn(Yes代表流失,No代表未流失),输入字段主要包含以下三个维度指标:用户画像指标、消费产品指标、消费信息指标。字段的具体说明如下:

    03 数据读入和概览

    首先导入所需包。

    # 数据处理

    import numpy as np

    import pandas as pd

    # 可视化

    import matplotlib.pyplot as plt

    import seaborn as sns

    import plotly as py

    import plotly.graph_objs as go

    import plotly.figure_factory as ff

    # 前处理

    from sklearn.preprocessing import LabelEncoder

    from sklearn.preprocessing import StandardScaler

    # 建模

    from sklearn.linear_model import LogisticRegression

    from sklearn.neighbors import KNeighborsClassifier

    from sklearn.tree import DecisionTreeClassifier

    from sklearn import tree

    from sklearn.ensemble import RandomForestClassifier

    from sklearn.naive_bayes import GaussianNB

    from sklearn.neural_network import MLPClassifier

    from sklearn.svm import SVC

    from lightgbm import LGBMClassifier

    from xgboost import XGBClassifier

    # 模型评估

    from sklearn.model_selection import train_test_split, GridSearchCV

    from sklearn.metrics import confusion_matrix, accuracy_score, classification_report

    from sklearn.metrics import roc_auc_score, roc_curve, scorer

    from sklearn.metrics import recall_score, precision_score, f1_score, cohen_kappa_score

    pd.set_option('display.max_columns', None)

    读入数据集

    df = pd.read_csv('./Telco-Customer-Churn.csv')

    df.head()

    04 数据初步清洗

    首先进行初步的数据清洗工作,包含错误值和异常值处理,并划分类别型和数值型字段类型,其中清洗部分包含:

    OnlineSecurity、OnlineBackup、DeviceProtection、TechSupport、StreamingTV、StreamingMovies:错误值处理

    TotalCharges:异常值处理

    tenure:自定义分箱

    定义类别型和数值型字段

    # 错误值处理

    repl_columns = ['OnlineSecurity', 'OnlineBackup', 'DeviceProtection',

    'TechSupport','StreamingTV', 'StreamingMovies']

    for i in repl_columns:

    df[i] = df[i].replace({'No internet service' : 'No'})

    # 替换值SeniorCitizen

    df["SeniorCitizen"] = df["SeniorCitizen"].replace({1: "Yes", 0: "No"})

    # 替换值TotalCharges

    df['TotalCharges'] = df['TotalCharges'].replace(' ', np.nan)

    # TotalCharges空值:数据量小,直接删除

    df = df.dropna(subset=['TotalCharges'])

    df.reset_index(drop=True, inplace=True) # 重置索引

    # 转换数据类型

    df['TotalCharges'] = df['TotalCharges'].astype('float')

    # 转换tenure

    def transform_tenure(x):

    if x <= 12:

    return 'Tenure_1'

    elif x <= 24:

    return 'Tenure_2'

    elif x <= 36:

    return 'Tenure_3'

    elif x <= 48:

    return 'Tenure_4'

    elif x <= 60:

    return 'Tenure_5'

    else:

    return 'Tenure_over_5'

    df['tenure_group'] = df.tenure.apply(transform_tenure)

    # 数值型和类别型字段

    Id_col = ['customerID']

    target_col = ['Churn']

    cat_cols = df.nunique()[df.nunique() < 10].index.tolist()

    num_cols = [i for i in df.columns if i not in cat_cols + Id_col]

    print('类别型字段:\n', cat_cols)

    print('-' * 30)

    print('数值型字段:\n', num_cols)

    类别型字段:

    ['gender', 'SeniorCitizen', 'Partner', 'Dependents', 'PhoneService', 'MultipleLines', 'InternetService', 'OnlineSecurity', 'OnlineBackup', 'DeviceProtection', 'TechSupport', 'StreamingTV', 'StreamingMovies', 'Contract', 'PaperlessBilling', 'PaymentMethod', 'Churn', 'tenure_group']

    ------------------------------

    数值型字段:

    ['tenure', 'MonthlyCharges', 'TotalCharges']

    05 探索性分析

    对指标进行归纳梳理,分用户画像指标,消费产品指标,消费信息指标。探索影响用户流失的关键因素。

    1. 目标变量Churn分布

    经过初步清洗之后的数据集大小为7032条记录,其中流失客户为1869条,占比26.6%,未流失客户占比73.4%。

    df['Churn'].value_counts()

    No 5163

    Yes 1869

    Name: Churn, dtype: int64

    trace0 = go.Pie(labels=df['Churn'].value_counts().index,

    values=df['Churn'].value_counts().values,

    hole=.5,

    rotation=90,

    marker=dict(colors=['rgb(154,203,228)', 'rgb(191,76,81)'],

    line=dict(color='white', width=1.3))

    )

    data = [trace0]

    layout = go.Layout(title='目标变量Churn分布')

    fig = go.Figure(data=data, layout=layout)

    py.offline.plot(fig, filename='./html/整体流失情况分布.html')

    2.性别

    分析可见,男性和女性在客户流失比例上没有显著差异。

    plot_bar(input_col='gender', target_col='Churn', title_name='性别与是否流失的关系')

    3. 老年用户

    老年用户流失比例更高,为41.68%,比非老年用户高近两倍,此部分原因有待进一步探讨。

    plot_bar(input_col='SeniorCitizen', target_col='Churn', title_name='老年用户与是否流失的关系')

    4. 是否有配偶

    从婚姻情况来看,数据显示,未婚人群中流失的比例比已婚人数高出13%。

    plot_bar(input_col='Partner', target_col='Churn', title_name='是否有配偶与是否流失的关系')

    5. 上网时长

    经过分析,这方面可以得出两个结论:

    用户的在网时长越长,表示用户的忠诚度越高,其流失的概率越低;

    新用户在1年内的流失率显著高于整体流失率,为47.68%。

    plot_bar(input_col='tenure_group', target_col='Churn', title_name='在网时长与是否流失的关系')

    6. 付款方式

    支付方式上,支付上,选择电子支票支付方式的用户流失最高,达到45.29%,其他三种支付方式的流失率相差不大。

    pd.crosstab(df['PaymentMethod'], df['Churn'])

    plot_bar(input_col='PaymentMethod', target_col='Churn', title_name='付款方式与是否流失关系')

    7. 月费用

    整体来看,随着月费用的增加,流失用户的比例呈现高高低低的变化,月消费80-100元的用户相对较高。

    plot_histogram(input_col='MonthlyCharges', title_name='月费用与是否流失关系')

    8. 数值型属性相关性

    从相关性矩阵图可以看出,用户的往来期间和总费用呈现高度相关,往来期间越长,则总费用越高。月消费和总消费呈现显著相关。

    plt.figure(figsize=(15, 10))

    sns.heatmap(df.corr(), linewidths=0.1, cmap='tab20c_r', annot=True)

    plt.title('数值型属性的相关性', fontdict={'fontsize': 'xx-large', 'fontweight':'heavy'})

    plt.xticks(fontsize=12)

    plt.yticks(fontsize=12)

    plt.show()

    06 特征选择

    使用统计检定方式进行特征筛选。

    # 删除tenure

    df = df.drop('tenure', axis=1)

    from feature_selection import Feature_select

    # 划分X和y

    X = df.drop(['customerID', 'Churn'], axis=1)

    y = df['Churn']

    fs = Feature_select(num_method='anova', cate_method='kf', pos_label='Yes')

    x_sel = fs.fit_transform(X, y)

    2020 09:30:02 INFO attr select success!

    After select attr: ['DeviceProtection', 'MultipleLines', 'OnlineSecurity', 'TechSupport', 'tenure_group', 'PaperlessBilling', 'InternetService', 'PaymentMethod', 'SeniorCitizen', 'MonthlyCharges', 'Dependents', 'Partner', 'Contract', 'StreamingTV', 'TotalCharges', 'StreamingMovies', 'OnlineBackup']

    经过特征筛选,gender和PhoneService字段被去掉。

    07 建模前处理

    在python中,为满足建模需要,一般需要对数据做以下处理:

    对于二分类变量,编码为0和1;

    对于多分类变量,进行one_hot编码;

    对于数值型变量,部分模型如KNN、神经网络、Logistic需要进行标准化处理。

    # 筛选变量

    select_features = x_sel.columns

    # 建模数据

    df_model = pd.concat([df['customerID'], df[select_features], df['Churn']], axis=1)

    Id_col = ['customerID']

    target_col = ['Churn']

    # 分类型

    cat_cols = df_model.nunique()[df_model.nunique() < 10].index.tolist()

    # 二分类属性

    binary_cols = df_model.nunique()[df_model.nunique() == 2].index.tolist()

    # 多分类属性

    multi_cols = [i for i in cat_cols if i not in binary_cols]

    # 数值型

    num_cols = [i for i in df_model.columns if i not in cat_cols + Id_col]

    # 二分类-标签编码

    le = LabelEncoder()

    for i in binary_cols:

    df_model[i] = le.fit_transform(df_model[i])

    # 多分类-哑变量转换

    df_model = pd.get_dummies(data=df_model, columns=multi_cols)

    df_model.head()

    08 模型建立和评估

    首先使用分层抽样的方式将数据划分训练集和测试集。

    # 重新划分

    X = df_model.drop(['customerID', 'Churn'], axis=1)

    y = df_model['Churn']

    # 分层抽样

    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0, stratify=y)

    print(X_train.shape, X_test.shape, y_train.shape, y_test.shape)

    #修正索引

    for i in [X_train, X_test, y_train, y_test]:

    i.index = range(i.shape[0])

    (5625, 31) (1407, 31) (5625,) (1407,)

    # 保存标准化训练和测试数据

    st = StandardScaler()

    num_scaled_train = pd.DataFrame(st.fit_transform(X_train[num_cols]), columns=num_cols)

    num_scaled_test = pd.DataFrame(st.transform(X_test[num_cols]), columns=num_cols)

    X_train_sclaed = pd.concat([X_train.drop(num_cols, axis=1), num_scaled_train], axis=1)

    X_test_sclaed = pd.concat([X_test.drop(num_cols, axis=1), num_scaled_test], axis=1)

    然后建立一系列基准模型并比较效果。

    假如我们关注roc指标,从模型表现效果来看,Naive Bayes效果最好。

    我们也可以对模型进行进一步优化,比如对决策树参数进行调优。

    parameters = {'splitter': ('best','random'),

    'criterion': ("gini","entropy"),

    "max_depth": [*range(3, 20)],

    }

    clf = DecisionTreeClassifier(random_state=25)

    GS = GridSearchCV(clf, parameters, scoring='f1', cv=10)

    GS.fit(X_train, y_train)

    print(GS.best_params_)

    print(GS.best_score_)

    {'criterion': 'entropy', 'max_depth': 5, 'splitter': 'best'}

    0.585900839405024

    clf = GS.best_estimator_

    test_pred = clf.predict(X_test)

    print('测试集:\n', classification_report(y_test, test_pred))

    测试集:

    precision recall f1-score support

    0 0.86 0.86 0.86 1033

    1 0.61 0.61 0.61 374

    accuracy 0.79 1407

    macro avg 0.73 0.73 0.73 1407

    weighted avg 0.79 0.79 0.79 1407

    将这颗树绘制出来。

    import graphviz

    dot_data = tree.export_graphviz(decision_tree=clf, max_depth=3,

    out_file=None,

    feature_names=X_train.columns,

    class_names=['not_churn', 'churn'],

    filled=True,

    rounded=True

    )

    graph = graphviz.Source(dot_data)

    输出决策树属性重要性排序:

    imp = pd.DataFrame(zip(X_train.columns, clf.feature_importances_))

    imp.columns = ['feature', 'importances']

    imp = imp.sort_values('importances', ascending=False)

    imp = imp[imp['importances'] != 0]

    table = ff.create_table(np.round(imp, 4))

    py.offline.iplot(table)

    后续优化方向:

    数据:分类技术应用在目标类别分布越均匀的数据集时,其所建立之分类器通常会有比较好的分类效能。针对数据在目标字段上分布不平衡,可采用过采样和欠采样来处理类别不平衡问题;

    属性:进一步属性筛选方法和属性组合;

    算法:参数调优;调整预测门槛值来增加预测效能。

    本文出品:CDA数据分析师(ID: cdacdacda)返回搜狐,查看更多

    责任编辑:

    展开全文
  • 使用电信客户流失数据确定功能重要性 杰夫·斯帕格诺拉 笔记: 该自述文件正在进行中,将很快完成。 介绍 客户流失是任何业务的主要关注点。 在当今世界,客户比以往任何时候都更加了解信息,并且只需在移动设备...
  • 针对数据挖掘方法在电信客户流失预测中的局限性,提出将信息融合与数据挖掘相结合,分别从数据层、特征层、决策层构建客户流失预测模型。确定客户流失预测指标;根据客户样本在特征空间分布的差异性对客户进行划分,...
  • 原标题:实例 | 教你用python写一个电信客户流失预测模型 CDA数据分析师 出品作者:真达、Mika数据:真达【导读】今天教大家如何用Python写一个电信用户流失预测模型。之前我们用Python写了员工流失预测模型,这次...

    原标题:实例 | 教你用python写一个电信客户流失预测模型

    CDA数据分析师 出品

    作者:真达、Mika

    数据:真达

    【导读】

    今天教大家如何用Python写一个电信用户流失预测模型。之前我们用Python写了员工流失预测模型,这次我们试试Python预测电信用户的流失。

    01、商业理解

    流失客户是指那些曾经使用过产品或服务,由于对产品失去兴趣等种种原因,不再使用产品或服务的顾客。

    电信服务公司、互联网服务提供商、保险公司等经常使用客户流失分析和客户流失率作为他们的关键业务指标之一,因为留住一个老客户的成本远远低于获得一个新客户。

    预测分析使用客户流失预测模型,通过评估客户流失的风险倾向来预测客户流失。由于这些模型生成了一个流失概率排序名单,对于潜在的高概率流失客户,他们可以有效地实施客户保留营销计划。

    下面我们就教你如何用Python写一个电信用户流失预测模型,以下是具体步骤和关键代码。

    02、数据理解

    此次分析数据来自于IBM Sample Data Sets,统计自某电信公司一段时间内的消费数据。共有7043笔客户资料,每笔客户资料包含21个字段,其中1个客户ID字段,19个输入字段及1个目标字段-Churn(Yes代表流失,No代表未流失),输入字段主要包含以下三个维度指标:用户画像指标、消费产品指标、消费信息指标。字段的具体说明如下:

    03、数据读入和概览

    首先导入所需包。

    # 数据处理

    import numpy as np

    import pandas as pd

    # 可视化

    import matplotlib.pyplot as plt

    import seaborn as sns

    import plotly as py

    import plotly.graph_objs as go

    import plotly.figure_factory as ff

    # 前处理

    from sklearn.preprocessing import LabelEncoder

    from sklearn.preprocessing import StandardScaler

    # 建模

    from sklearn.linear_model import LogisticRegression

    from sklearn.neighbors import KNeighborsClassifier

    from sklearn.tree import DecisionTreeClassifier

    from sklearn import tree

    from sklearn.ensemble import RandomForestClassifier

    from sklearn.naive_bayes import GaussianNB

    from sklearn.neural_network import MLPClassifier

    from sklearn.svm import SVC

    from lightgbm import LGBMClassifier

    from xgboost import XGBClassifier

    # 模型评估

    from sklearn.model_selection import train_test_split, GridSearchCV

    from sklearn.metrics import confusion_matrix, accuracy_score, classification_report

    from sklearn.metrics import roc_auc_score, roc_curve, scorer

    from sklearn.metrics import recall_score, precision_score, f1_score, cohen_kappa_score

    pd.set_option('display.max_columns', None)

    读入数据集

    df = pd.read_csv('./Telco-Customer-Churn.csv')

    df.head()

    04、数据初步清洗

    首先进行初步的数据清洗工作,包含错误值和异常值处理,并划分类别型和数值型字段类型,其中清洗部分包含:

    OnlineSecurity、OnlineBackup、DeviceProtection、TechSupport、StreamingTV、StreamingMovies:错误值处理

    TotalCharges:异常值处理

    tenure:自定义分箱

    定义类别型和数值型字段

    # 错误值处理

    repl_columns = ['OnlineSecurity', 'OnlineBackup', 'DeviceProtection',

    'TechSupport','StreamingTV', 'StreamingMovies']

    for i in repl_columns:

    df[i] = df[i].replace({'No internet service' : 'No'})

    # 替换值SeniorCitizen

    df["SeniorCitizen"] = df["SeniorCitizen"].replace({1: "Yes", 0: "No"})

    # 替换值TotalCharges

    df['TotalCharges'] = df['TotalCharges'].replace(' ', np.nan)

    # TotalCharges空值:数据量小,直接删除

    df = df.dropna(subset=['TotalCharges'])

    df.reset_index(drop=True, inplace=True) # 重置索引

    # 转换数据类型

    df['TotalCharges'] = df['TotalCharges'].astype('float')

    # 转换tenure

    def transform_tenure(x):

    if x <= 12:

    return 'Tenure_1'

    elif x <= 24:

    return 'Tenure_2'

    elif x <= 36:

    return 'Tenure_3'

    elif x <= 48:

    return 'Tenure_4'

    elif x <= 60:

    return 'Tenure_5'

    else:

    return 'Tenure_over_5'

    df['tenure_group'] = df.tenure.apply(transform_tenure)

    # 数值型和类别型字段

    Id_col = ['customerID']

    target_col = ['Churn']

    cat_cols = df.nunique()[df.nunique() < 10].index.tolist()

    num_cols = [i for i in df.columns if i not in cat_cols + Id_col]

    print('类别型字段:\n', cat_cols)

    print('-' * 30)

    print('数值型字段:\n', num_cols)

    类别型字段:

    ['gender', 'SeniorCitizen', 'Partner', 'Dependents', 'PhoneService',

    'MultipleLines', 'InternetService', 'OnlineSecurity',

    'OnlineBackup', 'DeviceProtection', 'TechSupport',

    'StreamingTV', 'StreamingMovies', 'Contract', 'PaperlessBilling',

    'PaymentMethod', 'Churn', 'tenure_group']

    ------------------------------

    数值型字段:

    ['tenure', 'MonthlyCharges', 'TotalCharges']

    05、探索性分析

    对指标进行归纳梳理,分用户画像指标,消费产品指标,消费信息指标。探索影响用户流失的关键因素。

    1. 目标变量Churn分布

    经过初步清洗之后的数据集大小为7032条记录,其中流失客户为1869条,占比26.6%,未流失客户占比73.4%。

    df['Churn'].value_counts()

    No 5163

    Yes 1869

    Name: Churn, dtype: int64

    trace0 = go.Pie(labels=df['Churn'].value_counts().index,

    values=df['Churn'].value_counts().values,

    hole=.5,

    rotation=90,

    marker=dict(colors=['rgb(154,203,228)', 'rgb(191,76,81)'],

    line=dict(color='white', width=1.3))

    )

    data = [trace0]

    layout = go.Layout(title='目标变量Churn分布')

    fig = go.Figure(data=data, layout=layout)

    py.offline.plot(fig, filename='./html/整体流失情况分布.html')

    2.性别

    分析可见,男性和女性在客户流失比例上没有显著差异。

    plot_bar(input_col='gender', target_col='Churn', title_name='性别与是否流失的关系')

    3. 老年用户

    老年用户流失比例更高,为41.68%,比非老年用户高近两倍,此部分原因有待进一步探讨。

    plot_bar(input_col='SeniorCitizen', target_col='Churn', title_name='老年用户与是否流失的关系')

    4. 是否有配偶

    从婚姻情况来看,数据显示,未婚人群中流失的比例比已婚人数高出13%。

    plot_bar(input_col='Partner', target_col='Churn', title_name='是否有配偶与是否流失的关系')

    5. 上网时长

    经过分析,这方面可以得出两个结论:

    用户的在网时长越长,表示用户的忠诚度越高,其流失的概率越低;

    新用户在1年内的流失率显著高于整体流失率,为47.68%。

    plot_bar(input_col='tenure_group', target_col='Churn', title_name='在网时长与是否流失的关系')

    6. 付款方式

    支付方式上,支付上,选择电子支票支付方式的用户流失最高,达到45.29%,其他三种支付方式的流失率相差不大。

    pd.crosstab(df['PaymentMethod'], df['Churn'])

    plot_bar(input_col='PaymentMethod', target_col='Churn', title_name='付款方式与是否流失关系')

    7. 月费用

    整体来看,随着月费用的增加,流失用户的比例呈现高高低低的变化,月消费80-100元的用户相对较高。

    plot_histogram(input_col='MonthlyCharges', title_name='月费用与是否流失关系')

    8. 数值型属性相关性

    从相关性矩阵图可以看出,用户的往来期间和总费用呈现高度相关,往来期间越长,则总费用越高。月消费和总消费呈现显著相关。

    plt.figure(figsize=(15, 10))

    sns.heatmap(df.corr(), linewidths=0.1, cmap='tab20c_r', annot=True)

    plt.title('数值型属性的相关性', fontdict={'fontsize': 'xx-large', 'fontweight':'heavy'})

    plt.xticks(fontsize=12)

    plt.yticks(fontsize=12)

    plt.show()

    06、特征选择

    使用统计检定方式进行特征筛选。

    # 删除tenure

    df = df.drop('tenure', axis=1)

    from feature_selection import Feature_select

    # 划分X和y

    X = df.drop(['customerID', 'Churn'], axis=1)

    y = df['Churn']

    fs = Feature_select(num_method='anova', cate_method='kf', pos_label='Yes')

    x_sel = fs.fit_transform(X, y)

    2020 09:30:02 INFO attr select success!

    After select attr: ['DeviceProtection', 'MultipleLines', 'OnlineSecurity',

    'TechSupport', 'tenure_group', 'PaperlessBilling',

    'InternetService', 'PaymentMethod', 'SeniorCitizen',

    'MonthlyCharges', 'Dependents', 'Partner', 'Contract',

    'StreamingTV', 'TotalCharges', 'StreamingMovies', 'OnlineBackup']

    经过特征筛选,gender和PhoneService字段被去掉。

    07、建模前处理

    在python中,为满足建模需要,一般需要对数据做以下处理:

    对于二分类变量,编码为0和1;

    对于多分类变量,进行one_hot编码;

    对于数值型变量,部分模型如KNN、神经网络、Logistic需要进行标准化处理。

    # 筛选变量

    select_features = x_sel.columns

    # 建模数据

    df_model = pd.concat([df['customerID'], df[select_features], df['Churn']], axis=1)

    Id_col = ['customerID']

    target_col = ['Churn']

    # 分类型

    cat_cols = df_model.nunique()[df_model.nunique() < 10].index.tolist()

    # 二分类属性

    binary_cols = df_model.nunique()[df_model.nunique() == 2].index.tolist()

    # 多分类属性

    multi_cols = [i for i in cat_cols if i not in binary_cols]

    # 数值型

    num_cols = [i for i in df_model.columns if i not in cat_cols + Id_col]

    # 二分类-标签编码

    le = LabelEncoder()

    for i in binary_cols:

    df_model[i] = le.fit_transform(df_model[i])

    # 多分类-哑变量转换

    df_model = pd.get_dummies(data=df_model, columns=multi_cols)

    df_model.head()

    08、模型建立和评估

    首先使用分层抽样的方式将数据划分训练集和测试集。

    # 重新划分

    X = df_model.drop(['customerID', 'Churn'], axis=1)

    y = df_model['Churn']

    # 分层抽样

    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0, stratify=y)

    print(X_train.shape, X_test.shape, y_train.shape, y_test.shape)

    #修正索引

    for i in [X_train, X_test, y_train, y_test]:

    i.index = range(i.shape[0])

    (5625, 31) (1407, 31) (5625,) (1407,)

    # 保存标准化训练和测试数据

    st = StandardScaler()

    num_scaled_train = pd.DataFrame(st.fit_transform(X_train[num_cols]), columns=num_cols)

    num_scaled_test = pd.DataFrame(st.transform(X_test[num_cols]), columns=num_cols)

    X_train_sclaed = pd.concat([X_train.drop(num_cols, axis=1), num_scaled_train], axis=1)

    X_test_sclaed = pd.concat([X_test.drop(num_cols, axis=1), num_scaled_test], axis=1)

    然后建立一系列基准模型并比较效果。

    假如我们关注roc指标,从模型表现效果来看,Naive Bayes效果最好。我们也可以对模型进行进一步优化,比如对决策树参数进行调优。

    parameters = {'splitter': ('best','random'),

    'criterion': ("gini","entropy"),

    "max_depth": [*range(3, 20)],

    }

    clf = DecisionTreeClassifier(random_state=25)

    GS = GridSearchCV(clf, parameters, scoring='f1', cv=10)

    GS.fit(X_train, y_train)

    print(GS.best_params_)

    print(GS.best_score_)

    {'criterion': 'entropy', 'max_depth': 5, 'splitter': 'best'}

    0.585900839405024

    clf = GS.best_estimator_

    test_pred = clf.predict(X_test)

    print('测试集:\n', classification_report(y_test, test_pred))

    测试集:

    precision recall f1-score support

    0 0.86 0.86 0.86 1033

    1 0.61 0.61 0.61 374

    accuracy 0.79 1407

    macro avg 0.73 0.73 0.73 1407

    weighted avg 0.79 0.79 0.79 1407

    将这棵树绘制出来。

    import graphviz

    dot_data = tree.export_graphviz(decision_tree=clf, max_depth=3,

    out_file=None,

    feature_names=X_train.columns,

    class_names=['not_churn', 'churn'],

    filled=True,

    rounded=True

    )

    graph = graphviz.Source(dot_data)

    输出决策树属性重要性排序:

    imp = pd.DataFrame(zip(X_train.columns, clf.feature_importances_))

    imp.columns = ['feature', 'importances']

    imp = imp.sort_values('importances', ascending=False)

    imp = imp[imp['importances'] != 0]

    table = ff.create_table(np.round(imp, 4))

    py.offline.iplot(table)

    后续优化方向:

    数据:分类技术应用在目标类别分布越均匀的数据集时,其所建立之分类器通常会有比较好的分类效能。针对数据在目标字段上分布不平衡,可采用过采样和欠采样来处理类别不平衡问题;

    属性:进一步属性筛选方法和属性组合;

    算法:参数调优;调整预测门槛值来增加预测效能。

    责任编辑:

    展开全文
  • Kaggle-电信客户流失分析及应对措施-随机森林分析原因分析目标分析背景数据集内容分析流程分析结果制定策略对流失用户进行召回总结 分析原因 在产品使用过程中,一些用户会因为各式各样的原因而不再使用我们的产品, ...
  • 数据仓库在电信客户流失分析中的应用,吴琼,姚军剑,本文介绍了数据仓库技术和数据挖掘技术的相关知识,介绍了客户流失分析专题相关概念和应用数据仓库技术进行数据处理流程,最后选
  • 电信客户流失数据分析(一)

    千次阅读 2020-04-20 22:41:50
    目录来做个数据分析项目^-^任务1:探索数据集任务2:哪些输入特征与顾客流失具有关联性? 来做个数据分析项目- 背景:在kaggle网站上发现了这个数据集,就顺手拿来做个数据分析...电信客户流失数据集共7043条记录,...
  • 针对客户流失数据集的非平衡性问题和错分代价的差异性问题,将代价敏感学习应用于Veropoulos提出的采用不同惩罚系数的支持向量机,建立客户流失预测模型,对实际的电信客户流失数据进行验证。通过与传统SVM、C4.5和...
  • 电信客户流失预测模型设计与实现,工作中总结的
  • 针对电信领域客户流失的问题,提出了改进聚类的客户流失预测模型。...通过实际电信客户数据集测试,与传统的预测算法比较,证明这种算法适合解决大数据集和不平衡数据,具有更高的精确度,能够取得较好的客户流失预测效果。
  • 用数据挖掘法分析电信客户流失,很实用,建议下载看看
  • 电信客户流失预测

    2021-02-03 10:27:09
    CustomerID 客户ID Gender 性别 partneratt 配偶是否也为att用户 dependents_att 家人是否也是att用户 landline 是否使用att固话服务 internet_att/internet_other 是否使用att的互联网...
  • 数据挖掘在电信客户流失中的应用很实用,建议下载看看
  • 基于贝叶斯网络的电信客户流失预测分析 很好

空空如也

空空如也

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

电信客户流失