电信用户流失分析与预测
这是作者自己做的一个数据分析项目,通篇采用大量python源码编写,欢迎一起学习交流,提升自我。
作者的博客园地址: https://www.cnblogs.com/lverkou/p/13441302.html
转载请标明出处,谢谢!
数据集下载地址: https://www.datafountain.cn/datasets/35guide
一. 研究背景
用户流失预测在机器学习中算是一种比较典型的分类场景,做好用户的流失预测可以降低营销成本,留住用户并且获得更好的用户体验,在三大巨头的瓜分下,做好营销运营比重新获取一个新用户更节省成本。达到较好的运营回报。如果在传统分类模式下,通常是通过人工对各个特征进行统计,然后分到合适的类别中,这样不但会耗费大量的资源,且低效。
二. 分析结论与建议
- 增加套餐福利,解锁更多权益,如赠送流量,免费看视频,增设小游戏,贵族制度,
- 加强电话服务质量,设立评分反馈系统,及时跟踪异常评分
- 加强光纤相关设施的建设,增强网络稳定性
- 鼓励用户开通各种服务,如在线安全,在线备份,设备保护,技术支持
- 增加充值返现,充值满减,发放优惠券的方式,用户消费达到一定金额解锁特权
- 针对老年人建议赠送通话时长,提高活跃度
- 针对排名前十的职业根据相应的职业给予相应的优惠和福利,提高用户的粘性。
三. 任务与实现
我们的任务在于:
1.分析出流失用户有哪些显著性特征?
2.找出哪些用户容易流失?
具体实现内容包括:
能够对数据进行数据预处理 包括缺失值,异常值,重复值
能够描述性分析各个特征与流失用户的占比是否显著
能够将连续型变量进行分箱离散化
能够将离散型特征进行独热编码
能够建立基模型,将源数据进行标准化
能够处理样本不均衡
能够熟练运用多种分类模型对电信用户进行预测
分析模型有:逻辑回归,KNN,朴素贝叶斯,决策树,多层感知器。
四. 数据集解析
每行代表一个客户,每列包含元数据列中描述的客户属性。
一共7043行数据,21个列。前20个为特征列,最后一个为研究对象。
1 customerID Integer :用户ID
2 gender String:性别(Female or Male)
3 SeniorCitizen Integer: 老年人(1表示是,0表示不是)
4 Partner String:配偶(Yes or No)
5 Dependents String:家属(Yes or No)
6 tenure Integer :职位(0~72,共73个职位)
7 PhoneService String:电话服务(Yes or No)
8 MultipleLines String:多线(Yes 、No or No phoneservice 三种)
9 InternetService String:互联网服务(No, DSL数字网络,fiber optic光纤网络 三种)
10 OnlineSecurity String:在线安全(Yes,No,No internetserive 三种)
11 OnlineBackup String:在线备份(Yes,No,No internetserive 三种)
12 DeviceProtection String:设备保护(Yes,No,No internetserive 三种)
13 TechSupport String:技术支持(Yes,No,No internetserive 三种)
14 StreamingTV String:网络电视(Yes,No,No internetserive 三种)
15 StreamingMovies:网络电影 (Yes,No,No internetserive 三种)
16 Contract String:合同(Month-to-month,One year,Two year 三种)
17 PaperlessBilling String:账单(Yes or No)
18 PaymentMethod String:付款方式(bank transfer,credit card,electronic check,mailed check 四种)
19 MonthlyCharges Integer :月费用
20 TotalCharges Integer :总费用
21 Churn String:流失(Yes or No)
五. 数据分析套餐
1.准备工作
本文数据源来自网上,数据源除了各大网站可以下载,还可以来自自家公司的数据库,爬虫等方式获取。
导入相关的库
# 导入库
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
from matplotlib import font_manager
import time
sns.set(style="darkgrid", font_scale=1.2)
# plt.rcParams["font.family"] = "SimHei"
plt.rcParams['font.family'] = ['Arial Unicode MS']
plt.rcParams["axes.unicode_minus"] = False
my_font=font_manager.FontProperties(fname=
'/System/Library/Fonts/PingFang.ttc',
size=15)
warnings.filterwarnings("ignore")
from scipy import stats #用于方差分析
# from sklearn.linear_model import LinearRegression #线性回归模型
from sklearn.model_selection import train_test_split #切分训练集 测试集
from sklearn.linear_model import LogisticRegression #逻辑回归模型
from sklearn.metrics import classification_report #混淆矩阵打分
from sklearn.model_selection import GridSearchCV #网格交叉验证
from imblearn.over_sampling import SMOTE,ADASYN # 引入SMOTE和ADASYN处理样本不均衡
from collections import Counter #查看每个类别出现的次数
from sklearn.pipeline import Pipeline #引入流水线
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score # 评估指标 --正确率 精准率 召回率 F1调和平均值
from sklearn.neighbors import KNeighborsClassifier #KNN分类模型
from sklearn.preprocessing import StandardScaler, MinMaxScaler # StandardScaler:均值标准差标准化 # MinMaxScaler:最小最大值标准化
from sklearn.neural_network import MLPClassifier #多层感知器
导入数据集
data = pd.read_csv("WA_Fn-UseC_-Telco-Customer-Churn.csv")
print(data.shape)
data.sample(10)
2.数据预处理
查看数据整体情况
data.info()
类型转换
我们发现TotalCharges本应该是float64类型,这里却是object类型,那么需要转换。
data['TotalCharges'].astype(np.float64)
会报错 无法转换。这里用到一个函数:DataFrame.convert_objects( convert_dates = True,convert_numeric = False,convert_timedeltas = True,copy = True )
data["TotalCharges"]=data["TotalCharges"].convert_objects(convert_numeric=True)
data['TotalCharges'].dtype
输出:dtype(‘float64’) ,转换完成。
缺失值处理
data.isnull().sum(axis=0)
TotalCharges有11个缺失值,查看数据分布 再确定是删除还是填充
print(data["TotalCharges"].skew())
sns.distplot(data["TotalCharges"].dropna())
结果大于0, 属于右偏 ,当然 ,也能一眼从图形看出。
缺失值的处理方式有:删除,中位数填充,均值填充,众数填充。
右偏数据我们用中位数填充,两种方式计算中位数,计算中位数时会剔除缺失值
data["TotalCharges"].median()
或者
np.median(data["TotalCharges"].dropna().values)
输出为:1397.475
填充缺失值
data.fillna({
"TotalCharges":data["TotalCharges"].median()},inplace=True)
data.isnull().sum(axis=0)
重复值处理
如果有重复值:可直接删除
# data.drop_duplicates(inplace=True)
data.duplicated().sum()
输出:0
说明没有重复值
3.查看流失情况
整体流失情况 条形图查看
Churn_value=data["Churn"].value_counts()
display(Churn_value)
sns.countplot(x="Churn",data=data)
也可饼状图查看
size=Churn_value.values
label_list=Churn_value.index
color=["#009999","#FF7400"]
explode=[0,0.1]
plt.figure(figsize=(8,8),dpi=80)
patches,l_text,p_text=plt.pie(size,
explode=explode,
colors=color,
labels=label_list,
labeldistance=1.1,
autopct='%1.1f%%',
shadow=True,
startangle=90,
pctdistance=0.6,
)
plt.show()
饼状图可以内部直接算出百分比,从图中可以看出流失比例26.5%,占比较高,也处于样本不均衡问题,后面可以采用过采样来解决,过采样相比欠采样较稳定。
对于研究对象:Churn,我们用pandas中的map函数实现数字化处理,我们通常将关注的类别设为1。
data["Churn"]=data["Churn"].map({
"Yes":1,"No":0})
data.head()
可以看出已经成功的将Churn做了离散化处理。
4.类别特征的描述性分析
特征列主要分两大阵容,类别变量和连续型变量,我们有两种方式把他们分离出来,第一种是直接一个一个drop掉,第二种方法是采用pandas里面提供的判断类别类型和数值型的方法,这里我们采用第二种方式。
data_columns=[]
for col in data.columns.drop(["customerID","Churn"]):
# is_object_dtype:查看特征是否为类别类型,是的话往下执行;
# is_numeric_dtype:查看类别是否是数值类型的,是的话就继续往下
if pd.api.types.is_object_dtype