概述
案例 电信客户流失预测
-
案例简介
-
AT&T数据,用户个人,通话,上网等信息数据
-
充分利用数据预测客户的流失情况
-
帮助挽留用户,保证用户基数和活跃程度
-
-
数据说明
-
CustomerID 客户ID
-
Gender 性别
-
partneratt 配偶是否也为att用户
-
dependents_att 家人是否也是att用户
-
landline 是否使用att固话服务
-
internet_att/internet_other 是否使用att的互联网服务
-
Paymentbank/creditcard/electroinc 付款方式
-
MonthlyCharges 每月话费
-
TotalCharges 累计话费
-
Contract_month/1year 用户使用月度/年度合约
-
StreamingTv/streamingMovies 是否使用在线视频或者电影app
-
Churn 客户转化的flag
-
-
处理流程
-
分析流程:数据概况分析->单变量分析->相关性分析与可视化->回归模型
-
数据概况分析
-
数据行/列数量
-
缺失值分布
-
-
单变量分析
-
数字型变量的描述指标(平均值,最大最小值,标准差)
-
类别型变量(多少个分类,各自占比)
-
正负样本占比
-
-
相关性分析与可视化
-
按类别交叉对比
-
变量之间的相关性分析
-
散点图/热力图
-
-
逻辑回归分析
-
-
》 模型建立
》模型评估与优化
代码
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
churn=pd.read_csv('churn.csv')
churn.info()
#显示结果
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 7043 entries, 0 to 7042
Data columns (total 16 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Churn 7043 non-null object
1 gender 7043 non-null object
2 Partner_att 7043 non-null int64
3 Dependents_att 7043 non-null int64
4 landline 7043 non-null int64
5 internet_att 7043 non-null int64
6 internet_other 7043 non-null int64
7 StreamingTV 7043 non-null int64
8 StreamingMovies 7043 non-null int64
9 Contract_Month 7043 non-null int64
10 Contract_1YR 7043 non-null int64
11 PaymentBank 7043 non-null int64
12 PaymentCreditcard 7043 non-null int64
13 PaymentElectronic 7043 non-null int64
14 MonthlyCharges 7043 non-null float64
15 TotalCharges 7043 non-null float64
dtypes: float64(2), int64(12), object(2)
memory usage: 880.5+ KB
#预测目标是churn,是类别型变量 gender也是类别型变量 需要对类别型变量进行处理
churn.head()
#显示结果
Churn | gender | Partner_att | Dependents_att | landline | internet_att | internet_other | StreamingTV | StreamingMovies | Contract_Month | Contract_1YR | PaymentBank | PaymentCreditcard | PaymentElectronic | MonthlyCharges | TotalCharges | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | No | Female | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 29.85 | 29.85 |
1 | No | Male | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 56.95 | 1889.50 |
2 | Yes | Male | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 53.85 | 108.15 |
3 | No | Male | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 42.30 | 1840.75 |
4 | Yes | Female | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 70.70 | 151.65 |
churn.describe()
#显示结果
Partner_att | Dependents_att | landline | internet_att | internet_other | StreamingTV | StreamingMovies | Contract_Month | Contract_1YR | PaymentBank | PaymentCreditcard | PaymentElectronic | MonthlyCharges | TotalCharges | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
count | 7043.000000 | 7043.000000 | 7043.000000 | 7043.000000 | 7043.000000 | 7043.000000 | 7043.000000 | 7043.000000 | 7043.000000 | 7043.000000 | 7043.000000 | 7043.000000 | 7043.000000 | 7043.000000 |
mean | 0.483033 | 0.299588 | 0.903166 | 0.343746 | 0.439585 | 0.384353 | 0.387903 | 0.550192 | 0.209144 | 0.219225 | 0.216101 | 0.335794 | 64.761692 | 2275.929881 |
std | 0.499748 | 0.458110 | 0.295752 | 0.474991 | 0.496372 | 0.486477 | 0.487307 | 0.497510 | 0.406726 | 0.413751 | 0.411613 | 0.472301 | 30.090047 | 2266.920469 |
min | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 18.250000 | 18.800000 |
25% | 0.000000 | 0.000000 | 1.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 35.500000 | 392.575000 |
50% | 0.000000 | 0.000000 | 1.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 70.350000 | 1389.850000 |
75% | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | 89.850000 | 3778.525000 |
max | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 118.750000 | 8684.800000 |
#需要把churn和gender转变为数字型变量,使用get_dummies
churn=pd.get_dummies(churn)
Partner_att | Dependents_att | landline | internet_att | internet_other | StreamingTV | StreamingMovies | Contract_Month | Contract_1YR | PaymentBank | PaymentCreditcard | PaymentElectronic | MonthlyCharges | TotalCharges | Churn_No | Churn_Yes | gender_Female | gender_Male | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 29.85 | 29.85 | 1 | 0 | 1 | 0 |
1 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 56.95 | 1889.50 | 1 | 0 | 0 | 1 |
2 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 53.85 | 108.15 | 0 | 1 | 0 | 1 |
3 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 42.30 | 1840.75 | 1 | 0 | 0 | 1 |
4 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 70.70 | 151.65 | 0 | 1 | 1 | 0 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
7038 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 84.80 | 1990.50 | 1 | 0 | 0 | 1 |
7039 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 103.20 | 7362.90 | 1 | 0 | 1 | 0 |
7040 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 29.60 | 346.45 | 1 | 0 | 1 | 0 |
7041 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 74.40 | 306.60 | 0 | 1 | 0 | 1 |
7042 | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 0 | 1 | 0 | 0 | 105.65 | 6844.50 | 1 | 0 | 0 | 1 |
7043 rows × 18 columns
#数据整理,将churn_yes保留,将female保留,drop不需要的数据
churn.drop(['Churn_No','gender_Male'],axis=1,inplace=True)
#churn_No。churn_Yes留一个即可,任何一个都可表达数值,本案例讲的是从训练数据中得出是否流失,churn_Yes中的1代表流失,0代表没有。
#留下gender_female,此时的1为女,0为难。需要在下面修改变量命名
churn
#显示结果
Partner_att | Dependents_att | landline | internet_att | internet_other | StreamingTV | StreamingMovies | Contract_Month | Contract_1YR | PaymentBank | PaymentCreditcard | PaymentElectronic | MonthlyCharges | TotalCharges | Churn_Yes | gender_Female | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 29.85 | 29.85 | 0 | 1 |
1 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 56.95 | 1889.50 | 0 | 0 |
2 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 53.85 | 108.15 | 1 | 0 |
3 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 42.30 | 1840.75 | 0 | 0 |
4 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 70.70 | 151.65 | 1 | 1 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
7038 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 84.80 | 1990.50 | 0 | 0 |
7039 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 103.20 | 7362.90 | 0 | 1 |
7040 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 29.60 | 346.45 | 0 | 1 |
7041 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 74.40 | 306.60 | 1 | 0 |
7042 | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 0 | 1 | 0 | 0 | 105.65 | 6844.50 | 0 | 0 |
7043 rows × 16 columns
#变量大小写不规则,统一变成小写
churn.columns=churn.columns.str.lower() #把所有的列名改为小写
churn.columns
#显示结果
Index(['partner_att', 'dependents_att', 'landline', 'internet_att',
'internet_other', 'streamingtv', 'streamingmovies', 'contract_month',
'contract_1yr', 'paymentbank', 'paymentcreditcard', 'paymentelectronic',
'monthlycharges', 'totalcharges', 'churn_yes', 'gender_female'],
dtype='object')
#将churn_yes重命名,方便后续的变量编写,修改列名,传入columns={"要被修改的列名";‘修改后的列名’}
churn=churn.rename(columns={'churn_yes':'flag'})
churn=churn.rename(columns={'gender_female':'gender'})
churn
#显示结果;此时列名已经改变。
partner_att | dependents_att | landline | internet_att | internet_other | streamingtv | streamingmovies | contract_month | contract_1yr | paymentbank | paymentcreditcard | paymentelectronic | monthlycharges | totalcharges | flag | gender | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 29.85 | 29.85 | 0 | 1 |
1 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 56.95 | 1889.50 | 0 | 0 |
2 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 53.85 | 108.15 | 1 | 0 |
3 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 42.30 | 1840.75 | 0 | 0 |
4 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 70.70 | 151.65 | 1 | 1 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
7038 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 84.80 | 1990.50 | 0 | 0 |
7039 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 103.20 | 7362.90 | 0 | 1 |
7040 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 29.60 | 346.45 | 0 | 1 |
7041 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 74.40 | 306.60 | 1 | 0 |
7042 | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 0 | 1 | 0 | 0 | 105.65 | 6844.50 | 0 | 0 |
7043 rows × 16 columns
#二分类模型,查看样本分布情况,分析flag 1和0的占比
churn.flag.value_counts() #流失共有1869个,未流失5174个
#显示结果
0 5174
1 1869
Name: flag, dtype: int64
#大多数是未流失用户,流失用户占26.5%
churn.flag.value_counts(normalize=True)
#显示结果
0 0.73463
1 0.26537
Name: flag, dtype: float64
#churn.flag.value_counts(1) #value_counts(1) 查看比例
#等同churn.flag.value_counts(normalize=True)
#value_counts() 把每個值分別有多少展示一下.在 python 当中 除了 0 其他數字都是 true。所以在这里,括号里可以用任何数字表示。
summary=churn.groupby('flag')
summary.mean() #按流失率求各个部门占比,这几个数据流失比较明显 ~~~ internet_other contract_month paymentelectronic
#显示结果
partner_att | dependents_att | landline | internet_att | internet_other | streamingtv | streamingmovies | contract_month | contract_1yr | paymentbank | paymentcreditcard | paymentelectronic | monthlycharges | totalcharges | gender | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
flag | |||||||||||||||
0 | 0.528218 | 0.344801 | 0.901044 | 0.379204 | 0.347700 | 0.365868 | 0.369927 | 0.429068 | 0.252609 | 0.248550 | 0.249324 | 0.250097 | 61.265124 | 2545.918081 | 0.492656 |
1 | 0.357945 | 0.174425 | 0.909042 | 0.245586 | 0.693954 | 0.435527 | 0.437667 | 0.885500 | 0.088818 | 0.138042 | 0.124131 | 0.573034 | 74.441332 | 1528.514714 | 0.502408 |
结论:观察flag在0和1的情况下,所有自变量的差别 internet_other变量,在0的分组中,均值是0.35,在1的分组中,均值是0.69。数据显示如果使用别的公司的互联网,用户流失的概率就越高(拿0.35跟0.69对比,流失的数据占了没流失的近2倍。)
sns.countplot(y='contract_month',hue='flag',data=churn) #hue目标值是什么target
<matplotlib.axes._subplots.AxesSubplot at 0x232eef53fd0>
结论:contract_month为1的客户流失的概率更高,即与非按月付费客户相比,按月付费客户流失比例高
#围绕flag变量,分析其他变量与flag的相关关系;ascending=False降序
churn.corr()[['flag']].sort_values('flag',ascending=False)
#显示结果
flag | |
---|---|
flag | 1.000000 |
contract_month | 0.405103 |
internet_other | 0.308020 |
paymentelectronic | 0.301919 |
monthlycharges | 0.193356 |
streamingtv | 0.063228 |
streamingmovies | 0.061382 |
landline | 0.011942 |
gender | 0.008612 |
paymentbank | -0.117937 |
internet_att | -0.124214 |
paymentcreditcard | -0.134302 |
partner_att | -0.150448 |
dependents_att | -0.164221 |
contract_1yr | -0.177820 |
totalcharges | -0.198175 |
结论:contract_month,internet_other,paymentelectronic与flag相关性高。
逻辑回归模型,
选择不同的数据进行建模,以下是对模型测试,并进行不断优化。
第一次,随机选取可用x数据
#设定因变量与自变量, y是 flag, x根据刚才的相关分析挑选contract_month,internet_other与streamingtv
#自变量可以分为几类,partner/dependents,internet,streaming,contract,payment,charges,后续可以自己挑选进行建模
#确定目标值和特征值
y=churn['flag']
x=churn[['contract_month','internet_other','streamingtv']]
#调用sklearn模块,随机抽取训练集与测试集
from sklearn.model_selection import train_test_split
x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.3,random_state=100)
#使用LR预测
from sklearn import linear_model
lr=linear_model.LogisticRegression()
lr.fit(x_train,y_train)
#LogisticRegression()
lr.intercept_
#array([-3.25029156])
lr.coef_
#array([[2.2229613 , 1.15089043, 0.24559832]])
模型的评估
y_pred_train=lr.predict(x_train)
y_pred_test=lr.predict(x_test)
import sklearn.metrics as metrics
metrics.accuracy_score(y_train,y_pred_train)
#0.7697768762677485
metrics.accuracy_score(y_test,y_pred_test)
#0.7453857075248462
from sklearn.metrics import roc_curve,auc
fpr,tpr,threshold=roc_curve(y_train,y_pred_train)
roc_auc=auc(fpr,tpr)
roc_auc
#0.7247770357096229
第二次,将是否使用在线视频 改为电子支付 测试对数据的影响
#设定因变量与自变量, y是 flag, x经过模型优化改变为contract_month,internet_other,将streamingtv调整为paymentelectronic
y=churn['flag']
x=churn[['contract_month','internet_other','paymentelectronic']]
#调用sklearn模块,随机抽取训练集与测试集
from sklearn.model_selection import train_test_split
x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.3,random_state=100)
#使用LR预测
from sklearn import linear_model
lr=linear_model.LogisticRegression()
lr.fit(x_train,y_train)
#LogisticRegression()
lr.intercept_
#array([-3.21842661])
lr.coef_
#array([[2.01264249, 1.05076404, 0.64248053]])
#模型的评估
y_pred_train=lr.predict(x_train)
y_pred_test=lr.predict(x_test)
import sklearn.metrics as metrics
metrics.accuracy_score(y_train,y_pred_train)
#0.7776876267748478
metrics.accuracy_score(y_test,y_pred_test)
#0.7624230951254141
from sklearn.metrics import roc_curve,auc
fpr,tpr,threshold=roc_curve(y_train,y_pred_train)
roc_auc=auc(fpr,tpr)
roc_auc
#0.6669635362478181
第三次,改变测试集和训练集的分布
#模型优化,测试集与训练集对半分.
y=churn['flag']
x=churn[['contract_month','internet_other','paymentelectronic']]
#调用sklearn模块,随机抽取训练集与测试集
from sklearn.model_selection import train_test_split
x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.5,random_state=100)
#使用LR预测
from sklearn import linear_model
lr=linear_model.LogisticRegression()
lr.fit(x_train,y_train)
#LogisticRegression()
lr.intercept_
#array([-3.22833942])
lr.coef_
#array([[2.03292538, 1.04095427, 0.62567014]])
#模型的评估
y_pred_train=lr.predict(x_train)
y_pred_test=lr.predict(x_test)
import sklearn.metrics as metrics
metrics.accuracy_score(y_train,y_pred_train)
#0.7779040045441636
metrics.accuracy_score(y_test,y_pred_test)
#0.768313458262351
from sklearn.metrics import roc_curve,auc
fpr,tpr,threshold=roc_curve(y_train,y_pred_train)
roc_auc=auc(fpr,tpr)
roc_auc
#0.666251219518281
结论:auc 的选择如果为0.5-1是推荐的。 风控0.6, 0.7, 0.8,很少有超过0.85的情况.(零点六几基本就可以用了,所以这里的数据为有效数据)
AUC的物理意义为任取一对例和负例,正例得分大于负例得分的概率,AUC越大,表明方法效果越好。
电信客户流失预测:系数1*特征1+系数2*特征2+系数3*特征3.。。。=流失概率
1,要针对沉默会员做会员重新激活,应该挑选具有什么特征的会员?
按月付费的流失率比较高,想办法让用户办按年付费的套餐,可以给按月付费的老用户发广告,老用户回馈,充值返现。满减优惠。
2,商品A库存积压严重,现在要通过促销活动清仓,选择哪些类型的促销活动更容易实现该目标?
根据类别找到更感兴趣的用户。不断换用户特征,测试出一批用户后,找出更可能买的用户特征。哪些用户更可能买。
3,网站需要大流量广告位来满足VIP商家的精准广告投放,具有哪些特征的广告位更符合VIP商家的客户需求?
测试出的这一批用户,可以反过来看下用户所拥有的特征。
最后
以上就是幸福眼神为你收集整理的电信客户流失预测的全部内容,希望文章能够帮你解决电信客户流失预测所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复