一、数据介绍
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()
通过info函数了解数据集信息。
data.info()
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。
绘制了三种图形,饼图,箱线图,柱状图。
饼图展示的是,数据集客户流失占比。
箱线图展示的是,数据集数值特征的分布情况,通过区别流失/未流失的数据,可以更直观的体会到该特征对客户是否流失的影响。
柱状图展示的是,数据集分类特征的分布情况。
第三行柱状图,是在分类特征的不同类别中区分流失/未流失数据分布情况。
第四行柱状图,是比较分类特征的不同类别中流失客户的占比。
从这些图表中可以看出:
- 流失客户占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")
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()
通过相关性分析可以发现: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
特征只有两个取值,可以直接用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_)
最终得到min-max标准化分数更高,准确率为0.9556。
五、 模型构建
在上面的网格搜索中,已经进行了模型的训练。
计算空准确率
空准确率:指的是模型只预测为一类,也能达到的一个准确率。
在本数据集中,准确率需要超过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)
六、模型调优
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)
经过特征选择后训练数据只保留了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_)
3、模型验证
使用验证集对该模型进行验证。
X_val_select=rfecv.transform(X_val)
predict_evaluate(grid.best_estimator_,X_val_select,y_val)
将未优化模型和优化后的模型评估进行对比。
优化后的模型准确率为95.44%大于空准确率84.35%,模型是有意义的。
七、最终模型
1、最终模型评估
用得出的最佳参数组合模型,预测评估之前划分出的测试集。
X_test_select=rfecv.transform(X_test)
predict_evaluate(grid.best_estimator_,X_test_select,y_test)
混淆矩阵
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()
分类报告
from sklearn.metrics import classification_report
class_report = classification_report(y_test, y_pred)
print(class_report)
2、评估分析
- 模型的准确率(Accuracy)为 0.94,这意味着在所有测试样本中,有 94% 的样本被正确分类。
- 对于流失客户,精确率(Precision)为0.84,查全率(Recall)为0.79,F1分数为0.82。在该案例背景下,可能更看重精确率,因为若能精准找出流失客户,可以减少在该客户身上投入的时间、成本等。
- 在样本不均衡的情况下,AUC分数为0.96,说明该模型有很好的分类能力。
- 从分类报告(classification_report)中,可以看到模型在预测客户未流失方面表现更好。这可能是因为数据集中未流失客户的样本数量远多于流失客户,导致模型偏向于预测未流失客户。
参考资料
kNN算法调参--超参数和网格搜索_knn调参-CSDN博客