对于电信用户流失的预测

一、分析目的

随着电信行业的不断发展,运营商们越来越重视如何扩大其客户群体。据研究,获取新客户所需的成本远高于保留现有客户的成本,因此为了满足在激烈竞争中的优势,保留现有客户成为一大挑战。对电信行业而言,可以通过数据挖掘等方式来分析可能影响客户决策的各种因素,以预测他们是否会产生流失(停用服务、转投其他运营商等)。

二、数据来源

数据集一共提供了7043条用户样本,每条样本包含21列属性,由多个维度的客户信息以及用户是否最终流失的标签组成。21列原始属性中,除了最后一列Churn表示该数据集的目标变量(即标签列)外,其余20列按照原始数据集中的排列顺序刚好可以分为三类特征群: 客户的基本信息、开通业务信息、签署的合约信息。

 字段说明

 

电信用户流失预测中,运营商最为关心的是客户的召回率,即在真正流失的样本中,我们预测到多少条样本。故其策略是宁可把未流失的客户预测为流失客户而进行多余的留客行为,也不漏掉任何一名真正流失的客户。

三、数据处理

1.引入库

import numpy as np
import pandas as pd
import warnings
warnings.filterwarnings('ignore') #  忽略弹出的warnings信息

2.数据清洗

加载数据

data = pd.read_csv('./WA_Fn-UseC_-Telco-Customer-Churn.csv')
data.head()

 数据预处理

data.duplicated().sum() #检查重复值
data.isnull().any().sum() #检查缺失值

可能存在这样的情况:数据集采用 'Null'、'NaN'、' ' 等字符(串)表示缺失。

数据集中就有这样一列TotalCharges特征,存在特征值为空格字符(' ')如果缺失数据不多则可直接删除行TotalCharge表示总费用,原始数据类型为字符串,可以将其修改成数值型。

(data['TotalCharges'] == ' ').sum() #结果显示有11个样本为空值
drop_index = data.loc[data['TotalCharges'] == ' '].index
data.drop(labels=drop_index,axis=0,inplace=True)  #去空值
data['TotalCharges'] = data['TotalCharges'].astype(dtype='float')  #该数据类型

异常数据处理

#发现只有4列为数值型特征,其中SeniorCitizen只有0,1组成可以视为类别特征,无需进行异常值处理
data.describe([0.2,0.5,0.9,0.99]).T

 使用箱型图进行异常值探索

import matplotlib.pyplot as plt
%matplotlib inline
a = plt.boxplot(data['tenure'],vert=False)
plt.title('tenure')

a = plt.boxplot(data['MonthlyCharges'],vert=False)
plt.title('MonthlyCharges')

a = plt.boxplot(data['TotalCharges'],vert=False)
plt.title('TotalCharges')

由箱型图直观可见,这三列数值特征均不含离群点(即异常值)。

3.特征工程

特征抽取

观察如下几列特征的组成元素:

  • ‘MultipleLines’,'OnlineSecurity', 'OnlineBackup', 'DeviceProtection', 'TechSupport', 'StreamingTV', 'StreamingMovies'
  • 发现MultipleLines特征组成元素为:yes,no和No phone service
  • 剩下几列特征的组成元素为:yes,no和No internet service,那么No phone service就表示no,所以可以将其修改为no,从而减少特征组成元素的数量,方便后期进行特征值化
data.loc[data['MultipleLines']=='No phone service', 'MultipleLines'] = 'No'
internetCols = ['OnlineSecurity', 'OnlineBackup', 'DeviceProtection', 'TechSupport', 'StreamingTV', 'StreamingMovies']
for i in internetCols:
    data.loc[data[i]=='No internet service', i] = 'No'

一些类别特征只有两类取值,可以直接用0、1代替

  • 'Partner','Dependents','PhoneService','MultipleLines','OnlineSecurity', 'OnlineBackup', 'DeviceProtection', 'TechSupport', 'StreamingTV', 'StreamingMovies','PaperlessBilling'
  • 顺便把目标变量也进行编码,直接用0、1代替
cols_name = ['Partner','Dependents','PhoneService','MultipleLines','OnlineSecurity', 'OnlineBackup', 'DeviceProtection', 'TechSupport', 'StreamingTV', 'StreamingMovies','PaperlessBilling']
for col in cols_name:
    data[col] = data[col].map({'Yes': 1, 'No': 0})

data['Churn'] = data['Churn'].map({'Yes': 1, 'No': 0})

其他无序的类别特征采用独热编码

  • 'InternetService', 'Contract', 'PaymentMethod'
cols_name = ['InternetService', 'Contract', 'PaymentMethod']
for col in cols_name:
    oneHot_df = pd.get_dummies(data[col],prefix=col) 
    data = pd.concat(objs=[data,oneHot_df],axis=1)

#删除原来的列
data.drop(labels=cols_name,axis=1,inplace=True)

特征选择

'customerID'特征的每个特征值都不同,因此对模型预测不起贡献,可以直接删除。

data.drop(labels='customerID',axis=1,inplace=True)

四、数据分析

基本特征对客户流失影响

  • 性别、是否老年人、是否有配偶、是否有家属特征对客户流失的影响(占比情况:例如在性别特征中,统计女性流失占不流失的比例and男性)
    • 'gender', 'SeniorCitizen', 'Partner', 'Dependents'
    • 这些特征的组成元素只有【是和否】
  • 入网月数特征对客户流失的影响
    • ‘tenure’
    • 该特征的组成元素有多个
pd.crosstab(data['gender'], data['Churn'])

baseCols = ['gender', 'SeniorCitizen', 'Partner', 'Dependents']
for col in baseCols:
    col_df = pd.crosstab(data[col], data['Churn'])
    p_a = col_df.iloc[0][1] / col_df.iloc[0][0]
    p_b = col_df.iloc[1][1] / col_df.iloc[1][0]
    print(col,":",p_a,p_b)

 

由数据可知:性别对客户流失基本没有影响;年龄对客户流失有影响;是否有配偶对客户流失有影响;是否有家属对客户流失有影响。

流失率和入网月数之间的关系

  • 计算不同入网月数对应的流失率(每月流失客户占当月总客户的比例)
s = data.groupby(by='tenure')['Churn'].sum() / data.groupby(by='tenure')['Churn'].count()
plt.plot(s.index,s.values)
plt.xlabel('month')
plt.ylabel('p')

 发现:流失率随着入网时间的延长呈下降趋势

剩下的合约类型特征、业务类型特征同上进行分析即可,最终发现如下特征对目标标签没有影响,可以将其删除

  • 'gender'、'PhoneService'、'StreamingTV' 和 'StreamingMovies'
data.drop(labels=['gender','PhoneService','StreamingTV','StreamingMovies'],axis=1,inplace=True)

对数据集中的三列连续型数值特征 'tenure', 'MonthlyCharges', 'TotalCharges' 计算相关系数.

  • 如果存在较强相关性,因此可以考虑删除该列,以避免特征冗余
import seaborn as sns
nu_fea = data[['tenure', 'MonthlyCharges', 'TotalCharges']]    # 选择连续型数值特征计算相关系数
nu_fea = list(nu_fea)    # 特征名列表
pearson_mat = data[nu_fea].corr()  
plt.figure(figsize=(8,8)) # 建立图像
sns.heatmap(pearson_mat, square=True, annot=True, cmap="YlGnBu")    # 用热度图表示相关系数矩阵
plt.show() # 展示热度图

 其中 'TotalCharges' 与其他两列特征的相关系数均大于0.6,即存在较强相关性,因此可以考虑删除该列

data.drop(labels='TotalCharges',axis=1,inplace=True)

类别不平衡问题处理

对不同样本类别的数量进行统计

p = data['Churn'].value_counts()
a = plt.pie(p,autopct='%.2f%%',labels=p.index)

 

由饼状图可见流失用户占比为26.54%,存在类别不平衡现象,需要进行相应处理。

from imblearn.over_sampling import SMOTE
s = SMOTE(k_neighbors=3)
x_cols = [col for col in data.columns if col != 'Churn']
X = data[x_cols]
y = data['Churn']
feature,target = s.fit_sample(X,y)

模型选择和评估

召回率代表的意义则是:在真正流失的样本中,我们预测到多少条样本。很明显,召回率是运营商们关心的指标,即宁可把未流失的客户预测为流失客户而进行多余的留客行为,也不漏掉任何一名真正流失的客户。

from sklearn.linear_model import LogisticRegression    # 逻辑回归
from sklearn.svm import SVC    # SVM
from sklearn.ensemble import RandomForestClassifier    # 随机森林
from sklearn.model_selection import train_test_split
from sklearn.metrics import recall_score, f1_score    

x_train,x_test,y_train,y_test = train_test_split(feature,target,test_size=0.2,random_state=2020)

lr = LogisticRegression()
svm = SVC()
rf = RandomForestClassifier()
model_list = [lr,svm,rf]
for model in model_list:
    model = model.fit(x_train,y_train)
    y_pred = model.predict(x_test)
    score = recall_score(y_test,y_pred)
    print(model,score)
    

 

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值