电信用户流失预测

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


image.png

电信用户流失预测中,运营商最为关心的是客户的召回率,即在真正流失的样本中,我们预测到多少条样本。

import numpy as np
import pandas as pd
import warnings
warnings.filterwarnings('ignore') #  忽略弹出的warnings信息
data = pd.read_csv('./WA_Fn-UseC_-Telco-Customer-Churn.csv')
data.tail(10)

数据预处理

data.duplicated().sum()
(data['TotalCharges'] == ' ').sum()
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().T

使用箱型图对tenure、MonthlyCharges和TotalCharges进行异常值探索

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')
image.png

image.png

image.png

特征工程
特征抽取
在特征介绍图中观察如下几列特征的组成元素:
‘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)
image.png

由数据可知:性别对客户流失基本没有影响;年龄对客户流失有影响;是否有配偶对客户流失有影响;是否有家属对客户流失有影响。
观察流失率和入网月数之间的关系
计算不同入网月数对应的流失率(每月流失客户占当月总客户的比例)

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')
image.png

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

剩下的合约类型特征、业务类型特征同上进行分析即可,最终发现如下特征对目标标签没有影响,可以将其删除
'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() # 展示热度图
image.png

其中 '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)
image.png

由饼状图可见流失用户占比为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)
image.png

你可能感兴趣的:(电信用户流失预测)