【机器学习实战】用KNN算法预测电信公司的客户是否流失

一、数据介绍

1、背景与来源

本次数据集来自UCI机器学习库中的电信流失数据集。

伊朗流失 - UCI 机器学习存储库 --- Iranian Churn - UCI Machine Learning Repository

该数据集是在 12 个月内从伊朗某电信公司数据库中随机收集的。

通过收集客户数据、清洗、选择关键特征、建立预测模型、评估和优化模型等步骤,帮助企业了解客户行为模式,并采取相应措施来最大程度地减少流失、提高留存。

2、数据说明

该数据集总共有 3150 行数据,每行代表一个客户,包含 13 列的信息。

数据集的输入变量为12个特征,包含数值特征和分类特征。输出变量为Churn(y) 。

除Churn(y) 外的所有特征都是前 9 个月的数据。流失标签是客户在 12 个月结束时的状态。期间的三个月是指定的规划间隔。

Call Failure呼叫失败的次数
Complains客户是否投诉(0:无投诉,1:投诉)
Subscription Length订阅总月数
Charge Amount充值金额分组
Seconds of Use通话总秒数
Frequency of use通话频率
Frequency of SMS短信频率
Distinct Called Numbers不同电话呼叫的总数
Age Group年龄分组
Tariff Plan资费计划(1:现收现付,2:合同)
Status活动状态(1:活动,2:非活动)
Age年龄
Customer Value客户价值
Churn

流失标签(0:未流失,1:流失)

二、数据清洗

1、数据读取

import pandas as pd
import numpy as np

data=pd.read_csv("Customer Churn.csv")
data.head()

fed1cd85782548bdae6e42b1e59b8241.png

通过info函数了解数据集信息。

data.info()

 e7b2db82eb264a38b7782cc0287fbfdc.png

2、缺失值重复值处理

#检查缺失值
data.isnull().sum()
data.describe()

#删除重复行
data.duplicated().sum()
data=data.drop_duplicates()

经过查看,该数据集中没有缺失值。

三、影响因素分析

1、可视化分析

from plotly.subplots import make_subplots
import plotly.graph_objects as go

data=data.rename(columns={"Churn":"y"})

 可视化的实现是使用plotly。

75df00752f324ca3a9be6a71476cbe50.png

 绘制了三种图形,饼图,箱线图,柱状图。

饼图展示的是,数据集客户流失占比。

箱线图展示的是,数据集数值特征的分布情况,通过区别流失/未流失的数据,可以更直观的体会到该特征对客户是否流失的影响。

柱状图展示的是,数据集分类特征的分布情况。

第三行柱状图,是在分类特征的不同类别中区分流失/未流失数据分布情况。

第四行柱状图,是比较分类特征的不同类别中流失客户的占比。

从这些图表中可以看出:

  • 流失客户占15.6%,未流失客户占84.4%。可以看出,数据集类别不均衡。
  • Seconds of Use、Frequency of use、Distinct Called Numbers和Customer Value在流失和未流失客户间有显著差异。
  • Complains=1,即投诉过的客户流失率最高,高达82.61%。
  • Status=2,即处于非活动状态的客户流失率也较高,有47.51%,将近一半。

从图表中,可以直观看到特征对客户是否流失的影响,例如Seconds of Use、Frequency of use和Complains等。

但是也有一些特征不能从图表直观看出与响应变量的关系。

2、相关性检验

皮尔逊相关系数,用于测量列之间的线性关系。一般,皮尔逊相关系数要求每列特征是正态分布。

但是,在很大程度上,也可以忽略这个要求,因为数据集很大(超过500)。

import matplotlib.pyplot as plt
import seaborn as sns

plt.figure(figsize=(10,10))
sns.heatmap(data=data.corr(),annot=True, cmap="coolwarm") 

d28f9a90ea7b4360a7846683b26b7d47.png

fig = go.Figure()

corrlated=data.corr()["y"].abs()
corrlated=corrlated.drop("y")
fig.add_trace(go.Bar(x=corrlated,y=corrlated.index,orientation='h'))

fig.show()

01cd26a6895848b29100ec337e01faab.png

通过相关性分析可以发现:Complains、Charge Amount、Seconds of Use、Frequency of use、Frequency of SMS、Distinct Called Numbers、Status、Customer Value与y有显著的相关关系,虽然大部分是弱相关,但是与之前可视化分析得到的结论相差不大。

四、特征工程

1、特征编码

该数据集已经对分类特征数值化,这些分类特征分无序和有序两类。
Charge  Amount和Age Group属于有序序列,Complains、Tariff Plan和Status属于无序序列。

其中Complains取值为0和1,不需要进行编码。

相比之下,Tariff Plan、Status都取值为1和2,形式上看起来像是有序的,但实际上我们清楚类别之间并没有自然的顺序关系。

因此,除非有明确的理由表明1和2之间存在某种等级或量化的含义,一般情况下,0和1作为二分类更合适。

from sklearn.preprocessing import OneHotEncoder

#选择要独热编码的列
data_encoder=pd.get_dummies(data,columns=['Tariff Plan','Status'])
#删掉多余特征
data_encoder=data_encoder.drop(["Age","Tariff Plan_2", "Status_2"], axis=1)
data_encoder

54f66b3704e049e593dd0278f0f92128.png

特征只有两个取值,可以直接用0,1来表示类别,避免了额外的维度和稀疏性问题。所以在独热编码后,把多余的列删除。也可以用其他方式将取值1,2转化为0,1。

2、特征归一化

KNN算法依赖于样本间的距离来确定相似度,因此归一化是必要的。

如果不进行归一化,距离计算就会受较大特征的影响,较小的特征权重就被稀释,导致预测结果偏差。

在特征归一化之前,进行数据集的划分,从而防止数据泄露。

#划分数据集
from sklearn.model_selection import train_test_split

def dataset_split(dataset,rs1,rs2):
    X=dataset.drop("y",axis=1)
    y=dataset["y"]
    X_train0, X_test,y_train0,y_test= train_test_split(X,y,test_size=0.2, random_state=rs1)
    X_train,X_val,y_train,y_val= train_test_split(X_train0,y_train0, test_size=0.25, random_state=rs2)
    return X_train,X_val, X_test,y_train,y_val,y_test

    
X_train,X_val, X_test,y_train,y_val,y_test=dataset_split(data_encoder,33,47)

使用网格搜索,选择得分最高的归一化方法。 

from sklearn.pipeline import Pipeline
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.preprocessing import StandardScaler,MinMaxScaler #z分数归一化和min-max标准化

#参数
knn_params = {"classify__n_neighbors": np.arange(1,11,1)} 

#实例化knn模型
knn=KNeighborsClassifier()

#knn_pipeline = Pipeline([('standardize',StandardScaler()), ('classify', knn)])
knn_pipeline = Pipeline([('standardize',MinMaxScaler()), ('classify', knn)]) #min-max标准化分数更高

grid=GridSearchCV(knn_pipeline,knn_params)
grid.fit(X_train,y_train)

print("最佳准确率: %.6f" %grid.best_score_)
print("最佳参数:",grid.best_params_)

d4a154c7a1934e78a4ae87c8bdd7734e.png

最终得到min-max标准化分数更高,准确率为0.9556。

五、 模型构建

在上面的网格搜索中,已经进行了模型的训练。

计算空准确率
空准确率:指的是模型只预测为一类,也能达到的一个准确率。

bf06bb6cc1164ea086b2c53faba9bb76.png

在本数据集中,准确率需要超过84.35%才有意义。

接下来是使用验证集进行预测并评估模型。

from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score,roc_auc_score,roc_curve

#定义一个预测和评估模型的函数
def predict_evaluate(model,X_data,y_data):
    y_pred= model.predict(X_data)
    y_pred_proba= model.predict_proba(X_data)[:, 1]
    accuracy = accuracy_score(y_data, y_pred)
    precision = precision_score(y_data, y_pred)
    recall = recall_score(y_data, y_pred)
    f1 = f1_score(y_data, y_pred)
    roc_auc = roc_auc_score(y_data, y_pred_proba)
    
    print(f"Accuracy: {accuracy}")
    print(f"Precision: {precision}")
    print(f"Recall: {recall}")
    print(f"F1 Score: {f1}")
    print(f"ROC AUC Score: {roc_auc}")  
    
predict_evaluate(grid.best_estimator_,X_val,y_val)

b9fea0267aca4f6b99a0c66f2f254cd9.png  

六、模型调优

1、特征选择

#交叉验证递归特征消除法
from sklearn.feature_selection import RFECV
from sklearn.linear_model import LinearRegression

#选择基模型 
model = LinearRegression()

#使用RFECV进行特征选择
rfecv= RFECV(model, step=1,cv=5)   #表示每次迭代移除一个特征,进行5次交叉验证。
rfecv.fit(X_train,y_train)
X_train_select=rfecv.transform(X_train)

feature_list=np.array(X_train.columns)[rfecv.get_support()]
print("最佳特征数量 : %d" % rfecv.n_features_)
print("选择特征: ",feature_list)

e1943c892b8b4798a43310b7ff66da95.png

经过特征选择后训练数据只保留了11个特征。

2、参数调整

特征选择后,结合网格搜索来寻找模型的最佳超参数。

KNN要调整的参数主要有三个。

  • n_neighbors(k值)
  • metric(距离计算方式)
  • weights(邻居的权重)
knn_params = {"classify__n_neighbors": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
             "classify__metric": ["euclidean", "manhattan"],
             "classify__weights": ['uniform','distance']} 

knn=KNeighborsClassifier()

knn_piepeline = Pipeline([('standardize',MinMaxScaler()), ('classify', knn)])


grid=GridSearchCV(knn_piepeline,knn_params)
grid.fit(X_train_select,y_train)
print("最佳准确率: %.6f" %grid.best_score_)
print("最佳参数:",grid.best_params_)

d9e7492a1f6f4385b405b06ab4109fac.png

3、模型验证

使用验证集对该模型进行验证。

X_val_select=rfecv.transform(X_val)
predict_evaluate(grid.best_estimator_,X_val_select,y_val)

85d193fd129445d6ab926570ff92a583.png

 将未优化模型和优化后的模型评估进行对比。

d7da2a94f1b84b07831b8e1693463474.png

优化后的模型准确率为95.44%大于空准确率84.35%,模型是有意义的。 

七、最终模型

1、最终模型评估

用得出的最佳参数组合模型,预测评估之前划分出的测试集。

X_test_select=rfecv.transform(X_test)
predict_evaluate(grid.best_estimator_,X_test_select,y_test)

 e6f5c2fc94f84129b673dbba11d3a305.png

混淆矩阵 

from sklearn.metrics import confusion_matrix

y_pred= grid.best_estimator_.predict(X_test_select)
cm = confusion_matrix(y_test,y_pred)

plt.figure(figsize=(8, 6))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', 
            xticklabels=['predicted 0', 'predicted 1'], 
            yticklabels=['actual 0', 'actual 1'])
plt.title('confusion_matrix')
plt.show()

72ab672ce65044f8a9088b24c19d2ad5.png

分类报告

from sklearn.metrics import classification_report

class_report = classification_report(y_test, y_pred)
print(class_report)

ffc4e59482414793ae5cd989b90fb7a1.png

 

2、评估分析

  1. 模型的准确率(Accuracy)为 0.94,这意味着在所有测试样本中,有 94% 的样本被正确分类。
  2. 对于流失客户,精确率(Precision)为0.84,查全率(Recall)为0.79,F1分数为0.82。在该案例背景下,可能更看重精确率,因为若能精准找出流失客户,可以减少在该客户身上投入的时间、成本等。
  3. 在样本不均衡的情况下,AUC分数为0.96,说明该模型有很好的分类能力。
  4. 从分类报告(classification_report)中,可以看到模型在预测客户未流失方面表现更好。这可能是因为数据集中未流失客户的样本数量远多于流失客户,导致模型偏向于预测未流失客户。

参考资料

kNN算法调参--超参数和网格搜索_knn调参-CSDN博客

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值