数据分析-78-电信用户流失预测探索性分析(决策树、逻辑回归)(包含数据及代码)

算法学习4对1辅导论文辅导核心期刊
项目的代码和数据下载可以通过公众号滴滴我


一、项目背景

  • Kaggle平台数据集,项目目标对流失用户进行预测并找出影响用户流失的重要因子。
  • 本项目仅对数据集进行探索性分析,意图找出影响用户流失的重要因子,并给出有业务价值的建议。

分析概述

  • 数据集中流失用户的比例约为26%,是一个偏态数据集,整体用户流失率较高
  • Contract_Month-to-month与Churn之间相关系数为0.4,呈现较强的正相关关系
  • tenure与Churn之间相关系数约为-0.35,呈现较强的负相关关系
  • 性别对是否流失没有太大影响
  • 老年用户人数不多,但是流失比例较高,流失风险较大
  • 没有伴侣的流失风险较高
  • 经济不独立的流失风险高
  • 未开通服务(网络安全服务、在线备份服务、设备保护、技术支持)的用户- 流失风险较大
  • 是否开通网络电视与是否开通网络电影对用户是否流失的影响不大,但未开通的流失风险稍高
  • 按月签订合同的用户流失风险较高
  • 开通电子账单的用户流失风险较高
  • 采用电子支付的用户流失风险很高
  • 合同按月签订的用户流失风险较高,流失概率增加10%以上,按年签订的用户不易流失。
  • 开通电子账单的用户流失风险较高,流失概率增加约7%
  • Contract合同签订方式这一特征对用户是否流失有巨大影响

二、数据说明

该数据集WA_Fn-UseC_-Telco-Customer-Churn.csv7043行数据,共20个字段,分别是:

字段说明
customerID用户ID
gender性别
SeniorCitizen是否是老年人(1代表是)
Partner是否有配偶(Yes or No)
Dependents是否经济独立(Yes or No)
tenure用户入网时间
PhoneService是否开通电话业务(Yes or No)
MultipleLines是否开通多条电话业务(Yes 、 No or No phoneservice)
InternetService是否开通互联网服务(No、DSL数字网络或filber potic光线网络)
OnlineSecurity是否开通网络安全服务(Yes、No or No internetservice)
OnlineBackup是否开通在线备份服务(Yes、No or No internetservice)
DeviceProtection是否开通设备保护服务(Yes、No or No internetservice)
TechSupport是否开通技术支持业务(Yes、No or No internetservice)
StreamingTV是否开通网络电视(Yes、No or No internetservice)
StreamingMovies是否开通网络电影(Yes、No or No internetservice)
Contract合同签订方式(按月、按年或者两年)
PaperlessBilling是否开通电子账单(Yes or No)
PaymentMethod付款方式(bank transfer、credit card、electronic check、mailed check)
MonthlyCharges月度费用
TotalCharges总费用
Churn是否流失(Yes or No)

三、数据处理

3.1 导入包并读取数据

import numpy as np
import pandas as pd
data = pd.read_csv("WA_Fn-UseC_-Telco-Customer-Churn.csv")
data.info()

在这里插入图片描述

3.2 数据正确性检验

  • D列是否有重复值或是空值(info中已验证没有)
  • 各个字段的取值是否和给定的取值范围一致
  • 数据中是否有重复值(完全相同的两行)
# 判断ID列是否有重复
data['customerID'].nunique() == data.shape[0]

True

# 判断data中是否有重复值
data.duplicated().sum()

0

# 判断data中各个字段的取值是否与数据字典中一致
# 以及判断是否存在额外的空值,如空格表示的空值
for column in data.columns:
    print(column + ":" + str(data[column].unique()))

在这里插入图片描述

3.3 数据缺失值检验

# 统计数据缺失率
miss_rate = data.apply(lambda x:sum(x.isnull())/len(x),axis=0)
miss_rate

在这里插入图片描述

3.4 数据字段类型探索

  • LR模型需要对分类变量进行独热编码操作
  • object类型无法带入模型进行建模
# 离散字段
category_cols = ['gender', 'SeniorCitizen', 'Partner', 'Dependents', 
                'PhoneService', 'MultipleLines', 'InternetService', 'OnlineSecurity', 'OnlineBackup', 
                'DeviceProtection', 'TechSupport', 'StreamingTV', 'StreamingMovies', 'Contract', 'PaperlessBilling',
                'PaymentMethod']

# 连续字段
numeric_cols = ['MonthlyCharges', 'TotalCharges','tenure']

# 标签
target = 'Churn'

# ID列
ID_col = 'customerID'
# TotalCharges类型转换,报错ValueError: could not convert string to float: ' ',说明其中存在空格
# data['TotalCharges'].astype(float)
# TotalCharges类型转换,空格数据用 0 填充(需要从业务角度理解)
data['TotalCharges'] = data['TotalCharges'].apply(lambda x: 0 if x== ' ' else x).astype(float)
data.info()

在这里插入图片描述

3.5 异常值检验

  • 离散型变量异常值在前期正确性检验阶段已进行验证,故本次只对连续型变量进行异常值检验
data[numeric_cols].describe()

在这里插入图片描述
三倍标准差法

for col in numeric_cols:
    print(f"{col}:[{data[col].mean() - 3 * data[col].std()},{data[col].mean() + 3 * data[col].std()}]")

MonthlyCharges:[-25.508448832436443,155.0318337536348]
TotalCharges:[-4520.649105503237,9080.117712630881]
tenure:[-41.30729441104045,106.0495917275249]

箱线图法

# 绘制箱线图
import matplotlib.pyplot as plt
plt.figure(figsize=(16, 6), dpi=200)
plt.subplot(121)
plt.boxplot(data['MonthlyCharges'])
plt.xlabel('MonthlyCharges')
plt.subplot(122)
plt.boxplot(data['TotalCharges'])
plt.xlabel('TotalCharges')

在这里插入图片描述

  • 使用上述两种方案分别进行判断,MonthlyCharges和TotalCharges两者中均不存在异常数据

四、探索性分析

4.1 标签分布分析

y = data['Churn']
print(f'Percentage of Churn:{round(y.value_counts(normalize=True)[1]*100,2)}%  --> ({y.value_counts()[1]}customer)')
print(f'Percentage of customer did not churn:{round(y.value_counts(normalize=True)[0]*100,2)}  %  --> ({y.value_counts()[0]} customer)')

Percentage of Churn:26.54% --> (1869customer)
Percentage of customer did not churn:73.46 % --> (5174 customer)

  • 流失用户的比例约为26%,原始数据集是一个偏态数据集

4.2 特征标签相关性分析

  • 相关系数矩阵
# 去掉原始数据中ID列
data_temp = data.iloc[:,1:].copy()

# 标签列转换
data_temp['Churn'] = data_temp['Churn'].apply(lambda x: 1 if x=='Yes' else 0)
data_temp['Churn'].replace(to_replace = 'No', value=0, inplace=True)
# 哑变量转换,将分类变量转换为哑变量,否则无法进行相关系数计算
data_dummies = pd.get_dummies(data_temp)
# 计算相关系数矩阵
data_dummies.corr()['Churn'].sort_values(ascending=False)

在这里插入图片描述

# 相关系数可视化展示
import seaborn as sns
sns.set()
plt.figure(figsize=(15,8), dpi=200)
data_dummies.corr()['Churn'].sort_values(ascending=False).plot(kind='bar')

在这里插入图片描述

  • Contract_Month-to-month与Churn之间相关系数为0.4,呈现较强的正相关关系
  • tenure与Churn之间相关系数约为-0.35,呈现较强的负相关关系

4.3 数据可视化探索

col_1 = ["gender", "SeniorCitizen", "Partner", "Dependents"]

fig,axes=plt.subplots(nrows=2,ncols=2,figsize=(16,12), dpi=200)

# 采用原始数据集进行绘制
for i, item in enumerate(col_1):
    plt.subplot(2,2,(i+1)) #子图标记从1开始
    ax=sns.countplot(x=item,hue="Churn",data=data,palette="Blues", dodge=False)
    plt.xlabel(item)
    plt.title("Churn by "+ item)

在这里插入图片描述

  • 性别对是否流失没有太大影响
  • 老年用户人数不多,但是流失比例较高,流失风险较大
  • 没有伴侣的流失风险较高
  • 经济不独立的流失风险高
col_2 = ["OnlineSecurity", "OnlineBackup", "DeviceProtection", "TechSupport", "StreamingTV", "StreamingMovies"]

fig,axes=plt.subplots(nrows=2,ncols=3,figsize=(16,12))

for i, item in enumerate(col_2):
    plt.subplot(2,3,(i+1))
    ax=sns.countplot(x=item,hue="Churn",data=data,palette="Blues",order=["Yes","No","No internet service"], dodge=False)
    plt.xlabel(item)
    plt.title("Churn by "+ item)

在这里插入图片描述

  • 未开通服务(网络安全服务、在线备份服务、设备保护、技术支持)的用户流失风险较大
  • 是否开通网络电视与是否开通网络电影对用户是否流失的影响不大,但未开通的流失风险稍高
col_3 = ["Contract", "PaperlessBilling", "PaymentMethod"]

fig,axes=plt.subplots(nrows=2,ncols=2,figsize=(16,12))

for i, item in enumerate(col_3):
    plt.subplot(2,2,(i+1))
    ax=sns.countplot(x=item,hue="Churn",data=data,palette="Blues", dodge=False)
    plt.xlabel(item)
    plt.title("Churn by "+ item)

在这里插入图片描述

  • 按月签订合同的用户流失风险较高
  • 开通电子账单的用户流失风险较高
  • 采用电子支付的用户流失风险很高,猜测是否是在线支付服务较差

五、逻辑回归建模

  • 借助逻辑回归线性方程中对应特征的参数探索影响用户流失的重要因子
  • 针对逻辑回归模型来说,对离散变量需要进行独热编码,对连续变量需要进行标准化

5.1 建模

from sklearn import preprocessing
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import make_pipeline
import time
def cate_colName(Transformer, category_cols, drop='if_binary'):
    """
    离散字段独热编码后字段名创建函数
    
    :param Transformer: 独热编码转化器(已经训练好的)
    :param category_cols: 输入转化器的离散变量
    :param drop: 独热编码转化器的drop参数
    """
    cate_cols_new = []
    col_value = Transformer.categories_
    
    for i, j in enumerate(category_cols):
        if (drop == 'if_binary') & (len(col_value[i]) == 2):
            cate_cols_new.append(j)
        else:
            for f in col_value[i]:
                feature_name = j + '_' + f
                cate_cols_new.append(feature_name)
    return(cate_cols_new)
dataLR = data.copy()
dataLR['Churn'] =  dataLR['Churn'].apply(lambda x: 1 if x=='Yes' else 0)
# 切分训练集和测试集
train, test = train_test_split(dataLR, test_size=0.3, random_state=21)
# 划分特征和标签
X_train = train.drop(columns=[ID_col, target]).copy()
y_train = train['Churn'].copy()
X_test = test.drop(columns=[ID_col, target]).copy()
y_test = test['Churn'].copy()
# 数据预处理:对离散变量进行独热编码(跳过二分类变量),对连续变量进行z-score标准化
logistic_preprocess = ColumnTransformer([
    ('cat', preprocessing.OneHotEncoder(drop='if_binary'), category_cols), 
    ('num', preprocessing.StandardScaler(), numeric_cols)
])
# 实例化逻辑回归模型
logistic_model = LogisticRegression(max_iter=int(1e8))
# 设置pipeline
logistic_pipeline = make_pipeline(logistic_preprocess, logistic_model)
# 网格搜索参数空间
num_pre = ['passthrough', preprocessing.StandardScaler()]
# 注意是两个下划线__
logistic_param = [
    {'columntransformer__num':num_pre, 'logisticregression__penalty': ['l1'], 'logisticregression__C': np.arange(0.1, 2.1, 0.1).tolist(), 'logisticregression__solver': ['saga']}, 
    {'columntransformer__num':num_pre, 'logisticregression__penalty': ['l2'], 'logisticregression__C': np.arange(0.1, 2.1, 0.1).tolist(), 'logisticregression__solver': ['lbfgs', 'newton-cg', 'sag', 'saga']}, 
    {'columntransformer__num':num_pre, 'logisticregression__penalty': ['elasticnet'], 'logisticregression__C': np.arange(0.1, 1, 0.1).tolist(), 'logisticregression__l1_ratio': np.arange(0.1, 1.1, 0.1).tolist(), 'logisticregression__solver': ['saga']}
]
# 实例化网格搜索评估器
logistic_GS = GridSearchCV(estimator = logistic_pipeline,
                            param_grid = logistic_param)

s = time.time()
logistic_GS.fit(X_train, y_train)
print(time.time()-s, "s")

1960.2904598712921 s

logistic_GS.best_score_

0.8046653144016227

logistic_GS.best_params_

在这里插入图片描述

from sklearn.metrics import accuracy_score, recall_score, precision_score, f1_score, roc_auc_score
def result_df(model, X_train, y_train, X_test, y_test, metrics=
              [accuracy_score, recall_score, precision_score, f1_score, roc_auc_score]):
    res_train = []
    res_test = []
    col_name = []
    for fun in metrics:
        res_train.append(fun(model.predict(X_train), y_train))
        res_test.append(fun(model.predict(X_test), y_test)) 
        col_name.append(fun.__name__) # 输出评估指标函数的名字 .__name__ 函数的名字
    idx_name = ['train_eval', 'test_eval']
    res = pd.DataFrame([res_train, res_test], columns=col_name, index=idx_name)
    return res
result_df(logistic_GS.best_estimator_, X_train, y_train, X_test, y_test)

在这里插入图片描述

5.2 流失因子探索

  • 借助逻辑回归模型中线性方程的参数探索特征重要性
# 获取逻辑回归线性方程的系数
coef = logistic_GS.best_estimator_.named_steps['logisticregression'].coef_
# 将系数与特征对应
onehot_tf = logistic_GS.best_estimator_.named_steps['columntransformer'].named_transformers_['cat']
# 独热编码后离散变量的变量名
category_cols_new = cate_colName(onehot_tf, category_cols)
# 全部特征的变量名
features = category_cols_new + numeric_cols
# 特征和权重之间的对应关系
feature_to_weight = pd.Series(coef.flatten(), index=features)
feature_to_weight

在这里插入图片描述

# 可视化展示
plt.figure(figsize=(16, 6), dpi=200)

# 挑选正相关的前10个变量
plt.subplot(121)
feature_to_weight.sort_values(ascending = False)[:10].plot(kind='bar')

# 挑选负相关的前10个变量
plt.subplot(122)
feature_to_weight.sort_values(ascending = False)[-10:].plot(kind='bar')

在这里插入图片描述

  • 合同按月签订的用户流失风险较高,流失概率增加10%以上,按年签订的用户不易流失。
  • 开通电子账单的用户流失风险较高,流失概率增加约7%。

六、决策树建模

  • 决策树建模无需独热编码,只需进行OrdinalEncoder
  • 树模型无需标准化处理
  • 树模型具有一定随机性,多次运行得到的结果可能不一样,也不能像逻辑回归那样有具体的参数,故最终结果仅供参考

6.1 建模

from sklearn.tree import DecisionTreeClassifier
# 数据预处理
tree_pre = ColumnTransformer([
    ('cat', preprocessing.OrdinalEncoder(), category_cols), 
    ('num', 'passthrough', numeric_cols)
])
# 实例化模型
tree_model = DecisionTreeClassifier()

# 构造pipeline
tree_pipeline = make_pipeline(tree_pre, tree_model)
# 构造包含阈值的参数空间
tree_param = {'decisiontreeclassifier__ccp_alpha': np.arange(0, 1, 0.1).tolist(),
              'decisiontreeclassifier__max_depth': np.arange(2, 8, 1).tolist(), 
              'decisiontreeclassifier__min_samples_split': np.arange(2, 5, 1).tolist(), 
              'decisiontreeclassifier__min_samples_leaf': np.arange(1, 4, 1).tolist(), 
              'decisiontreeclassifier__max_leaf_nodes':np.arange(6,10, 1).tolist()}
# 网格搜索
tree_GS = GridSearchCV(estimator = tree_pipeline,
                        param_grid = tree_param)
# 在训练集上进行训练
s = time.time()
tree_GS.fit(X_train, y_train)
print(time.time()-s, "s")

168.0950047969818 s

result_df(tree_GS.best_estimator_, X_train, y_train, X_test, y_test)

在这里插入图片描述

tree_GS.best_score_

0.79026369168357

tree_GS.best_params_

在这里插入图片描述

6.2 流失因子探索

  • 借助树模型的feature_importances_属性探索各个特征的重要性
feature_importance = tree_GS.best_estimator_.named_steps['decisiontreeclassifier'].feature_importances_
feature_to_importance = pd.Series(feature_importance, index=category_cols + numeric_cols)
feature_to_importance

在这里插入图片描述

  • 共有5个特征参与了决策树模型的构建
# 可视化展示
plt.figure(figsize=(16, 6), dpi=200)

# 挑选正相关的前10个变量
plt.subplot(121)
feature_to_importance.sort_values(ascending = False)[:5].plot(kind='bar')

在这里插入图片描述


算法学习4对1辅导论文辅导核心期刊
项目的代码和数据下载可以通过公众号滴滴我

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AI研习星球

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值