(逻辑回归)电信用户流失分析与预测

项目说明:基于逻辑回归的电信用户流失分析与建模
项目数据:kaggle
获取一个新客户的成本远低于挽留或者维系一个老客户的成本
如何挽留更多用户成为一项关键业务指标,为了更好运营用户,首先要了解流失用户的特征,分析流失原因,并合理预测下个阶段的预测用流失率,确定挽留目标用户群体并制定有效方案。
字段描述:
customerID : 用户ID。
gender:性别。(Female & Male)
SeniorCitizen :老年人 (1表示是,0表示不是)
Partner :是否有配偶 (Yes or No)
Dependents :是否经济独立 (Yes or No)
tenure : 客户的职位(0-72,共73个职位)
PhoneService : 是否开通电话服务业务 (Yes or No)
MultipleLines: 是否开通了多线业务(Yes 、No or No phoneservice 三种)
InternetService:是否开通互联网服务 (No, DSL数字网络,fiber optic光纤网络 三种)
OnlineSecurity:是否开通网络安全服务(Yes,No,No internetserive 三种)
OnlineBackup:是否开通在线备份业务(Yes,No,No internetserive 三种)
DeviceProtection:是否开通了设备保护业务(Yes,No,No internetserive 三种)
TechSupport:是否开通了技术支持服务(Yes,No,No internetserive 三种)
StreamingTV:是否开通网络电视(Yes,No,No internetserive 三种)
StreamingMovies:是否开通网络电影(Yes,No,No internetserive 三种)
Contract:签订合同方式 (按月,一年,两年)
PaperlessBilling:是否开通电子账单(Yes or No)
PaymentMethod:付款方式(bank transfer,credit card,electronic check,mailed check)
MonthlyCharges:月费用
TotalCharges:总费用
Churn:该用户是否流失(Yes or No)
使用语言:python 3.7

from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import pyecharts as pye
import datetime
%matplotlib inline

plt.rcParams['font.sans-serif']=['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False  # 用来正常显示负号

#警告删除
import warnings
warnings.simplefilter(action="ignore", category=FutureWarning)
import warnings
warnings.filterwarnings("ignore")
#画图风格
plt.style.use("fivethirtyeight")

Data

df = pd.read_csv('WA_Fn-UseC_-Telco-Customer-Churn.csv',pd.set_option('display.max_columns', None))
df.head()

Data overview

print ("Rows     : " ,df.shape[0])
print ("Columns  : " ,df.shape[1])
print ("\nFeatures : \n" ,df.columns.tolist())
print ("\nMissing values :  ", df.isnull().sum())
print ("\nUnique values :  \n",df.nunique())

在这里插入图片描述

Data Manipulation

df.info()

在这里插入图片描述

df["TotalCharges"] = df["TotalCharges"].astype(float)

在这里插入图片描述
发现不行,可能是有pandas不能识别的空值(空格,编码空格)

df[df["TotalCharges"]==' ']

在这里插入图片描述
果然是有的,发现这些用户既不是流失用户,月份数(tenure)也是0,可能是数据缺失,7000多行里也只有11行所以打算删除

#先转换空格为空值
df["TotalCharges"]=df["TotalCharges"].replace(' ',np.nan)
#查看是否还有空格
df[df["TotalCharges"]==' ']
#删除空值
df = df[df['TotalCharges'].notnull()]
df.shape
#重新设置index
df = df.reset_index()
df.drop(columns='index',inplace=True)

在这里插入图片描述

#类型转换并查看是否转换成功
df['TotalCharges']=df['TotalCharges'].astype(float)
df.info()

在这里插入图片描述
发现以下这几列中的nuniqe都为3且有一种值为’No internet service’or’No phone service’,其实际意义与no其实一样
由于三分类变量不利于我们做逻辑回归
于是将其转换为二分类变量,方便转换为哑变量建模

df_cols = [ 'OnlineSecurity', 'OnlineBackup', 'DeviceProtection',
                'TechSupport','StreamingTV', 'StreamingMovies']
for i in df_cols : 
    df[i]  = df[i].replace({'No internet service' : 'No'})
df['MultipleLines']=df['MultipleLines'].replace({'No phone service':'No'})
#目标列分类
churn     = df[df["Churn"] == "Yes"]
not_churn = df[df["Churn"] == "No"]

#分类变量和离散变量分开
Id_col     = ['customerID']
target_col = ["Churn"]
cat_cols   = df.nunique()[df.nunique() < 6].keys().tolist()
cat_cols   = [x for x in cat_cols if x not in target_col]
num_cols   = [x for x in df.columns if x not in cat_cols + target_col + Id_col]

EDA

流失顾客分布

from pyecharts import Pie
fig=plt.figure(figsize=(8,8))
pie = Pie("流失顾客分布",title_pos='right',width=900,height=300)
pie.add("流失顾客分布", df["Churn"].value_counts().keys().tolist(), df["Churn"].value_counts().values.tolist() ,center=[50,50],is_legend_show=True,is_label_show=True,radius=['40%','75%'])
pie

在这里插入图片描述

print('流失的比率:',str(round((1869/7032)*100,2))+'%')

在这里插入图片描述
是很不平衡的数据集奥,之后要用smote将其变为平衡分类数据以便合理预测
接下来会从分布的角度基于其他分类因素看流失与哪几个因素有关

df.Churn=df.Churn.replace({'Yes':'1','No':'0'})
df.Churn.astype(int)
def val_c(a):
    table=pd.crosstab(df[a],df.Churn)
    table.div(table.sum(1).astype(float), axis=0).plot(kind='bar', stacked=True,figsize=(6,4))
    plt.title('distribution  gra for '+str(a)+' and Churn',size=16)
    plt.xlabel(str(a))
    plt.ylabel('比率')
for i in cat_cols:
    val_c(i)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
我们发现对于分类变量而言


对流失有显著影响的变量有:

  • 年龄,年长的客户更容易流失
  • 是否有伴侣,没有伴侣的客户更容易流失
  • 是否有亲属,没有亲属的客户更容易流失
  • 不同的运营商,以Fiber optic作为运营商的客户更容易流失
  • 网络安全,没有安全服务的客户更容易流失
  • 网络备份,没有备份服务的客户更容易流失
  • 设备保护,没有设备保护的客户更容易流失
  • 技术支持,没有技术支持的客户更容易流失
  • 流媒体电视,有流媒体电视的客户更容易流失
  • 流媒体电影,有流媒体电影的客户更容易流失
  • 合同关系,包月制的客户更容易流失
  • 是否有纸质化账单,纸质化账单的用户更容易流失
  • 支付方式,使用电子支票的客户更容易流失
  • 已支付月数,使用时间越短越容易流失

从分布上看不出影响的变量有:

  • 性别
  • 电话服务
  • 是否有多条线路
  • 是否有电话服务
  • 是否有多条线路
def kdeplot(feature):
    plt.figure(figsize=(9, 4))
    plt.title("KDE for {0}".format(feature))
    ax0 = sns.kdeplot(df[df['Churn'] == 'No'][feature].dropna(), color= 'navy', label= 'Churn: No', shade='True')
    ax1 = sns.kdeplot(df[df['Churn'] == 'Yes'][feature].dropna(), color= 'orange', label= 'Churn: Yes',shade='True')
    plt.xlabel(feature)
for i in num_cols:
    kdeplot(i)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
对于连续型变量来说:

  • 客户使用时间越短,越容易流失
  • 客户月份花费越高,越容易流失,花费越低,越不容易流失,70-100左右最高,80左右到达流失峰值
  • 客户总花费越高,越不容易流失

通过以上定类定量分析,我们可得到流失客户的基本用户特征:
在这里插入图片描述

Model Building

from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.feature_selection import RFE
from sklearn.linear_model import LogisticRegression
from imblearn.over_sampling import SMOTE
from sklearn.metrics import confusion_matrix,accuracy_score,classification_report
from sklearn.metrics import roc_auc_score,roc_curve,scorer
from sklearn.metrics import f1_score

对分类型变量进行one-hot编码,以及删除不要的列

df=df.drop(columns='customerID')
for var in cat_cols:
    cat_list='var'+'_'+var
    cat_list = pd.get_dummies(df[var], prefix=var)
    df=df.join(cat_list)
    
df_vars=df.columns.values.tolist()
to_keep=[i for i in df_vars if i not in cat_cols]
df_final=df[to_keep]
print(df_final.columns.values)
df_final.head()

在这里插入图片描述

SMOTE

因为目标预测列为不平衡分类数据,这里用SMOTE过采用,首先我们从数据集中分离出特征和标签,然后我们通过随机抽样创建训练集合测试集

X = df_final.loc[:, df_final.columns != 'Churn']
y = df_final.loc[:, df_final.columns == 'Churn']
 
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=0)
columns = X_train.columns
print('过采样之前训练集中正负样本的比例分布:')
print(y_train['Churn'].value_counts())
print(y_train['Churn'].value_counts()/len(y_train))
os = SMOTE(random_state=0)
os_data_X,os_data_y=os.fit_sample(X_train, y_train.values.ravel())
os_data_X = pd.DataFrame(data=os_data_X,columns=columns )
os_data_y= pd.DataFrame(data=os_data_y,columns=['Churn'])
print('-------------------------------------------')
print('过采样之后训练集中正负样本的比例分布:')
print(os_data_y['Churn'].value_counts())
print(os_data_y['Churn'].value_counts()/len(os_data_y))

在这里插入图片描述
经过SMOTE算法过采样以后,我们看法正样本合负样本的数量都已经一致了,正负样本的比例达到了平衡

特征筛选-递归特征消除

df_final_vars=df_final.columns.values.tolist()
y=['Churn']
X=[i for i in df_final_vars if i not in y]
logreg = LogisticRegression(solver='liblinear')
rfe = RFE(logreg, 15)#选择15个特征
rfe = rfe.fit(os_data_X.values, os_data_y.values.ravel())
 
features = list(os_data_X.columns[rfe.support_])
 
print('原特征数量:',os_data_X.shape[1])
print()
print('经过模型筛选后的15个特征:')
print(features)
print()
print('特征排名:')
print(rfe.ranking_)

在这里插入图片描述

Logistic Regression

X=os_data_X[features]
y=os_data_y['Churn']
 
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=0)
logreg = LogisticRegression(solver='liblinear')
logreg.fit(X_train, y_train)
 
#在测试集上进行预测
y_pred = logreg.predict(X_test)
print('在测试集上预测的准确率: {:.2f}'.format(logreg.score(X_test, y_test)))

在这里插入图片描述

混淆矩阵

confusion_matrix = confusion_matrix(y_test, y_pred)
sns.heatmap(confusion_matrix, annot=True, fmt='d')
print(classification_report(y_test,y_pred))

在这里插入图片描述
模型效果还可以,之后会在中间加入RFM模型进一步细分客户,待更新待更新…最近在打M5,争取拿个铜牌…

  • 11
    点赞
  • 98
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值