当前位置:   article > 正文

电信用户流失预测Telco customer churn—(基于逻辑回归)_telecom churn

telecom churn

1. 项目背景

在电信用户运营中如何有效预测用户流失,了解潜在流失用户的流失概率,对潜在流失客户做特征分析和流失原因分析,将有助于运营童鞋发现改善用户体验的抓手,和确定挽留目标用户并制定有效方案

2. 提出问题

1、哪些用户可能会流失?

2、流失概率更高的用户有什么共同特征?

根据提出的问题可以对用户数据做如下分析,本文只进行用户流失预测模型

3. 理解数据

以下是数据指标的内容和含义

4. 数据清洗

4.1 导入数据并查看数据

python金融风控评分卡模型和数据分析(加强版)
入口1(推荐)
https://ke.qq.com/course/package/43071
入口2
https://study.163.com/series/1202915601.htm?share=2&shareId=400000000398149
import warnings #忽略警告提示
warnings.filterwarnings('ignore')
import os
import numpy as np
from scipy import  stats
import pandas as pd
import statsmodels.api as sm
import statsmodels.formula.api as smf
import matplotlib.pyplot as plt

# 导入数据和查看数据
os.chdir(r'……\数据分析案例')
churn=pd.read_csv(r'telecom_churn.csv')
print(churn.head(),'\n)
print(churn.shape)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

打印结果

打印结果

从数据查看的结果了解到:

数据一共有3463个样本,20个数据指标,且没有缺失数据

4.2 选取部分字段进行卡方检验

# 检验该用户通话时长是否呈现出上升态势(posTrend)对流失(churn) 是否有预测价值
# 分类变量的相关关系
cross_table=pd.crosstab(churn.posTrend,churn.churn,margins=True)
print(cross_table)

# 列联表
def percConvert(ser):
    return ser/float(ser[-1])
cross_table.apply(percConvert,axis=1)

# 卡方检验
print('''chisq=%6.4f
p_value=%6.4f
dof=%i
expected_freq=%s'''%stats.chi2_contingency(cross_table.iloc[:2,:2]))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

打印结果

卡方检验的H0假设为:用户通话时长趋势与用户流失无关,从p值的结果为0,因此拒绝H0假

5. 构建模型

流失预测模型

建立训练数据集和测试数据集

# 随机抽样,建立训练集与测试集
train=churn.sample(frac=0.75,random_state=1234).copy()
test=churn[~churn.index.isin(train.index)].copy()
print(' 训练集样本量: %i \n 测试集样本量: %i' %(len(train), len(test)))
  • 1
  • 2
  • 3
  • 4

打印结果

5.1 模型一

# 相关性矩阵
corrDf = churn.corr(method='pearson')
corrDf
# 查看各个特征与生存情况的相关系数
corrDf['churn'].sort_values(ascending =False)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

#  使用广义线性回归模型建模
lg=smf.glm('churn~duration',data=train,
          family=sm.families.Binomial(sm.families.links.logit)).fit()
print(lg.summary())
  • 1
  • 2
  • 3
  • 4

从各自变量和因变量的相关系数看,只有duration自变量和churn因变量中度相关。

使用duration自变量对churn因变量建立回归模型,回归系数P值均通过检验

5.2 模型二

检验模型的膨胀系数,再使用向前逐步法从其它备选变量中选择变量,构建基于AIC的最优模型

# 计算膨胀因子
candidates=['churn','duration','AGE','edu_class','posTrend',
           'negTrend','nrProm','prom','curPlan','avgplan',
            'planChange','incomeCode','feton','peakMinAv',
            'peakMinDiff','call_10086']
def vif(df,col_i):
    from statsmodels.formula.api import ols
    cols=list(df.columns)
    cols.remove(col_i)
    cols_noti=cols
    formula=col_i+'~'+'+'.join(cols_noti)
    r2=ols(formula,df).fit().rsquared
    return 1/(1-r2)
exog=train[candidates].drop(['churn'],axis=1)
for i in exog.columns:
    print(i,'\t',vif(df=exog,col_i=i))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

从膨胀因子VIF看,posTrend,negTrend;nrProm,prom;curPlan,avgplan有明显的共线性问题,剔除其中三个变量后再建模

# 向前逐步法
def forward_select(data,response):
    remaining=set(data.columns)
    remaining.remove(response)
    selected=[]
    current_score,best_new_score=float('inf'),float('inf')
    while remaining:
        aic_with_candidates=[]
        for candidate in remaining:
            formula='{}~{}'.format(
                response,'+'.join(selected+[candidate]))
            aic=smf.glm(
                formula=formula,data=data,
                family=sm.families.Binomial(sm.families.links.logit)
            ).fit().aic
            aic_with_candidates.append((aic,candidate))
        aic_with_candidates.sort(reverse=True)
        best_new_score,best_candidate=aic_with_candidates.pop()
        if current_score>best_new_score:
            remaining.remove(best_candidate)
            selected.append(best_candidate)
            current_score=best_new_score
            print('aic is {},continuing!'.format(current_score))
        else:
            print('forward selection over!')
            break
    formula='{}~{}'.format(response,'+'.join(selected))
    print('final formula is {}'.format(formula))
    model=smf.glm(
        formula=formula,data=data,
        family=sm.families.Binomial(sm.families.links.logit)
    ).fit()
    return(model)
# 建模
candidates=['churn','duration','AGE','edu_class','posTrend',
            'prom','curPlan','planChange','incomeCode','feton',
            'peakMinAv','peakMinDiff','call_10086']
data_for_select=train[candidates]
lg_m2=forward_select(data=data_for_select,response='churn')
print(lg_m2.summary())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

系数的P值均通过检验

5.3 模型三

使用lasso回归建立模型,lasso回归使用L1正则化,并使用交叉验证法确定惩罚参数(C值),

from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
candidates=['duration','AGE','edu_class','posTrend',
            'negTrend','nrProm','prom','curPlan','avgplan',
            'planChange','incomeCode','feton','peakMinAv',
            'peakMinDiff','call_10086']
scaler=StandardScaler()# 标准化
X=scaler.fit_transform(churn[candidates])
y=churn['churn']

# 正则化
from sklearn import linear_model
from sklearn.svm import l1_min_c
import datetime
cs=l1_min_c(X,y,loss='log')*np.logspace(0,4)
print('Computing regularization path …')

start = datetime.datetime.now()
clf=linear_model.LogisticRegression(C=1,
                                   penalty='l1',tol=1e-6)
coefs_=[]
for c in cs:
    clf.set_params(C=c)
    clf.fit(X,y)
    coefs_.append(clf.coef_.ravel().copy())
print("This took ", datetime.datetime.now() - start)

# 绘图
coefs_=np.array(coefs_)
plt.plot(np.log10(cs),coefs_)
ymin, ymax = plt.ylim()
plt.xlabel('log(C)')
plt.ylabel('Coefficients')
plt.title('Logistic Regression Path')
plt.axis('tight')
plt.show()

# 交叉验证参数
cs=l1_min_c(X,y,loss='log')*np.logspace(0,4)
import matplotlib.pyplot as plt
from sklearn.model_selection import cross_val_score # K折交叉验证

k_scores=[]
clf=linear_model.LogisticRegression(penalty='l1')
# 由迭代的方式来计算不同参数对模型的影响,并返回交叉验证后的平均准确率  
for c in cs:
    clf.set_params(C=c)
    scores=cross_val_score(clf,X,y,cv=10,scoring='roc_auc')
    k_scores.append([c,scores.mean(),scores.std()])
    
# 可视化
data=pd.DataFrame(k_scores) # 将字典转换成为数据框
fig=plt.figure()
ax1=fig.add_subplot(111)
ax1.plot(np.log10(data[0]),data[1],'b')
ax1.set_ylabel('log10(cs)')
ax2=ax1.twinx()
ax2.plot(np.log10(data[0]),data[2],'r')
ax2.set_ylabel('Std ROC Index(Red)')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

从方差曲线看,C值在-1.9左右方差最小,因此确定-1.9做为C值的最佳选择

# 实现Lasso算法
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
candidates=['duration','AGE','edu_class','posTrend','negTrend',
            'nrProm','prom','curPlan','avgplan','planChange',
            'incomeCode','feton','peakMinAv','peakMinDiff','call_10086']
scaler=StandardScaler() # 标准化
X=scaler.fit_transform(train[candidates])
y=train['churn']

from sklearn import linear_model
clf=linear_model.LogisticRegression(C=np.exp(-1.9),penalty='l1')
clf.fit(X,y)
print(clf.coef_)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

自变量系数

使用L1正则化,得到2个变量的系数为0

5.4 模型评估

from sklearn import metrics

# lg模型预测
train['lg_proba']=lg.predict(train)
test['lg_proba']=lg.predict(test)

# lg_m2模型预测
train['lg_m2_proba']=lg_m2.predict(train)
test['lg_m2_proba']=lg_m2.predict(test)

# clf模型预测
X1=scaler.fit_transform(test[candidates])
y1=test['churn']
train['clf_proba']=clf.predict(X)
test['clf_proba']=clf.predict(X1)

# 计算准确率
lg_acc=sum(test['lg_prediction']==test['churn'])/np.float(len(test))
lg_m2_acc=sum(test['lg_m2_prediction']==test['churn'])/np.float(len(test))
clf_acc=sum(test['clf_prediction']==test['churn'])/np.float(len(test))

print('The accurancy of lg is %.2f'%lg_acc,'\n')
print('The accurancy of lg_m2 is %.2f'%lg_m2_acc,'\n')
print('The accurancy of clf is %.2f'%clf_acc,'\n')

i=0.5  #  设定阈值
lg_prediction=(test['lg_proba']>i).astype('int')
lg_m2_prediction=(test['lg_m2_proba']>i).astype('int')
clf_prediction=(test['clf_proba']>i).astype('int')
 
# 混淆矩阵   
lg_confusion_matrix=pd.crosstab(lg_prediction,test.churn,margins=True)
lg_m2_confusion_matrix=pd.crosstab(lg_m2_prediction,test.churn,margins=True)
clf_confusion_matrix=pd.crosstab(clf_prediction,test.churn,margins=True)

 # 计算评估指标    
print('lg评估指标','\n',metrics.classification_report(test.churn, lg_prediction)) 
print('lg_m2评估指标','\n',metrics.classification_report(test.churn, lg_m2_prediction))  
print('clf评估指标','\n',metrics.classification_report(test.churn, clf_prediction)) 

# 绘制Roc曲线    
import sklearn.metrics as metrics

lg_fpr_test,lg_tpr_test,lg_th_test=metrics.roc_curve(test.churn,test.lg_proba)
lg_fpr_train,lg_tpr_train,lg_th_train=metrics.roc_curve(train.churn,train.lg_proba)

lg_m2_fpr_test,lg_m2_tpr_test,lg_m2_th_test=metrics.roc_curve(test.churn,test.lg_m2_proba)
lg_m2_fpr_train,lg_m2_tpr_train,lg_m2_th_train=metrics.roc_curve(train.churn,train.lg_m2_proba)

clf_fpr_test,clf_tpr_test,clf_th_test=metrics.roc_curve(test.churn,test.clf_proba)
clf_fpr_train,clf_tpr_train,clf_th_train=metrics.roc_curve(train.churn,train.clf_proba)

plt.subplots_adjust(hspace=0.3,wspace=0.3)

plt.subplot(221)
plt.plot(lg_fpr_test,lg_tpr_test,'b--',label='test')
plt.plot(lg_fpr_train,lg_tpr_train,'r--',label='train')
plt.title('lg_ROC curve')
plt.legend(loc='best')

plt.subplot(222)
plt.plot(lg_m2_fpr_test,lg_m2_tpr_test,'b--',label='test')
plt.plot(lg_m2_fpr_train,lg_m2_tpr_train,'r--',label='train')
plt.title('lg_m2_ROC curve')
plt.legend(loc='best')

plt.subplot(223)
plt.plot(clf_fpr_test,clf_tpr_test,'b--',label='test')
plt.plot(clf_fpr_train,clf_tpr_train,'r--',label='train')
plt.title('clf_ROC curve')
plt.legend(loc='best')

plt.subplot(224)
plt.plot(lg_fpr_test,lg_tpr_test,'b--',label='lg')
plt.plot(lg_m2_fpr_test,lg_m2_tpr_test,'r--',label='lg_m2')
plt.plot(clf_fpr_test,clf_tpr_test,'g--',label='cfl')
plt.title('ROC_test curve')
plt.legend(loc='best')
plt.subplots_adjust(hspace=0.5,wspace=0.3)
plt.show()

print('lg_AUC=%.4f'%metrics.auc(lg_fpr_test,lg_tpr_test))
print('lg_m2_AUC=%.4f'%metrics.auc(lg_m2_fpr_test,lg_m2_tpr_test))
print('clf_AUC=%.4f'%metrics.auc(clf_fpr_test,clf_tpr_test))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84

lg_m2模型和clf模型的正确率较高,正确率达到80%,且这两个模型的精确率和召回率差不多。

三个模型训练数据和测试数据曲线趋于重合,不存在过拟合的情况。

从三个模型的测试数据的ROC曲线和AUC值看,lg_m2模型优于其他模型。

转载https://zhuanlan.zhihu.com/p/58193546

总结:此文章对机器学习建模流程有一个清晰解读,建议大家学习。从细节角度看,很多微观地方处理过于机械,针对不同算法有不同处理方法,大家不要生硬模仿。欢迎同学关注《python金融风控评分卡模型和数据分析(加强版)》学习专业建模知识。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/584268
推荐阅读
相关标签
  

闽ICP备14008679号