当前位置:   article > 正文

aic python_python机器学习案例(五):逻辑回归电信客户流失建模

电信行业客户数据集逻辑回归

874c7a33cca33d2cb575d67c6c4e8e45.png

本逻辑回归电信客户流失建模案例代码及数据集链接及下载密码:关注公众号书豪创投笔记并回复python数据科学即可获取

e3ba2c7f0efd48dc63c881a4c87becff.png
关注公众号书豪创投笔记并回复python数据科学即可获取数据集

本案例来源乃是学习朋友常国珍老师的python数据科学书,内容非常实用!希望深入浅出学习机器学习的同学可学习北大博士常国珍建模实战课程系列常国珍录制的Python 数据科学机器学习教学视频,可以由浅入深的学习机器学习!

逻辑回归案例字段解释
#subscriberID="个人客户的ID"
#churn="是否流失:1=流失";
#Age="年龄"
#incomeCode="用户居住区域平均收入的代码"
#duration="在网时长"
#peakMinAv="统计期间内最高单月通话时长"
#peakMinDiff="统计期间结束月份与开始月份相比通话时长增加数量"
#posTrend="该用户通话时长是否呈现出上升态势:是=1"
#negTrend="该用户通话时长是否呈现出下降态势:是=1"
#nrProm="电话公司营销的数量"
#prom="最近一个月是否被营销过:是=1"
#curPlan="统计时间开始时套餐类型:1=最高通过200分钟;2=300分钟;3=350分钟;4=500分钟"
#avPlan="统计期间内平均套餐类型"
#planChange="统计结束时和开始时套餐的变化:正值代表套餐档次提升,负值代表下降,0代表不变"
#posPlanChange="统计期间是否提高套餐:1=是"
#negPlanChange="统计期间是否降低套餐:1=是"
#call_10086="拨打10086的次数"

  1. import os
  2. import numpy as np
  3. from scipy import stats
  4. import pandas as pd
  5. import statsmodels.api as sm
  6. import statsmodels.formula.api as smf
  7. import matplotlib.pyplot as plt
  8. churn = pd.read_csv(r'telecom_churn.csv', skipinitialspace=True)
  9. churn.head()

54ed5a8ad569722e6db170f51eb8f16a.png
  1. #1两变量分析:检验该用户通话时长是否呈现出上升态势(posTrend)对流失(churn) 是否有预测价值
  2. # ## 分类变量的相关关系
  3. #
  4. # 交叉表
  5. cross_table = pd.crosstab(churn.posTrend,
  6. churn.churn, margins=True)
  7. cross_table

8960c4a806f8d65a8dc0d62a9a9b5bea.png
交叉表
  1. # 列联表
  2. def percConvert(ser):
  3. return ser/float(ser[-1])
  4. cross_table.apply(percConvert, axis=1)

523a1b6aa1e4353a12d330afac15e454.png
  1. #卡方检验走一波
  2. print('''chisq = %6.4f
  3. p-value = %6.4f
  4. dof = %i
  5. expected_freq = %s''' %stats.chi2_contingency(cross_table.iloc[:2, :2]))

8f63c71c217c1d1e29168a4ecb38c5d4.png
卡方检验走一波告诉俺们显著的!
  1. #2首先将原始数据拆分为训练和测试数据集,使用训练数据集建立在网时长对流失的逻辑回归,使用测试数据集制作混淆矩阵
  2. #(阈值为0.5),提供准确性、召回率指标,提供ROC曲线和AUC。
  3. # ## 逻辑回归
  4. # duration 在网时长 这是个连续变量 !
  5. churn.plot(x='duration', y='churn', kind='scatter')

a9e316bf8dea2ebb395bd20a5a0c8ba9.png
在网时长与因变量是否流失的关系非常显著
  1. # •随机抽样,建立训练集与测试集
  2. # In[10]:
  3. train = churn.sample(frac=0.7, random_state=1234).copy()
  4. test = churn[~ churn.index.isin(train.index)].copy()
  5. print(' 训练集样本量: %i n 测试集样本量: %i' %(len(train), len(test)))

8e043fbc760da93ebe7558b0796b32a0.png
train占比0.7
  1. # 在网时长与因变量用户流失进行建模
  2. lg = smf.glm('churn ~ duration', data=train,
  3. family=sm.families.Binomial(sm.families.links.logit)).fit()
  4. lg.summary()

c51f4eabd266396b395378edfe500e46.png
在网时长与因变量用户流失是负相关并且是显著的!
  1. # 预测
  2. train['proba'] = lg.predict(train)
  3. test['proba'] = lg.predict(test)
  4. test['proba'].head()

6ba3e692f26737888ae468a2883e2998.png

以0.5为阈值建立混淆矩阵

  1. # ## 模型评估
  2. # 设定阈值来一波!
  3. # 以0.5为阈值建立混淆矩阵
  4. test['prediction'] = (test['proba'] > 0.5).astype('int')

混淆矩阵

  1. # 混淆矩阵
  2. pd.crosstab(test.churn, test.prediction, margins=True)

e960eb86607b197a8ef46fdb30992985.png
混淆矩阵
  1. # - 计算准确率
  2. acc = sum(test['prediction'] == test['churn']) /np.float(len(test))
  3. print('The accurancy is %.2f' %acc)

0d8634113f98a333410f8edcdd9efc19.png
  1. """
  2. for i in np.arange(0.1, 0.9, 0.1):
  3. prediction = (test['proba'] > i).astype('int')
  4. confusion_matrix = pd.crosstab(prediction,test.churn,
  5. margins = True)
  6. precision = confusion_matrix.ix[0, 0] /confusion_matrix.ix['All', 0]
  7. recall = confusion_matrix.ix[0, 0] / confusion_matrix.ix[0, 'All']
  8. Specificity = confusion_matrix.ix[1, 1] /confusion_matrix.ix[1,'All']
  9. f1_score = 2 * (precision * recall) / (precision + recall)
  10. print('threshold: %s, precision: %.2f, recall:%.2f ,Specificity:%.2f , f1_score:%.2f'%(i, precision, recall, Specificity,f1_score))
  11. """

metrics包里有roc_curve函数来绘制ROC曲线

  1. # - 绘制ROC曲线
  2. import sklearn.metrics as metrics
  3. fpr_test, tpr_test, th_test = metrics.roc_curve(test.churn, test.proba)
  4. fpr_train, tpr_train, th_train = metrics.roc_curve(train.churn, train.proba)
  5. plt.figure(figsize=[3, 3])
  6. plt.plot(fpr_test, tpr_test, 'b--') #blue就是蓝色的意思
  7. plt.plot(fpr_train, tpr_train, 'r-')#red 就是train 的表现
  8. plt.title('ROC curve')
  9. plt.show()

a87e3f94207ef7bda6b0dabfba1acf9b.png

train和test这两个线都是非常接近的,是不存在过度拟合的!

  1. # AUC是ROC曲线面积
  2. print('AUC = %.4f' %metrics.auc(fpr_test, tpr_test))

2e544c583e0c193b2db038c30f0b5043.png

ROC曲线面积是0.8790,模型非常好了!

把所有的X都放入模型

使用向前逐步法从其它备选变量中选择变量,构建基于AIC的最优模型,绘制ROC曲线,同时检验模型的膨胀系数。

  1. #3使用向前逐步法从其它备选变量中选择变量,构建基于AIC的最优模型,绘制ROC曲线,同时检验模型的膨胀系数。
  2. # In[14]:
  3. #- 多元逻辑回归
  4. # 向前法
  5. def forward_select(data, response):
  6. remaining = set(data.columns)
  7. remaining.remove(response)
  8. selected = []
  9. current_score, best_new_score = float('inf'), float('inf')
  10. while remaining:
  11. aic_with_candidates=[]
  12. for candidate in remaining:
  13. formula = "{} ~ {}".format(
  14. response,' + '.join(selected + [candidate]))
  15. aic = smf.glm(
  16. formula=formula, data=data,
  17. family=sm.families.Binomial(sm.families.links.logit)
  18. ).fit().aic
  19. aic_with_candidates.append((aic, candidate))
  20. aic_with_candidates.sort(reverse=True)
  21. best_new_score, best_candidate=aic_with_candidates.pop()
  22. if current_score > best_new_score:
  23. remaining.remove(best_candidate)
  24. selected.append(best_candidate)
  25. current_score = best_new_score
  26. print ('aic is {},continuing!'.format(current_score))
  27. else:
  28. print ('forward selection over!')
  29. break
  30. formula = "{} ~ {} ".format(response,' + '.join(selected))
  31. print('final formula is {}'.format(formula))
  32. model = smf.glm(
  33. formula=formula, data=data,
  34. family=sm.families.Binomial(sm.families.links.logit)
  35. ).fit()
  36. return(model)

逐步法(基于AIC)选择变量

  1. # 看看选出了哪些变量
  2. candidates = ['churn','duration','AGE','edu_class','posTrend','negTrend','nrProm','prom','curPlan','avgplan','planChange','incomeCode','feton','peakMinAv','peakMinDiff','call_10086']
  3. data_for_select = train[candidates]
  4. #调用向前法变量选择函数
  5. lg_m1 = forward_select(data=data_for_select, response='churn')
  6. lg_m1.summary()

e1e9cad326987598e8ceb6520dea6b6f.png

a02111fade45dce31082e370b0cb6579.png
这是选出的变量,并且选出的变量都是显著滴!

这是选出的变量,并且选出的变量都是显著滴!

  1. # Seemingly wrong when using 'statsmmodels.stats.outliers_influence.variance_inflation_factor'
  2. # 来定义一个解决多重共线性问题的函数
  3. def vif(df, col_i):
  4. from statsmodels.formula.api import ols
  5. cols = list(df.columns)
  6. cols.remove(col_i)
  7. cols_noti = cols
  8. formula = col_i + '~' + '+'.join(cols_noti)
  9. r2 = ols(formula, df).fit().rsquared
  10. return 1. / (1. - r2)

定义vif函数是因为我们还要解决变量之间多重共线性的问题!

  1. exog = train[candidates].drop(['churn'], axis=1)
  2. for i in exog.columns:
  3. print(i, 't', vif(df=exog, col_i=i))

9b7298ee38ad2b5e97782bdc175dec9f.png

看vif的话,一般大于10的是有共线性问题的!

posTrend,negTrend;curPlan,avgplan有明显的共线性问题,需要剔除后重新建模.

把'nrProm'和'avgplan'和posTrend 三个变量剔除!!!重新建模

  1. #把'nrProm''avgplan'和posTrend 三个变量剔除!!!重新建模
  2. candidates = ['churn','duration','AGE','edu_class','negTrend','prom','curPlan','planChange','incomeCode','feton','peakMinAv','peakMinDiff','call_10086']
  3. data_for_select = train[candidates]
  4. lg_m1 = forward_select(data=data_for_select, response='churn')
  5. lg_m1.summary()
  6. exog = train[candidates].drop(['churn'], axis=1)
  7. for i in exog.columns:
  8. print(i, 't', vif(df=exog, col_i=i))

0f6b30ba17a49a3b5ef5d1114d950b84.png

现在多重共线性问题已经解决!

以上是传统建模方法!

下面开始机器学习的方法进行建模

  1. #%%
  2. #4)使用岭回归和Laso算法重建第三步中的模型,使用交叉验证法确定惩罚参数(C值)。并比较步骤四中Laso算法得到的模型和第三步得到的模型的差异
  3. from sklearn.linear_model import LogisticRegression
  4. from sklearn.preprocessing import StandardScaler #变量需要标准化
  5. candidates = ['duration','AGE','edu_class','posTrend','negTrend','nrProm','prom','curPlan','avgplan','planChange','incomeCode','feton','peakMinAv','peakMinDiff','call_10086']
  6. #data_for_select = churn[candidates]
  7. scaler = StandardScaler() # 标准化 中心标准化
  8. X = scaler.fit_transform(churn[candidates])
  9. y = churn['churn']

888f3074b2991f2691723eee4b615dd1.png
经过标准化的X

经过标准化的X是必须的步骤!

  1. #%%下面开始建模
  2. from sklearn import linear_model
  3. from sklearn.svm import l1_min_c
  4. cs = l1_min_c(X, y, loss='log') * np.logspace(0, 4)#在对数空间进行搜索
  5. cs

0ff372cd9e4a30ac3dd95ecafeeef583.png

cs是一个搜索空间

惩罚是l1 Laso算法

  1. print("Computing regularization path ...")
  2. #start = datetime.now()
  3. #惩罚是l1 Laso算法,
  4. clf = linear_model.LogisticRegression(C=1.0, penalty='l1', tol=1e-6)
  5. coefs_ = [] #收集回归系数
  6. for c in cs:
  7. clf.set_params(C=c)
  8. clf.fit(X, y)
  9. coefs_.append(clf.coef_.ravel().copy())
  10. #print("This took ", datetime.now() - start)
  11. coefs_ = np.array(coefs_)
  12. plt.plot(np.log10(cs), coefs_)
  13. ymin, ymax = plt.ylim()
  14. plt.xlabel('log(C)')
  15. plt.ylabel('Coefficients')
  16. plt.title('Logistic Regression Path')
  17. plt.axis('tight')
  18. plt.show()

6a0bddc6603f7a7e146c5006465f1c4b.png
  1. #%%
  2. cs = l1_min_c(X, y, loss='log') * np.logspace(0, 4)
  3. import matplotlib.pyplot as plt #可视化模块
  4. from sklearn.cross_validation import cross_val_score # K折交叉验证模块
  5. k_scores = []
  6. clf = linear_model.LogisticRegression(penalty='l1')
  7. #藉由迭代的方式来计算不同参数对模型的影响,并返回交叉验证后的平均准确率
  8. for c in cs:
  9. clf.set_params(C=c)
  10. scores = cross_val_score(clf, X, y, cv=10, scoring='roc_auc') #http://scikit-learn.org/stable/modules/model_evaluation.html
  11. k_scores.append([c,scores.mean(),scores.std()])
  12. #%%
  13. #可视化数据
  14. #%%
  15. data=pd.DataFrame(k_scores)#将字典转换成为数据框
  16. fig = plt.figure()
  17. ax1 = fig.add_subplot(111)
  18. ax1.plot(np.log10(data[0]), data[1],'b')
  19. ax1.set_ylabel('Mean ROC(Blue)')
  20. ax1.set_xlabel('log10(cs)')
  21. ax2 = ax1.twinx()
  22. ax2.plot(np.log10(data[0]), data[2],'r')
  23. ax2.set_ylabel('Std ROC Index(Red)')

676f4c3896ef6ec5ff728563cf9dc29c.png

蓝色是ROC曲线的均值,是X越高越好的趋势,C越高,模型越复杂

C大概是-1.9的位置是最好的

  1. #重新实现Laso算法
  2. from sklearn.linear_model import LogisticRegression
  3. from sklearn.preprocessing import StandardScaler
  4. candidates = ['duration','AGE','edu_class','posTrend','negTrend','nrProm','prom','curPlan','avgplan','planChange','incomeCode','feton','peakMinAv','peakMinDiff','call_10086']
  5. #data_for_select = churn[candidates]
  6. scaler = StandardScaler() # 标准化
  7. X = scaler.fit_transform(churn[candidates])
  8. y = churn['churn']
  9. #%%
  10. from sklearn import linear_model
  11. clf = linear_model.LogisticRegression(C=np.exp(-1.9), penalty='l1')
  12. clf.fit(X, y)
  13. clf.coef_

c69660a173ac77163e6759c5bc4db16b.png

第一个变量是duration的参数,依次数过来,模型剔除了第9和第10个变量('avgplan','planChange')这两个变量被剔除了!

传统建模vif的方法刚刚剔除了把'nrProm'和'avgplan'和posTrend 三个变量剔除

机器学习方法和手工的传统建模vif的方法其实是差不多的!

往期精彩回顾

第一篇 | 深圳购房投资笔记(一)-楼市有泡沫吗?(上)

第二篇 |深圳购房投资笔记(二)-楼市有泡沫吗?(中)

第三篇|深圳购房笔记(三)-亲身经历货币棚改化(上)

第四篇|深圳购房投资笔记(四)-亲身经历货币化棚改(下)

第五篇|深圳购房投资笔记(五)-因为朋友,被杭州所迷惑

第六篇|深圳购房投资笔记(六)-为何放弃在杭州定居购房

第七篇|深圳购房投资笔记(七)-神奇的深圳人口?(下)

第八篇|深圳购房投资笔记(八)-为何选择在深圳定居购房?

第九篇|深圳购房投资笔记(九)-中国最大的炒房散户欧成效先生

第十篇|深圳购房投资笔记(十)-深圳楼市拐点已来临

第十一篇|深圳购房投资笔记(十一)-楼市谣言四处飞,我向人民说实话(上)

第十二篇|深圳购房投资笔记(十二)-楼市谣言四处飞,我想人民说实话(下)

第十三篇|深圳购房投资笔记(十三)-民间炒房高手财上团(上)

第十四篇|深圳购房笔记(十四)-2019年的这一切都是必然的

第十五篇|深圳购房投资笔记(十五)-2019年我们的处境

第十六篇|深圳购房投资笔记(十六)-从19年看李嘉诚为何资产

第十七篇|深圳购房投资笔记(十七)-2019年深圳楼市或将阴跌

第十八篇|深圳购房投资笔记(十八)-粤港澳湾区未来可期

第十九篇|深圳购房投资笔记(十九)-2019楼市倾向横盘利于刚需

深圳购房投资笔记(二十) 楼层的选择需慎重

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

闽ICP备14008679号