赞
踩
关于用户留存有这样一个观点:如果将用户流失率降低5%,公司利润将提升25%-85%。
而流失分析的目的就是找出用户流失的主要原因,通过这些因素采取措施,从而达到降低流失率的目的。
导入pandas库查看数据集的基本概况
import pandas as pd
import numpy as np
data = pd.read_csv(r"电信用户流失数据.csv",encoding='utf-8') #读取csv数据,设置编码方式
data.head() #查看前五行数据
离散型和连续型是概率论和数理统计中常用的两种数据类型。 它们的区别在于数据的取值范围不同。 离散型数据是指只能取有限个或可数个数值的数据,例如掷骰子的点数、班级学生的人数等。 这些数据的取值通常是整数,且相邻两个数之间有间隔。 连续型数据则是指可以取任意实数值的数据,例如人的身高、温度等。 这些数据的取值范围是连续的,相邻两个数之间没有间隔。 在统计分析中,离散型数据通常使用频数和频率来描述,而连续型数据则使用概率密度函数和累积分布函数来描述。
从中可以看出该数据集总共有21个字段,其中"客户是否为老年人",“客户留存时间”,“每月消费”,“总消费”这四个字段为数值型的数据,但“客户是否为老年人”只有0和1两种值,也就是说该字段和大部分其他的字符型的字段一样,属于离散型的数据。而其他数值型的数据属于连续型的数据。
首先查看一下数据集有没有缺失值:
data.isnull().sum() #统计每个特征的缺失值个数
可以从输出中看出“总消费”字段存在11个缺失值,由于缺失值的个数很小,所以这里采取将缺失值所在的行删除的措施。
data = data.dropna() #删除缺失值所在行
data.isnull().sum() #统计每个特征的缺失值个数
再次查看输出,发现已经没有缺失值了。
缺失值处理完之后查看数据的基本信息,看看有没有字段类型不正确,如果类型有问题,需要转换数据类型。
data.info() #查看数据信息
发现客户是否为老年人应该是分类特征
将该字段转换为字符型
data['客户是否为老年人'] = data['客户是否为老年人'].astype(str) #转为字符型
首先用户id是用户的唯一标识,所以不能用作数据的特征,另外用户是否流失是预测的目标值,所以也不是数据的特征。
基于这两点我们可以把其他的字段当作数据的特征。
print('列名:\n', data.columns.values) #列名
print(' '*20)
print('特征个数:', len(data.columns)-1) #特征个数
在投入模型训练前,最好先基于现有的数据,做一些简单的数据分析,可以帮我们更好的理解数据。(如果不想看这一段,可以跳到后面的模型训练部分)
import matplotlib.pyplot as plt
fig = plt.figure() #画布
loss = data.groupby('是否为流失')['客户ID'].count() #对流失进行分组统计,计算流失与未流失人数
label = ['否','是'] #标签
plt.bar(label,loss,width=0.5) #绘制条形图,宽度为0.5
plt.title('流失比例') #标题
plt.show()
data['是否为流失'].value_counts(normalize=True) #统计流失与未流失个数,显示为频率
用户流失率大概为26%
该数据集中,有三个连续型的数值型数据,我们可以简单分析一下数值型数据对于流失率的影响。
fig = plt.figure(figsize=(10,10)) #设置图像大小 #客户留存时间对比 ax1 = fig.add_subplot(3,1,1) #子图1 sns.distplot(data_churn['客户留存时间'],bins=10,hist=True,kde=False,color='b') #流失客户直方图,箱数设为10,不绘制核密度图,颜色为蓝色 sns.distplot(data_retention['客户留存时间'],bins=10,hist=True,kde=False,color='g') #未流失客户直方图,箱数设为10,不绘制核密度图,颜色为绿色 #客户每月消费对比 ax2 = fig.add_subplot(3,1,2)#子图2 sns.distplot(data_churn['每月消费'],bins=10,hist=True,kde=False,color='b') #流失客户直方图,箱数设为10,不绘制核密度图,颜色为蓝色 sns.distplot(data_retention['每月消费'],bins=10,hist=True,kde=False,color='g') #未流失客户直方图,箱数设为10,不绘制核密度图,颜色为绿色 #客户总消费对比 ax3 = fig.add_subplot(3,1,3)#子图3 sns.distplot(data_churn['总消费'],bins=10,hist=True,kde=False,color='b') #流失客户直方图,箱数设为10,不绘制核密度图,颜色为蓝色 sns.distplot(data_retention['总消费'],bins=10,hist=True,kde=False,color='g') #未流失客户直方图,箱数设为10,不绘制核密度图,颜色为绿色 plt.subplots_adjust(wspace=0.1) #子图水平距离 plt.show()
总消费的情况也是一样。
离散型的数据对于流失率的影响
分类特征很多,只选取其中6个特征作查看
#分类特征很多,只选取其中6个特征作查看 fig,axes = plt.subplots(2,3,figsize=(12,12)) #设置图像大小、子图排列方式,两行三列 axe=axes.ravel() #axes中解压所有子图数组 #流失与性别的关系 data0 = data.groupby(['性别','是否为流失'])['是否为流失'].count().unstack() #对性别和流失进行分组统计,计算性别,行转列 data0.plot(kind='bar',ax = axe[0],stacked='True') #绘制柱状图,位置是第一个子图,进行堆叠 #流失与老年人的关系 data1 = data.groupby(['客户是否为老年人','是否为流失'])['是否为流失'].count().unstack() data1.plot(kind='bar',ax = axe[1],stacked='True') #绘制柱状图,位置是第二个子图,进行堆叠 #流失与是否有配偶的关系 data2 = data.groupby(['客户是否有配偶','是否为流失'])['是否为流失'].count().unstack() data2.plot(kind='bar',ax = axe[2],stacked='True') #绘制柱状图,位置是第三个子图,进行堆叠 #流失与是否有设备保护的关系 data3 = data.groupby(['客户是否有设备保护','是否为流失'])['是否为流失'].count().unstack() data3.plot(kind='bar',ax = axe[3],stacked='True') #绘制柱状图,位置是第四个子图,进行堆叠 #流失与结算方式的关系 data4 = data.groupby(['结算方式','是否为流失'])['是否为流失'].count().unstack() data4.plot(kind='bar',ax = axe[4],stacked='True') #绘制柱状图,位置是第五个子图,进行堆叠 #流失与客户的互联网服务提供商的关系 data5 = data.groupby(['客户的互联网服务提供商','是否为流失'])['是否为流失'].count().unstack() data5.plot(kind='bar',ax = axe[5],stacked='True') #绘制柱状图,位置是第六个子图,进行堆叠 plt.subplots_adjust(hspace=0.6) #子图竖直距离 plt.show()
从这张图中可以看出,性别和客户是否有配偶两个特征与整体流失率没有关系
但是可以看出,老年人的群体的流失率比非老年人群体的流失率高。
从这张图中可以看出,月结用户和不购买设备保护服务的流失率较高,光纤用户的流失率也较高。
基本信息的可视化分析告一段落,可以进行预测任务了,对于用户留存分析这一任务,我们预测的首要目标是为了找出影响用户留存的最主要的因素。
首先进行拆分特征和标签
#特征
#特征是第二列至倒数第二列
x = data.iloc[:,1:-1] #特征。按行列索引,取所有行、第二列至倒数第二列
x[:5]
第一列和最后一列是id和是否流失,都不是特征,所以不需要。
y = data['是否为流失'] #标签。取最后一列,标签列
y[:5]
是否为流失是标签列,变量命名为y。
将数据集分割为训练集和测试集
from sklearn.model_selection import train_test_split
#将数据分割为训练集,测试集,训练集标签,测试集标签
X_train,X_test,y_train,y_test = train_test_split(x, y, test_size=0.2) #测试集占比20%
由于模型不能识别字符型的数据,所以我们对字符型的数据进行编码
这里对标签列采用标签编码,对其他的特征列使用独热编码
#特征编码。分别对训练集测试集编码,防止数据泄露 #DictVectorizer进行独热编码时,不会将数值型编码,而是保留 from sklearn.preprocessing import LabelEncoder #标签编码器 from sklearn.feature_extraction import DictVectorizer #独热编码器 dv = DictVectorizer() #独热编码 le = LabelEncoder() #标签编码 dv_fit = dv.fit(X_train.to_dict(orient='records')) #转为dict,训练 le_fit = le.fit(y_train) #训练 X_train = dv_fit.transform(X_train.to_dict(orient='records')) #训练集转换 X_test = dv_fit.transform(X_test.to_dict(orient='records')) #测试集转换 y_train = le_fit.transform(y_train) #训练集转换 y_test = le_fit.transform(y_test) #测试集转换
这里可以查看以下所有编码过的特征列的名称
dv.get_feature_names_out() #获取特征名称
这里采用随机森林算法
#随机森林分类
from sklearn.ensemble import RandomForestClassifier
#随机森林分类器
clf = RandomForestClassifier(
min_samples_leaf=20, #每个节点的最小样本数
max_depth=10, #最大树深
n_estimators=150, #弱分类器的个数
random_state=42 #随机种子固定
)
clf.fit(X_train,y_train) #训练分类器
等模型拟合完毕,就可以使用测试集的数据查看模型的得分了。
#准确率
print(clf.score(X_train,y_train)) #训练集准确率
print(clf.score(X_test,y_test)) #测试集准确率
该模型在测试集上的准确率为0.78
可以通过模型预测的预测值和真实值建立混淆矩阵,并用热力图展示出来。
#预测 y_pred = clf.predict(X_test) #预测测试集标签 y_pred #混淆矩阵 from collections import Counter import seaborn as sns from sklearn.metrics import confusion_matrix print("真实值:",Counter(y_test)) #真实标签 print("预测值:",Counter(y_pred)) #预测标签 print ("总体准确率:\n",round(sum(y_test==y_pred) / len(y_test),4)) #计算准确率 confmat= confusion_matrix(y_true=y_test,y_pred=y_pred) #输出混淆矩阵 sns.heatmap(confmat,annot=True) #绘制热度图
from sklearn.metrics import classification_report
print(classification_report(y_pred,y_test,target_names=['否','是'])) #输出模型评估报告
还记的我们的最终目的吗?是找到影响流失率的关键因素。
可以使用feature_importances_ 查看特征重要性,来达到这一目的。
这里对特征重要性做了以下处理,和特征名称做了合并,用于可视化。
#特征排序
importance = clf.feature_importances_ #特征重要性
indices = np.argsort(importance) #获取排序索引
#indices
importance_sort = clf.feature_importances_[indices] #特征重要性按索引排序
feature_names_sort = np.array(dv.get_feature_names_out())[indices] #特征名转化为array格式后排序
list(zip(importance_sort,feature_names_sort))
我们把特征重要性小于0.01的忽略,找出大于0.01的所有特征列,并作可视化。
#特征筛选 importance_select = [] #特征重要性 feature_select = [] #特征名 #获取筛选后的特征名和特征重要性 for i,j in list(zip(importance_sort,feature_names_sort)): #特征重要性是否大于0.01 if i>0.01: importance_select.append(i) #添加到importance_select中 feature_select.append(j) #添加到feature_select中 else: continue print(importance_select) print(feature_select) #特征可视化 y_pos = np.arange(len(feature_select)) #特征个数的排列 fig = plt.figure(figsize=(12,12)) #设置图像大小 plt.barh(y_pos, importance_select, align='center') #条形图 plt.yticks(y_pos, feature_select) #y轴刻度 plt.xlabel('特征') #x轴标签 plt.xlim(0,1) #x轴坐标范围 plt.title('特征重要性') #标题 plt.show() plt.savefig("out3.jpg")
该图就是特征重要性从高到低排序的结果了,可以看到影响客户流失率最主要的几个因素。
import numpy as np import pandas as pd pd.set_option('display.max_columns', None) #显示所有列 import matplotlib.pyplot as plt plt.rcParams['font.sans-serif']=['SimHei'] #显示中文 import seaborn as sns import warnings warnings.filterwarnings("ignore") #忽略警告 # 数据读取 data = pd.read_csv("电信用户流失数据.csv") print("---------------------------数据概览------------------------------------") print(data.head(5)) print("---------------------------每个特征的缺失值个数------------------------------------") print(data.isnull().sum()) data = data.dropna() #删除缺失值所在行 print("---------------------------删除行之后的缺失值个数------------------------------------") print(data.isnull().sum()) print("---------------------------查看数据信息------------------------------------") print(data.info()) #特征类型转换 data['客户是否为老年人'] = data['客户是否为老年人'].astype(str) #转为字符型 print("---------------------------类型转换的数据信息------------------------------------") print(data.info()) print("---------------------------查看数据统计量------------------------------------") print(data.describe()) print("---------------------------查看特征名和特征个数------------------------------------") print('列名:\n', data.columns.values) #列名 print(' '*20) print('特征个数:', len(data.columns)-1) #特征个数 fig = plt.figure() #画布 loss = data.groupby('是否为流失')['客户ID'].count() #对流失进行分组统计,计算流失与未流失人数 label = ['否','是'] #标签 plt.bar(label,loss,width=0.5) #绘制条形图,宽度为0.5 plt.title('流失比例') #标题 plt.savefig("./churn_rate.jpg") print("---------------------------计算流失比例------------------------------------") print(data['是否为流失'].value_counts(normalize=True)) #数值型特征的客户对比 data_churn = data[data['是否为流失']=='是'] #流失 data_retention = data[data['是否为流失']=='否'] #未流失 #三个数值特征可视化 fig = plt.figure(figsize=(10,10)) #设置图像大小 #客户留存时间对比 ax1 = fig.add_subplot(3,1,1) #子图1 sns.distplot(data_churn['客户留存时间'],bins=10,hist=True,kde=False,color='b') #流失客户直方图,箱数设为10,不绘制核密度图,颜色为蓝色 sns.distplot(data_retention['客户留存时间'],bins=10,hist=True,kde=False,color='g') #未流失客户直方图,箱数设为10,不绘制核密度图,颜色为绿色 #客户每月消费对比 ax2 = fig.add_subplot(3,1,2)#子图2 sns.distplot(data_churn['每月消费'],bins=10,hist=True,kde=False,color='b') #流失客户直方图,箱数设为10,不绘制核密度图,颜色为蓝色 sns.distplot(data_retention['每月消费'],bins=10,hist=True,kde=False,color='g') #未流失客户直方图,箱数设为10,不绘制核密度图,颜色为绿色 #客户总消费对比 ax3 = fig.add_subplot(3,1,3)#子图3 sns.distplot(data_churn['总消费'],bins=10,hist=True,kde=False,color='b') #流失客户直方图,箱数设为10,不绘制核密度图,颜色为蓝色 sns.distplot(data_retention['总消费'],bins=10,hist=True,kde=False,color='g') #未流失客户直方图,箱数设为10,不绘制核密度图,颜色为绿色 plt.subplots_adjust(wspace=0.1) #子图水平距离 plt.savefig("./num_type_feature_churn_rate.jpg") print("客户留存时间越长,越不容易流失;每月消费20到30月的客户很多,客户是否流失与消费金额关系不大。") #分类特征很多,只选取其中6个特征作查看 fig,axes = plt.subplots(2,3,figsize=(12,12)) #设置图像大小、子图排列方式,两行三列 axe=axes.ravel() #axes中解压所有子图数组 #流失与性别的关系 data0 = data.groupby(['性别','是否为流失'])['是否为流失'].count().unstack() #对性别和流失进行分组统计,计算性别,行转列 data0.plot(kind='bar',ax = axe[0],stacked='True') #绘制柱状图,位置是第一个子图,进行堆叠 #流失与老年人的关系 data1 = data.groupby(['客户是否为老年人','是否为流失'])['是否为流失'].count().unstack() data1.plot(kind='bar',ax = axe[1],stacked='True') #绘制柱状图,位置是第二个子图,进行堆叠 #流失与是否有配偶的关系 data2 = data.groupby(['客户是否有配偶','是否为流失'])['是否为流失'].count().unstack() data2.plot(kind='bar',ax = axe[2],stacked='True') #绘制柱状图,位置是第三个子图,进行堆叠 #流失与是否有设备保护的关系 data3 = data.groupby(['客户是否有设备保护','是否为流失'])['是否为流失'].count().unstack() data3.plot(kind='bar',ax = axe[3],stacked='True') #绘制柱状图,位置是第四个子图,进行堆叠 #流失与结算方式的关系 data4 = data.groupby(['结算方式','是否为流失'])['是否为流失'].count().unstack() data4.plot(kind='bar',ax = axe[4],stacked='True') #绘制柱状图,位置是第五个子图,进行堆叠 #流失与客户的互联网服务提供商的关系 data5 = data.groupby(['客户的互联网服务提供商','是否为流失'])['是否为流失'].count().unstack() data5.plot(kind='bar',ax = axe[5],stacked='True') #绘制柱状图,位置是第六个子图,进行堆叠 plt.subplots_adjust(hspace=0.6) #子图竖直距离 plt.savefig("./class_feature_churn_rate.jpg") print("性别与流失的关系不大;老年人更容易流失;没有配偶更容易流失;月结客户更容易流失;使用光纤的客户更容易流失") #特征 #特征是第二列至倒数第二列 x = data.iloc[:,1:-1] #特征。按行列索引,取所有行、第二列至倒数第二列 print("---------------------------特征列概览------------------------------------") print(x[:5]) #标签 #最后一列为标签 y = data['是否为流失'] #标签。取最后一列,标签列 print("---------------------------标签列概览------------------------------------") print(y[:5]) #分割数据集 from sklearn.model_selection import train_test_split #将数据分割为训练集,测试集,训练集标签,测试集标签 X_train,X_test,y_train,y_test = train_test_split(x, y, test_size=0.2, random_state=42) #测试集占比20%,设置随机种子为42 #特征编码。分别对训练集测试集编码,防止数据泄露 #DictVectorizer进行独热编码时,不会将数值型编码,而是保留 from sklearn.preprocessing import LabelEncoder #标签编码器 from sklearn.feature_extraction import DictVectorizer #独热编码器 dv = DictVectorizer() #独热编码 le = LabelEncoder() #标签编码 dv_fit = dv.fit(X_train.to_dict(orient='records')) #转为dict,训练 le_fit = le.fit(y_train) #训练 X_train = dv_fit.transform(X_train.to_dict(orient='records')) #训练集转换 X_test = dv_fit.transform(X_test.to_dict(orient='records')) #测试集转换 y_train = le_fit.transform(y_train) #训练集转换 y_test = le_fit.transform(y_test) #测试集转换 #查看特征 print("---------------------------查看特征------------------------------------") print(dv.get_feature_names_out()) #随机森林分类 print("---------------------------随机森林算法训练开始------------------------------------") from sklearn.ensemble import RandomForestClassifier #随机森林分类器 clf = RandomForestClassifier( min_samples_leaf=20, #每个节点的最小样本数 max_depth=10, #最大树深 n_estimators=150, #弱分类器的个数 random_state=42 #随机种子固定 ) clf.fit(X_train,y_train) #训练分类器 print("---------------------------训练结束------------------------------------") print("---------------------------查看准确率------------------------------------") #准确率 print("训练集准确率:",clf.score(X_train,y_train)) #训练集准确率 print("测试集准确率",clf.score(X_test,y_test)) #测试集准确率 #预测 y_pred = clf.predict(X_test) #预测测试集标签 print("---------------------------预测测试集标签------------------------------------") print(y_pred) print("---------------------------评估模型------------------------------------") #混淆矩阵 from collections import Counter import seaborn as sns from sklearn.metrics import confusion_matrix print("真实值:",Counter(y_test)) #真实标签 print("预测值:",Counter(y_pred)) #预测标签 print ("总体准确率:\n",round(sum(y_test==y_pred) / len(y_test),4)) #计算准确率 confmat= confusion_matrix(y_true=y_test,y_pred=y_pred) #输出混淆矩阵 sns.heatmap(confmat,annot=True) #绘制热度图 plt.savefig("./metric_model.jpg") print("---------------------------生成模型报告------------------------------------") #模型报告 from sklearn.metrics import classification_report print(classification_report(y_pred,y_test,target_names=['否','是'])) #输出模型评估报告 print("---------------------------计算特征重要性------------------------------------") #特征排序 importance = clf.feature_importances_ #特征重要性 indices = np.argsort(importance) #获取排序索引 #indices importance_sort = clf.feature_importances_[indices] #特征重要性按索引排序 feature_names_sort = np.array(dv.get_feature_names_out())[indices] #特征名转化为array格式后排序 #特征筛选 importance_select = [] #特征重要性 feature_select = [] #特征名 #获取筛选后的特征名和特征重要性 for i,j in list(zip(importance_sort,feature_names_sort)): #特征重要性是否大于0.01 if i>0.01: importance_select.append(i) #添加到importance_select中 feature_select.append(j) #添加到feature_select中 else: continue #特征可视化 y_pos = np.arange(len(feature_select)) #特征个数的排列 fig = plt.figure(figsize=(12,12)) #设置图像大小 plt.barh(y_pos, importance_select, align='center') #条形图 plt.yticks(y_pos, feature_select) #y轴刻度 plt.xlabel('特征') #x轴标签 plt.xlim(0,1) #x轴坐标范围 plt.title('特征重要性') #标题 plt.savefig("./importance_feature.jpg") print("---------------------------可视化完成 图片已保存------------------------------------")
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。