电信流失用户预测"/>
电信流失用户预测
写在前面的话
数据来源:DataFountain,后来发现DF上的数据可能是从Kaggle上搬运的。
笔者英语一般,所以原本数据集中英文列名被替换为了中文,文中变量的命名也有很多汉语拼音,阅读时请见谅。
刚刚入行Python数据分析,整体思路及部分代码多有参考,不足之处请大家多多指教。
导入需要用到的包及添加绘图设置
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import LabelEncoder,StandardScaler
import seaborn as sns
from collections import Counter
import warnings
warnings.filterwarnings('ignore')
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
%matplotlib inline
加载数据
data = pd.read_csv('C:/Users/Yans1/Desktop/WA_Fn-UseC_-Telco-Customer-Churn.csv')
data.head()
customerID | gender | SeniorCitizen | Partner | Dependents | tenure | PhoneService | MultipleLines | InternetService | OnlineSecurity | ... | DeviceProtection | TechSupport | StreamingTV | StreamingMovies | Contract | PaperlessBilling | PaymentMethod | MonthlyCharges | TotalCharges | Churn | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 7590-VHVEG | Female | 0 | Yes | No | 1 | No | No phone service | DSL | No | ... | No | No | No | No | Month-to-month | Yes | Electronic check | 29.85 | 29.85 | No |
1 | 5575-GNVDE | Male | 0 | No | No | 34 | Yes | No | DSL | Yes | ... | Yes | No | No | No | One year | No | Mailed check | 56.95 | 1889.5 | No |
2 | 3668-QPYBK | Male | 0 | No | No | 2 | Yes | No | DSL | Yes | ... | No | No | No | No | Month-to-month | Yes | Mailed check | 53.85 | 108.15 | Yes |
3 | 7795-CFOCW | Male | 0 | No | No | 45 | No | No phone service | DSL | Yes | ... | Yes | Yes | No | No | One year | No | Bank transfer (automatic) | 42.30 | 1840.75 | No |
4 | 9237-HQITU | Female | 0 | No | No | 2 | Yes | No | Fiber optic | No | ... | No | No | No | No | Month-to-month | Yes | Electronic check | 70.70 | 151.65 | Yes |
5 rows × 21 columns
数据清洗
#将列索引替换为中文,便于理解
data.rename(columns={'customerID':'用户ID','gender':'性别','SeniorCitizen':'老年人','Partner':'是否有配偶','Dependents':'是否经济独立','tenure':'入网时间','PhoneService':'电话服务','MultipleLines':'多线业务','InternetService':'互联网服务','OnlineSecurity':'网络安全','OnlineBackup':'网络备份','DeviceProtection':'设备保护','TechSupport':'技术支持','StreamingTV':'网络电视','StreamingMovies':'网络电影','Contract':'合同方式','PaperlessBilling':'是否开通电子账单','PaymentMethod':'付款方式','MonthlyCharges':'月费用','TotalCharges':'总费用','Churn':'是否流失'},inplace=True)
查看数据类型
data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 7043 entries, 0 to 7042
Data columns (total 21 columns):# Column Non-Null Count Dtype
--- ------ -------------- ----- 0 用户ID 7043 non-null object 1 性别 7043 non-null object 2 老年人 7043 non-null int64 3 是否有配偶 7043 non-null object 4 是否经济独立 7043 non-null object 5 入网时间 7043 non-null int64 6 电话服务 7043 non-null object 7 多线业务 7043 non-null object 8 互联网服务 7043 non-null object 9 网络安全 7043 non-null object 10 网络备份 7043 non-null object 11 设备保护 7043 non-null object 12 技术支持 7043 non-null object 13 网络电视 7043 non-null object 14 网络电影 7043 non-null object 15 合同方式 7043 non-null object 16 是否开通电子账单 7043 non-null object 17 付款方式 7043 non-null object 18 月费用 7043 non-null float6419 总费用 7043 non-null object 20 是否流失 7043 non-null object
dtypes: float64(1), int64(2), object(18)
memory usage: 1.1+ MB
1.根据月费用与总费用之间的关系做出推断,入网时间的单位为月,即入网时间的数字代表用户已经入网了几个月;
2.总费用需要转化为数值类型;
3.无缺失值
查看有无重复值
data.duplicated().sum()
0
没有完全相同的两行。
将总费用列转换为float类型
data['总费用'].astype(float)
#ValueError: could not convert string to float: ''
运行后报错,提示数据列中含有空格。
#找出含有空值的行
data.loc[data['总费用']==' ']
用户ID | 性别 | 老年人 | 是否有配偶 | 是否经济独立 | 入网时间 | 电话服务 | 多线业务 | 互联网服务 | 网络安全 | ... | 设备保护 | 技术支持 | 网络电视 | 网络电影 | 合同方式 | 是否开通电子账单 | 付款方式 | 月费用 | 总费用 | 是否流失 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
488 | 4472-LVYGI | Female | 0 | Yes | Yes | 0 | No | No phone service | DSL | Yes | ... | Yes | Yes | Yes | No | Two year | Yes | Bank transfer (automatic) | 52.55 | No | |
753 | 3115-CZMZD | Male | 0 | No | Yes | 0 | Yes | No | No | No internet service | ... | No internet service | No internet service | No internet service | No internet service | Two year | No | Mailed check | 20.25 | No | |
936 | 5709-LVOEQ | Female | 0 | Yes | Yes | 0 | Yes | No | DSL | Yes | ... | Yes | No | Yes | Yes | Two year | No | Mailed check | 80.85 | No | |
1082 | 4367-NUYAO | Male | 0 | Yes | Yes | 0 | Yes | Yes | No | No internet service | ... | No internet service | No internet service | No internet service | No internet service | Two year | No | Mailed check | 25.75 | No | |
1340 | 1371-DWPAZ | Female | 0 | Yes | Yes | 0 | No | No phone service | DSL | Yes | ... | Yes | Yes | Yes | No | Two year | No | Credit card (automatic) | 56.05 | No | |
3331 | 7644-OMVMY | Male | 0 | Yes | Yes | 0 | Yes | No | No | No internet service | ... | No internet service | No internet service | No internet service | No internet service | Two year | No | Mailed check | 19.85 | No | |
3826 | 3213-VVOLG | Male | 0 | Yes | Yes | 0 | Yes | Yes | No | No internet service | ... | No internet service | No internet service | No internet service | No internet service | Two year | No | Mailed check | 25.35 | No | |
4380 | 2520-SGTTA | Female | 0 | Yes | Yes | 0 | Yes | No | No | No internet service | ... | No internet service | No internet service | No internet service | No internet service | Two year | No | Mailed check | 20.00 | No | |
5218 | 2923-ARZLG | Male | 0 | Yes | Yes | 0 | Yes | No | No | No internet service | ... | No internet service | No internet service | No internet service | No internet service | One year | Yes | Mailed check | 19.70 | No | |
6670 | 4075-WKNIU | Female | 0 | Yes | Yes | 0 | Yes | Yes | DSL | No | ... | Yes | Yes | Yes | No | Two year | No | Mailed check | 73.35 | No | |
6754 | 2775-SEFEE | Male | 0 | No | Yes | 0 | Yes | Yes | DSL | Yes | ... | No | Yes | No | No | Two year | Yes | Bank transfer (automatic) | 61.90 | No |
11 rows × 21 columns
观察总费用列为空格的数据发现,这11列数据都有一个共同点,即入网时间为0。判断是因为导出数据的时候这11位用户是当月入网的新用户,只产生了月费用,没有生成总费用。同时可知,数据集中总费用不包括当月费用。所以将这部分数据填充0。
#使用replace()函数替换
data['总费用'] = data['总费用'].replace(' ',0)
data['总费用'] = data['总费用'].astype(float)
#查看此时的数据类型
data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 7043 entries, 0 to 7042
Data columns (total 21 columns):# Column Non-Null Count Dtype
--- ------ -------------- ----- 0 用户ID 7043 non-null object 1 性别 7043 non-null object 2 老年人 7043 non-null int64 3 是否有配偶 7043 non-null object 4 是否经济独立 7043 non-null object 5 入网时间 7043 non-null int64 6 电话服务 7043 non-null object 7 多线业务 7043 non-null object 8 互联网服务 7043 non-null object 9 网络安全 7043 non-null object 10 网络备份 7043 non-null object 11 设备保护 7043 non-null object 12 技术支持 7043 non-null object 13 网络电视 7043 non-null object 14 网络电影 7043 non-null object 15 合同方式 7043 non-null object 16 是否开通电子账单 7043 non-null object 17 付款方式 7043 non-null object 18 月费用 7043 non-null float6419 总费用 7043 non-null float6420 是否流失 7043 non-null object
dtypes: float64(2), int64(2), object(17)
memory usage: 1.1+ MB
将用户ID列设置为索引列
data.set_index('用户ID',inplace=True)
查看离散型变量的取值
data_object_value = {}
for column in data.select_dtypes('object').columns.tolist():list = data[column].unique().tolist()data_object_value[column] = list
data_object_value
{'性别': ['Female', 'Male'],'是否有配偶': ['Yes', 'No'],'是否经济独立': ['No', 'Yes'],'电话服务': ['No', 'Yes'],'多线业务': ['No phone service', 'No', 'Yes'],'互联网服务': ['DSL', 'Fiber optic', 'No'],'网络安全': ['No', 'Yes', 'No internet service'],'网络备份': ['Yes', 'No', 'No internet service'],'设备保护': ['No', 'Yes', 'No internet service'],'技术支持': ['No', 'Yes', 'No internet service'],'网络电视': ['No', 'Yes', 'No internet service'],'网络电影': ['No', 'Yes', 'No internet service'],'合同方式': ['Month-to-month', 'One year', 'Two year'],'是否开通电子账单': ['Yes', 'No'],'付款方式': ['Electronic check','Mailed check','Bank transfer (automatic)','Credit card (automatic)'],'是否流失': ['No', 'Yes']}
通过查看表格内容可知特征变量包括:
用户属性相关:
性别
是否老年人
是否有配偶
是否经济独立
服务内容相关:
1.入网时间
2.有无电话服务,其中有电话服务的客户又分为有无多线;
3.有无互联网服务,其中有互联网服务的又分为:有无在线安全、有无在线备份、有无设备保护、有无技术支持、有无流媒体电视、有无流媒体电影;
合同相关:
合同签订方式方式:月度、年度、两年度;
是否开通电子账单;
付款方式,分为四类;
月费用;
总费用
流失用户分析
查看整体流失情况
data_mubiao = data.groupby('是否流失')['是否流失'].count()
plt.pie(data_mubiao,labels=['未流失','流失'],explode=(0.1,0),autopct='%.2f%%',textprops={'fontsize':'20'})
plt.title('客户流失率比例',size=24)
plt.show()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gvBPyr1C-1651242909185)(output_27_0.png)]
根据用户属性查看流失情况
list_yonghushuxing = ['性别','老年人','是否有配偶','是否经济独立']
fig, axes = plt.subplots(2,2,figsize=(18,12))
for i, item in enumerate(list_yonghushuxing):plt.subplot(2,2,(i+1))ax = sns.countplot(x=item,hue='是否流失',data=data,palette="Set2")plt.xlabel(item,fontsize=16)plt.ylabel('count',fontsize=16)plt.tick_params(labelsize=12)plt.title('用户流失与'+ str(item) + '之间的关系',fontsize=18)i=i+1
plt.tight_layout()
plt.show()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BmSg3gun-1651242909186)(output_29_0.png)]
可以看出:
1.性别对流失情况的区分不明显;
2.在用户群体中老年人占比不多但是流失率高;
3.未婚用户流失率较高;
4.经济未独立的用户流失率高于经济独立的用户。
根据服务内容查看流失情况
根据入网时间查看流失情况
#创建基于入网时间的流失率
df_time_liushi= data.groupby(['入网时间','是否流失'])['性别'].count() #此处以性别列做辅助列,无特殊意义,下同
df_time_liushi = pd.DataFrame(df_time_liushi)
df_time_liushi.reset_index(level=[0,1],inplace=True)
df_time_liushi = df_time_liushi.pivot_table(index='入网时间',columns='是否流失',values='性别')
df_time_liushi['流失率'] = df_time_liushi['Yes']/(df_time_liushi['No']+df_time_liushi['Yes'])
#生成图像
plt.rcParams['figure.figsize'] = (16,8)
sns.histplot(data=data,x='入网时间',hue='是否流失',multiple='dodge',palette='Set2')
plt.xlabel('入网时间',fontsize=16)
plt.ylabel('count',fontsize=16)
plt.title('用户流失与入网时间的关系',fontsize=18)
plt.twinx() #设置双坐标轴
sns.lineplot(x='入网时间',y='流失率',label='流失率',data=df_time_liushi)
plt.legend(loc='upper center')
plt.show()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SJINWcef-1651242909187)(output_34_0.png)]
可以看出:
入网时间在一年以内的用户流失率非常高,随着入网时间的增长,流失率呈下降趋势,表明用户在使用过程中,粘性越来越高。但是对于新入网用户的留存还需要加大运营力度。
根据电话服务查看流失情况
#基于有无电话服务的流失率
df_phone_liushi= data.groupby(['电话服务','是否流失'])['性别'].count()
df_phone_liushi = pd.DataFrame(df_phone_liushi)
df_phone_liushi.reset_index(level=[0,1],inplace=True)
df_phone_liushi = df_phone_liushi.pivot_table(index='电话服务',columns='是否流失',values='性别')
df_phone_liushi['流失率'] = df_phone_liushi['Yes']/(df_phone_liushi['No']+df_phone_liushi['Yes'])
df_phone_liushi
是否流失 | No | Yes | 流失率 |
---|---|---|---|
电话服务 | |||
No | 512 | 170 | 0.249267 |
Yes | 4662 | 1699 | 0.267096 |
#基于多线业务的流失率
df_duoxian_liushi= data.groupby(['多线业务','是否流失'])['性别'].count()
df_duoxian_liushi = pd.DataFrame(df_duoxian_liushi)
df_duoxian_liushi.reset_index(level=[0,1],inplace=True)
df_duoxian_liushi = df_duoxian_liushi.pivot_table(index='多线业务',columns='是否流失',values='性别')
df_duoxian_liushi['流失率'] = df_duoxian_liushi['Yes']/(df_duoxian_liushi['No']+df_duoxian_liushi['Yes'])
df_duoxian_liushi
是否流失 | No | Yes | 流失率 |
---|---|---|---|
多线业务 | |||
No | 2541 | 849 | 0.250442 |
No phone service | 512 | 170 | 0.249267 |
Yes | 2121 | 850 | 0.286099 |
通过与整体流失率比较,可以看出基于电话服务流失情况与整体流失情况基本一致,而开通了多线业务的流失率更高。
根据互联网服务内容查看流失情况
#创建基于互联网服务的流失率
df_net_liushi = data.groupby(['互联网服务','是否流失'])['性别'].count()
df_net_liushi = pd.DataFrame(df_net_liushi)
df_net_liushi.reset_index(level=[0,1],inplace=True)
df_net_liushi = df_net_liushi.pivot_table(index='互联网服务',columns='是否流失',values='性别')
df_net_liushi['流失率'] = df_net_liushi['Yes']/(df_net_liushi['No']+df_net_liushi['Yes'])
#生成图像
plt.rcParams['figure.figsize'] = (8,8)
sns.histplot(data=data,x='互联网服务',hue='是否流失',multiple='stack',palette='Set2',shrink=0.5)
plt.xlabel('互联网服务',fontsize=16)
plt.ylabel('count',fontsize=16)
plt.title('用户流失与互联网服务之间的关系',fontsize=18)
plt.twinx()
sns.lineplot(x=['DSL', 'Fiber optic', 'No'],y='流失率',label='流失率',data=df_net_liushi)
plt.legend(loc='right')
plt.show()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u9vqDwHK-1651242909187)(output_42_0.png)]
可以看出:
开通了’Fiber optic’服务的用户流失率非常高,建议针对这部分用户优化用户体验,提升用户留存。
根据有无互联网增值服务查看流失情况
list_fuwuneirong = ['网络安全','网络备份','设备保护','技术支持','网络电视','网络电影']
fig, axes = plt.subplots(2,3,figsize=(18,12))
for i, item in enumerate(list_fuwuneirong):plt.subplot(2,3,(i+1))ax = sns.countplot(x=item,hue='是否流失',data=data,palette="Set2",order=["Yes","No","No internet service"])plt.xlabel(str(item),fontsize=16)plt.ylabel('count',fontsize=16)plt.tick_params(labelsize=12)plt.title('用户流失与'+ str(item) + '之间的关系',fontsize=18)i=i+1
plt.tight_layout()
plt.show()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3mrs5p1U-1651242909187)(output_45_0.png)]
可以看出:
开通了相关服务的用户流失率较低。没有互联网服务的用户群体流失率低,但是开通了互联网服务,没有开通某些单项服务的用户群体流失率高。推测是因为用户没有体验到完整服务,得不到完善的使用体验,建议对这部分用户进行服务拓展,提升用户留存。
根据合同相关的部分查看流失情况
根据合同方式查看流失情况
#创建基于合同方式的流失率
df_hetong_liushi= data.groupby(['合同方式','是否流失'])['性别'].count()
df_hetong_liushi = pd.DataFrame(df_hetong_liushi)
df_hetong_liushi.reset_index(level=[0,1],inplace=True)
df_hetong_liushi = df_hetong_liushi.pivot_table(index='合同方式',columns='是否流失',values='性别')
df_hetong_liushi['流失率'] = df_hetong_liushi['Yes']/(df_hetong_liushi['No']+df_hetong_liushi['Yes'])
#生成图像
plt.rcParams['figure.figsize'] = (8,8)
sns.histplot(data=data,x='合同方式',hue='是否流失',multiple='stack',palette='Set2',shrink=0.5)
plt.xlabel('合同方式',fontsize=16)
plt.ylabel('count',fontsize=16)
plt.title('用户流失与合同方式的关系',fontsize=18)
plt.twinx()
sns.lineplot(x='合同方式',y='流失率',label='流失率',data=df_hetong_liushi)
plt.legend(loc='upper center')
plt.show()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bqpspeIw-1651242909187)(output_50_0.png)]
可以看出:
合同签订的越长,用户流失率越低,一部分原因是这类用户对产品的信赖度高,相对稳定,另一份原因是长期合同对用户产生了一定的约束,避免了用户的流失。
建议签订合同时,通过优惠等促销手段引导用户签订长期合同。
根据是否开通电子账单查看流失情况
#创建基于合同方式的流失率
df_zhangdan_liushi= data.groupby(['是否开通电子账单','是否流失'])['性别'].count()
df_zhangdan_liushi = pd.DataFrame(df_zhangdan_liushi)
df_zhangdan_liushi.reset_index(level=[0,1],inplace=True)
df_zhangdan_liushi = df_zhangdan_liushi.pivot_table(index='是否开通电子账单',columns='是否流失',values='性别')
df_zhangdan_liushi['流失率'] = df_zhangdan_liushi['Yes']/(df_zhangdan_liushi['No']+df_zhangdan_liushi['Yes'])
#生成图像
plt.rcParams['figure.figsize'] = (8,8)
sns.histplot(data=data,x='是否开通电子账单',hue='是否流失',multiple='stack',palette='Set2',shrink=0.5)
plt.xlabel('是否开通电子账单',fontsize=16)
plt.ylabel('count',fontsize=16)
plt.title('用户流失与是否开通电子账单之间的关系',fontsize=18)
plt.twinx()
sns.lineplot(x='是否开通电子账单',y='流失率',label='流失率',data=df_zhangdan_liushi)
plt.legend(loc='upper center')
plt.show()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1MKbHQWs-1651242909188)(output_54_0.png)]
可以看出:
开通了电子账单的用户流失率较高,说明电子账单的用户体验并不好,建议针对这点进行优化。
根据付款方式查看流失情况
#创建基于合同方式的流失率
df_pay_liushi= data.groupby(['付款方式','是否流失'])['性别'].count()
df_pay_liushi = pd.DataFrame(df_pay_liushi)
df_pay_liushi.reset_index(level=[0,1],inplace=True)
df_pay_liushi = df_pay_liushi.pivot_table(index='付款方式',columns='是否流失',values='性别')
df_pay_liushi['流失率'] = df_pay_liushi['Yes']/(df_pay_liushi['No']+df_pay_liushi['Yes'])
#生成图像
plt.rcParams['figure.figsize'] = (8,8)
sns.histplot(data=data,x='付款方式',hue='是否流失',multiple='stack',palette='Set2',shrink=0.5)
plt.xlabel('付款方式',fontsize=16)
plt.ylabel('count',fontsize=16)
plt.title('用户流失与付款方式之间的关系',fontsize=18)
plt.twinx()
sns.lineplot(x='付款方式',y='流失率',label='流失率',data=df_pay_liushi)
plt.legend(loc='upper center')
plt.show()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O2GY3jvz-1651242909188)(output_58_0.png)]
可以看出:
使用Electronic check的用户流失率最高,其余三种付款方式的用户流失率基本一致,建议优化Electronic check的使用体验
查看不同月消费金额的流失情况
plt.rcParams['figure.figsize'] = (16,8)
sns.histplot(data=data,x='月费用',hue='是否流失',multiple='dodge',palette='Set2')
plt.xlabel('月费用',fontsize=16)
plt.ylabel('count',fontsize=16)
plt.title('用户流失与月消费金额之间的关系',fontsize=18)
plt.show()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XrfiTYbg-1651242909188)(output_61_0.png)]
可以看出:
月消费金额在70~110之间的用户流失率较高。
查看不同总消费金额的流失情况
plt.rcParams['figure.figsize'] = (16,8)
sns.histplot(data=data,x='总费用',hue='是否流失',multiple='dodge',palette='Set2')
plt.xlabel('总费用',fontsize=16)
plt.ylabel('count',fontsize=16)
plt.title('用户流失与总消费金额之间的关系',fontsize=18)
plt.show()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wJKlQq31-1651242909188)(output_64_0.png)]
可以看出:
随着总消费金额的增多,用户的流失率逐渐下降。说明随着用户对产品的使用,用户粘性越来越高。
特征工程
对离散型变量编码并生成对应关系
#对文本型内容进行编码并生成对照表、转码后的数据表
le = LabelEncoder()
data_duizhao = pd.DataFrame(columns=[0,1,2,3])
data_trans = pd.DataFrame()
list_object_list = data.select_dtypes('object').columns.tolist()
for i,column in enumerate(list_object_list):label_i = le.fit_transform(data[column])data_trans[column] = label_ila_i = le.classes_list_data = []list_data.append(la_i.tolist())data_duizhao = data_duizhao.append(pd.DataFrame(list_data))
data_duizhao['变量'] = list_object_list
data_duizhao.set_index('变量',inplace=True)
data_duizhao
0 | 1 | 2 | 3 | |
---|---|---|---|---|
变量 | ||||
性别 | Female | Male | NaN | NaN |
是否有配偶 | No | Yes | NaN | NaN |
是否经济独立 | No | Yes | NaN | NaN |
电话服务 | No | Yes | NaN | NaN |
多线业务 | No | No phone service | Yes | NaN |
互联网服务 | DSL | Fiber optic | No | NaN |
网络安全 | No | No internet service | Yes | NaN |
网络备份 | No | No internet service | Yes | NaN |
设备保护 | No | No internet service | Yes | NaN |
技术支持 | No | No internet service | Yes | NaN |
网络电视 | No | No internet service | Yes | NaN |
网络电影 | No | No internet service | Yes | NaN |
合同方式 | Month-to-month | One year | Two year | NaN |
是否开通电子账单 | No | Yes | NaN | NaN |
付款方式 | Bank transfer (automatic) | Credit card (automatic) | Electronic check | Mailed check |
是否流失 | No | Yes | NaN | NaN |
对连续特征进行标准化处理
#对连续变量做归一化并与文本型变量处理后的表格合并
data_trans[['入网时间','月费用','总费用']] = StandardScaler().fit_transform(data[['入网时间','月费用','总费用']])
data_trans['老年人'] = data.loc[:,['老年人']].values
data_trans.head()
性别 | 是否有配偶 | 是否经济独立 | 电话服务 | 多线业务 | 互联网服务 | 网络安全 | 网络备份 | 设备保护 | 技术支持 | 网络电视 | 网络电影 | 合同方式 | 是否开通电子账单 | 付款方式 | 是否流失 | 入网时间 | 月费用 | 总费用 | 老年人 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 2 | 0 | 0 | 0 | 0 | 0 | 1 | 2 | 0 | -1.277445 | -1.160323 | -0.992611 | 0 |
1 | 1 | 0 | 0 | 1 | 0 | 0 | 2 | 0 | 2 | 0 | 0 | 0 | 1 | 0 | 3 | 0 | 0.066327 | -0.259629 | -0.172165 | 0 |
2 | 1 | 0 | 0 | 1 | 0 | 0 | 2 | 2 | 0 | 0 | 0 | 0 | 0 | 1 | 3 | 1 | -1.236724 | -0.362660 | -0.958066 | 0 |
3 | 1 | 0 | 0 | 0 | 1 | 0 | 2 | 0 | 2 | 2 | 0 | 0 | 1 | 0 | 0 | 0 | 0.514251 | -0.746535 | -0.193672 | 0 |
4 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 2 | 1 | -1.236724 | 0.197365 | -0.938874 | 0 |
data_trans即为处理好的数据集。
数据相关性分析
data_corr = data_trans.corr()
df_corr = pd.DataFrame(data_corr['是否流失'].sort_values(ascending = False)).T
plt.figure(figsize=(16,8))
sns.barplot(data=df_corr,palette='Set2')
plt.tick_params(labelsize=14)
plt.xticks(rotation=45)
plt.title('数据相关性分析',fontsize=20)
plt.show()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jTAa6EWg-1651242909189)(output_73_0.png)]
可以看出:
电话服务与性别这两个变量与是否流失的相关性最弱,这也符合前面针对单个变量查看时的情况。因此在特征变量选取时,可以删除这两个变量
提取特征变量和目标变量
#从处理好的数据集中删除电话服务和性别两列
data_trans.drop(['电话服务','性别'],axis=1,inplace=True)
#提取特征变量与目标变量
X = data_trans.drop(columns='是否流失').copy()
y = data_trans['是否流失']
#查看特征变量的前五行
X.head()
是否有配偶 | 是否经济独立 | 多线业务 | 互联网服务 | 网络安全 | 网络备份 | 设备保护 | 技术支持 | 网络电视 | 网络电影 | 合同方式 | 是否开通电子账单 | 付款方式 | 入网时间 | 月费用 | 总费用 | 老年人 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 1 | 0 | 0 | 2 | 0 | 0 | 0 | 0 | 0 | 1 | 2 | -1.277445 | -1.160323 | -0.992611 | 0 |
1 | 0 | 0 | 0 | 0 | 2 | 0 | 2 | 0 | 0 | 0 | 1 | 0 | 3 | 0.066327 | -0.259629 | -0.172165 | 0 |
2 | 0 | 0 | 0 | 0 | 2 | 2 | 0 | 0 | 0 | 0 | 0 | 1 | 3 | -1.236724 | -0.362660 | -0.958066 | 0 |
3 | 0 | 0 | 1 | 0 | 2 | 0 | 2 | 2 | 0 | 0 | 1 | 0 | 0 | 0.514251 | -0.746535 | -0.193672 | 0 |
4 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 2 | -1.236724 | 0.197365 | -0.938874 | 0 |
划分训练集与测试集
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=123)
X_train.shape
(4930, 17)
X_test.shape
(2113, 17)
模型搭建与评估
from sklearn.ensemble import RandomForestClassifier # 随机森林
from sklearn.svm import SVC, LinearSVC # 支持向量机
from sklearn.linear_model import LogisticRegression # 逻辑回归
from sklearn.neighbors import KNeighborsClassifier # KNN算法
from sklearn.naive_bayes import GaussianNB # 朴素贝叶斯
from sklearn.tree import DecisionTreeClassifier # 决策树模型
from xgboost import XGBClassifier # XGBoost算法
from sklearn.ensemble import AdaBoostClassifier # AdaBoost算法
from sklearn.ensemble import GradientBoostingClassifier # GBDT算法
from lightgbm import LGBMClassifier # LightGBM算法from sklearn.metrics import precision_score, recall_score, f1_score
from sklearn.metrics import confusion_matrix
from sklearn.model_selection import GridSearchCV # 网格搜索
Classifiers=[['随机森林',RandomForestClassifier()],['支持向量机',SVC()],['逻辑回归',LogisticRegression()],['KNN算法',KNeighborsClassifier(n_neighbors=5)],['朴素贝叶斯',GaussianNB()],['决策树模型',DecisionTreeClassifier()],["XGB", XGBClassifier()],['AdaBoost算法', AdaBoostClassifier()],['GBDT算法', GradientBoostingClassifier()],['LightGBM算法',LGBMClassifier()]
]
Classify_result=[]
names=[]
prediction=[]
for name,classifier in Classifiers:classifier=classifierclassifier.fit(X_train,y_train)y_pred=classifier.predict(X_test)recall=recall_score(y_test,y_pred)precision=precision_score(y_test,y_pred)f1score = f1_score(y_test, y_pred)class_eva=pd.DataFrame([recall,precision,f1score])Classify_result.append(class_eva)name=pd.Series(name)names.append(name)y_pred=pd.Series(y_pred)prediction.append(y_pred)
classifier_names=pd.DataFrame(names)
classifier_names=classifier_names[0].tolist()
result=pd.concat(Classify_result,axis=1)
result.columns=classifier_names
result.index=["recall","precision","f1score"]
result
随机森林 | 支持向量机 | 逻辑回归 | KNN算法 | 朴素贝叶斯 | 决策树模型 | XGB | AdaBoost算法 | GBDT算法 | LightGBM算法 | |
---|---|---|---|---|---|---|---|---|---|---|
recall | 0.459322 | 0.425424 | 0.520339 | 0.452542 | 0.727119 | 0.493220 | 0.493220 | 0.506780 | 0.501695 | 0.501695 |
precision | 0.665848 | 0.701117 | 0.683742 | 0.589404 | 0.551414 | 0.533945 | 0.627155 | 0.670404 | 0.682028 | 0.653422 |
f1score | 0.543631 | 0.529536 | 0.590953 | 0.511985 | 0.627193 | 0.512775 | 0.552182 | 0.577220 | 0.578125 | 0.567593 |
可以看出:
以f1_score为评估值,在都选取默认参数的情况下,朴素贝叶斯、逻辑回归、GBDT三个模型的效果较好。
结论与建议
流失用户特征
1.老年用户、未婚用户、经济未独立用户的更容易流失;
2.新用户、未完整开通互联网服务内容的用户流失率高;
3.签订短周期合同、使用Electronic check的用户流失率最高、月消费金额在70~110之间的三类用户,更容易流失。
建议
1.通过调查问卷,了解易流失用户的痛点,完善相关服务;
2.对于新用户,通过促销等优惠手段,引导用户体验完整的互联网服务;一方面增强用户对产品的认知度,另一方面促进用户对产品产生依赖;
3.对于签订月度合同的用户,通过增值服务等长期合约,或者通过优惠引导用户签订长期合同提高用户留存。
4.优化Electronic check的使用体验,深挖用户使用场景中的痛点并改进,或者通过优惠券引导用户使用其它支付方式。
更多推荐
电信流失用户预测
发布评论