机器学习简单案例:如何预测客户是否流失(附代码)

第一章:机器学习基本概念(附代码)
第二章:KNN算法:从思想到实现(附代码)
第三章:决策树算法:从思想到实现(附代码)
第四章:机器学习简单案例:如何预测客户是否流失(附代码)
第五章:理解数据标准化处理
第六章:线性模型:从原理到实践
第七章:KMeans 聚类算法:从理论到实践(附代码)
第八章:搞懂线性回归与梯度下降原理(附代码)
第九章:深度学习框架PyTorch
第十章:逻辑回归:从基础到实践(附代码)
第十一章:集成学习(一):从理论到实战(附代码)
第十二章:集成学习(二):从理论到实战(附代码)
第十三章:机器学习算法大比武(附代码)
第十四章:理解并解决欠拟合与过拟合
第十五章:机器学习案例:幸福感指数预测

一、引言

客户流失预测是客户关系管理中的核心问题之一。通过分析用户的历史数据(如业务信息、人口统计信息等),我们可以构建二分类模型预测客户是否会流失。本文将展示一个客户流失预测项目的技术流程,涵盖数据清洗、特征工程、模型训练与评估、特征重要性分析等关键步骤。

  • 人口统计学信息:年龄、性别、学历、婚姻、 …
  • 业务信息:购买记录、浏览记录、 …

二、数据准备

2.1 数据来源与读取

使用信用卡客户流失数据集(信用卡客户流失数据集.csv),包含 21个特征10134条样本

import pandas as pd

# 读取数据
data = pd.read_csv("./信用卡客户流失数据集.csv")
print("数据形状:", data.shape)  # 输出 (10134, 21)
# 输出数据信息
data.info()
# 输出前5个样本
data.head()
# 输出后3个样本
data.tail(n=3)
# 抽样输出3个样本
data.sample(n=3)

data.info()输出
在这里插入图片描述
data.head()输出
在这里插入图片描述
data.tail(n=3)输出
在这里插入图片描述
data.sample(n=3)输出
在这里插入图片描述

2.2 数据清洗

  1. 去重:根据用户唯一标识 CLIENTNUM 删除重复记录。
  2. 处理缺失值:删除存在缺失值的样本。
  3. 删除无关列:移除用户ID列 CLIENTNUM
  4. 处理异常值:对于样本的某一个特征值,处理存在明显的偏大或者偏小等情况
  5. 特征裁剪:样本中的特征和标签不一定都是强相关的,删除不相关的特征列
# 只是判断是否有重复
data.duplicated(subset=["CLIENTNUM"]).sum()
# 输出5
# 删除重复记录(注意:删除多余的,保留其中一条)
data.drop_duplicates(subset=["CLIENTNUM"], inplace=True)
data.info()
# 判断是否缺失
data.isna().sum()
# 处理缺失值
data.dropna(how="any", inplace=True)
data.info()
# 删除无关列
data.drop(columns=["CLIENTNUM"], inplace=True)
data.info()
print("清洗后数据形状:", data.shape)  # 输出 (10127, 20)

判断是否缺失data.isna().sum()输出
在这里插入图片描述
特征裁剪
样本中的特征和标签不一定都是强相关的,能够影响标签的取值,可能只有一部分特征,甚至有一部分特征对于训练模型还存在相反的影响,训练模型之前可以分析出哪些特征和标签列相关性高,把相关性低的特征删除,不参与模型的训练。

根据标签与特征的相关性,筛选出相关性高的特征去参与模型的训练。

什么是特征的相关性?
相关系数是一种统计指标,用于衡量两个变量之间的线性关系强度和方向。通过 data.corr() 方法,可以得到一个相关系数矩阵,矩阵中的每个元素表示两列数据之间的相关性。
相关系数通常使用皮尔逊相关系数(Pearson correlation coefficient)来计算,其值范围从 -1 到 1:

  • 1:完全正相关,表示两个变量的变化方向完全一致,一个变量增加时,另一个变量也增加。
  • 0:无相关,表示两个变量之间没有线性关系。
  • -1:完全负相关,表示两个变量的变化方向完全相反,一个变量增加时,另一个变量减少。

假设我们有一个包含多个特征的数据框 data,可以通过以下步骤计算相关系数矩阵:
在这里插入图片描述
上述代码将输出一个相关系数矩阵:
在这里插入图片描述

  • A和B: 相关系数为1,表示A和B完全正相关。
  • A和C:相关系数为-1,表示A和C完全负相关。
  • A和D:相关系数为0,表示A和D无相关。
  • D和D:相关系数为1,表示D和D完全相关(自身相关)。

2.3 数据预处理

data.shape
# 输出(10127, 20)
data.info()

检查每一个字段是否存在异常值,同时将分类变量(如性别、教育水平)转换为数值形式,将每个类别映射为唯一整数。

"""
    1, Attrition_Flag
"""
# 判断有多少个离散的状态
data["Attrition_Flag"].unique()
# 构建 状态字典
attrition_dict = {attrition: idx for idx, attrition in enumerate(data["Attrition_Flag"].unique())}
# 更新对应的列
data["Attrition_Flag"] = data["Attrition_Flag"].apply(func=lambda ele: attrition_dict[ele])
idx2label = {idx: attrition for attrition, idx in attrition_dict.items()}

"""
    2, Customer_Age
    | 按位或
    & 按位与
    ! 按位取反
"""
# 异常判断
((data["Customer_Age"] < 0) | (data["Customer_Age"] > 150)).sum()
# 输出0
"""
    3, Gender
"""
# 判断有多少个离散的状态
data["Gender"].unique()
# 构建 状态字典
gender_dict = {gender: idx for idx, gender in enumerate(data["Gender"].unique())}
# 更新对应的列
data["Gender"] = data["Gender"].apply(func=lambda ele: gender_dict[ele])
"""
    4, Dependent_count
"""
data["Dependent_count"].unique()
# 输出array([3, 5, 4, 2, 0, 1], dtype=int64)
"""
    5, Education_Level
"""
data["Education_Level"].unique()
# 构建 状态字典
education_dict = {education: idx for idx, education in enumerate(data["Education_Level"].unique())}
# 更新对应的列
data["Education_Level"] = data["Education_Level"].apply(func=lambda ele: education_dict[ele])
"""
    6, Marital_Status
"""
data["Marital_Status"].unique()
# 构建 状态字典
marital_dict = {marital: idx for idx, marital in enumerate(data["Marital_Status"].unique())}
# 更新对应的列
data["Marital_Status"] = data["Marital_Status"].apply(func=lambda ele: marital_dict[ele])
"""
    7, Income_Category
"""
data["Income_Category"].unique()
# 构建 状态字典
income_dict = {income: idx for idx, income in enumerate(data["Income_Category"].unique())}
# 更新对应的列
data["Income_Category"] = data["Income_Category"].apply(func=lambda ele: income_dict[ele])
"""
    8,Card_Category
"""
data["Card_Category"].unique()
# 构建 状态字典
card_dict = {card: idx for idx, card in enumerate(data["Card_Category"].unique())}
# 更新对应的列
data["Card_Category"] = data["Card_Category"].apply(func=lambda ele: card_dict[ele])
"""
    9, Months_on_book
"""
data["Months_on_book"].unique()
# 输出array([39, 44, 36, 34, 21, 46, 27, 31, 54, 30, 48, 37, 56, 42, 49, 33, 28,
       38, 41, 43, 45, 52, 40, 50, 35, 47, 32, 20, 29, 25, 53, 24, 55, 23,
       22, 26, 13, 51, 19, 15, 17, 18, 16, 14], dtype=int64)
"""
    10, Total_Relationship_Count
"""
data["Total_Relationship_Count"].unique()
# 输出array([5, 6, 4, 3, 2, 1], dtype=int64)
"""
    11, Months_Inactive_12_mon
"""
data["Months_Inactive_12_mon"].unique()
# 输出array([1, 4, 2, 3, 6, 0, 5], dtype=int64)
"""
    12, Contacts_Count_12_mon
"""
data["Contacts_Count_12_mon"].unique()
# 输出array([3, 2, 0, 1, 4, 5, 6], dtype=int64)
"""
    13, Credit_Limit
"""
data["Credit_Limit"].min(), data["Credit_Limit"].max(), data["Credit_Limit"].mean()
# 输出(1438.3, 34516.0, 8631.953698034955)
"""
    14, Total_Revolving_Bal
"""
data["Total_Revolving_Bal"].min(), data["Total_Revolving_Bal"].max(), data["Total_Revolving_Bal"].mean()
# 输出(0, 2517, 1162.8140614199665)
"""
    15, Avg_Open_To_Buy
"""
data["Avg_Open_To_Buy"].min(), data["Avg_Open_To_Buy"].max(), data["Avg_Open_To_Buy"].mean()
# 输出(3.0, 34516.0, 7469.139636614989)

"""
    16, Total_Amt_Chng_Q4_Q1
"""
data["Total_Amt_Chng_Q4_Q1"].min(), data["Total_Amt_Chng_Q4_Q1"].max(), data["Total_Amt_Chng_Q4_Q1"].mean()
# 输出(0.0, 3.397, 0.7599406536980349)
"""
    17, Total_Trans_Amt
"""
data["Total_Trans_Amt"].min(), data["Total_Trans_Amt"].max(), data["Total_Trans_Amt"].mean()
# 输出(510, 18484, 4404.086303939963)
"""
    18, Total_Trans_Ct
"""
data["Total_Trans_Ct"].min(), data["Total_Trans_Ct"].max(), data["Total_Trans_Ct"].mean()
# 输出(10, 139, 64.85869457884863)
"""
    19, Total_Ct_Chng_Q4_Q1
"""
data["Total_Ct_Chng_Q4_Q1"].min(), data["Total_Ct_Chng_Q4_Q1"].max(), data["Total_Ct_Chng_Q4_Q1"].mean()
#输出(0.0, 0.999, 0.2748935518909845)
"""
    20,Avg_Utilization_Ratio
"""
data["Avg_Utilization_Ratio"].min(), data["Avg_Utilization_Ratio"].max(), data["Avg_Utilization_Ratio"].mean()
data.sample(n=3)

2.4 数据集拆分

from sklearn.model_selection import train_test_split

# 划分训练集和测试集
y = data["Attrition_Flag"].to_numpy()
X = data.drop(columns=["Attrition_Flag"]).to_numpy()
X.shape, y.shape
#输出((10127, 19), (10127,))
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)

2.5 数据标准化处理

从训练集中抽取预处理参数 mu 和 sigma

# 计算训练集 X_train 每个特征的平均值。
# axis=0 表示沿着垂直方向计算平均值,即对每一列(特征)分别求平均值。
# 结果是一个包含每个特征平均值的一维数组 mu。
mu = X_train.mean(axis=0)
# 计算训练集 X_train 每个特征的标准差。
# 同样地,axis=0 表示沿着垂直方向计算标准差,即对每一列(特征)分别求标准差。
# 结果是一个包含每个特征标准差的一维数组 sigma。
sigma = X_train.std(axis=0)
# (X_train - mu):从训练集 X_train 中减去每个特征的平均值 mu。这一步操作将每个特征的均值调整为0。
# / sigma:然后除以每个特征的标准差 sigma。这一步操作将每个特征的方差调整为1。
# 综合这两步操作,将训练集中的每个特征标准化到零均值和单位方差的形式。标准化后的数据更有利于许多机器学习算法的学习过程。
X_train = (X_train - mu) / sigma
# 对测试集 X_test 执行相同的标准化操作,
X_test = (X_test - mu) / sigma

在计算测试集的标准化时,使用的均值 mu 和标准差 sigma 是从训练集计算得到的,而不是直接从测试集中计算。这是因为我们希望测试集的数据转换与训练集保持一致,避免数据泄漏的问题。

上述步骤,完成对训练集和测试集的标准化处理:
均值归零:通过减去每个特征的平均值,使得每个特征的均值变为0。
方差归一:通过除以每个特征的标准差,使得每个特征的方差变为1。

这样做有几个好处:

  • 提高了数值稳定性,特别是在使用梯度下降等优化算法时。
  • 确保不同尺度的特征对模型的影响是一致的,防止具有较大数值范围的特征主导那些数值范围较小的特征。
  • 加速了模型的收敛速度,特别是对于基于距离或梯度的方法。

注意,在实际应用中,总是使用训练集的统计量(如均值和标准差)来标准化测试集,这是为了避免信息泄露并确保模型泛化能力的有效评估。

也可以使用另外一种方式做标准化处理,使用StandardScale 进行标准化处理

from sklearn.preprocessing import StandardScaler
transfer = StandardScaler()
X_train_std_scl = transfer.fit_transform(X_train)
X_train_std_scl
X_test_std_scl = transfer.transform(X_test)
X_test_std_scl

关于数据标准化更多信息,可以看看这篇理解数据标准化处理

2.6 数据保存

  • 保存特征编码时的状态字典
  • 保存特征的各种异常判断
  • 保存标准化处理的参数
import joblib
state_dict = [idx2label, gender_dict, education_dict, marital_dict, income_dict, card_dict, mu, sigma]
data = [X_train, y_train, X_test, y_test]
joblib.dump(value=[state_dict, data], filename="all_data.lxh")

三、模型训练与评估

数据加载

import joblib
[state_dict, data] = joblib.load(filename="all_data.lxh")
idx2label, gender_dict, education_dict, marital_dict, income_dict, card_dict, mu, sigma = state_dict
X_train, y_train, X_test, y_test = data

尝试多种算法

KNN朴素贝叶斯决策树,观察不同算法的的评价指标值,最后采用一种最好的算法

1. K近邻算法(KNN)

通过交叉验证选择最佳邻居数 n_neighbors,最终在测试集上准确率达 90.8%

from sklearn.neighbors import KNeighborsClassifier
# 初始化相关参数
best_n_neighbor = 0
best_acc = 0
best_model = None
# 通过遍历不同的邻居数(n_neighbors),寻找最佳的K值
for n_neighbor in range(3, 31, 1):
    knn = KNeighborsClassifier(n_neighbors=n_neighbor)
    knn.fit(X=X_train, y=y_train)
    y_pred = knn.predict(X=X_test)
    acc = (y_pred == y_test).mean()
    if acc > best_acc:
        best_n_neighbor = n_neighbor
        best_acc = acc
        best_model = knn
        print(f"找到一个更好的模型: {best_n_neighbor}, {best_acc}")
# 保存模型
joblib.dump(value=[best_n_neighbor, best_acc, best_model], filename="best_knn.lxh")

输出结果

找到一个更好的模型: 3, 0.9002961500493584
找到一个更好的模型: 5, 0.9042448173741362
找到一个更好的模型: 7, 0.9067127344521224
找到一个更好的模型: 9, 0.9081934846989141

best_n_neighbor:记录当前找到的最佳邻居数量
best_acc:记录当前找到的最佳准确率
best_model:保存当前性能最好的模型实例

代码解释

  • for n_neighbor in range(3, 31, 1):
    使用一个循环来遍历不同的邻居数量 n_neighbor,范围是从3到30(包括30)。选择这个范围是因为通常较小的K值(如1或2)可能会导致过拟合,而较大的K值可能过于泛化。
  • knn = KNeighborsClassifier(n_neighbors=n_neighbor)
    创建一个K近邻分类器实例 knn,并设置邻居数量为当前循环中的 n_neighbor 值。
  • knn.fit(X=X_train, y=y_train)
    使用训练数据 X_train 和对应的标签 y_train 来训练K近邻分类器。注意,尽管KNN是一种基于实例的学习方法,不需要显式的“训练”过程,但这里调用fit方法主要是为了存储训练数据以便后续预测使用。
  • y_pred = knn.predict(X=X_test)
    对测试集 X_test 进行预测,得到预测结果 y_pred。
  • acc = (y_pred == y_test).mean()
    计算预测结果 y_pred 和真实标签 y_test 的平均准确率。(y_pred == y_test) 会生成一个布尔数组,表示每个预测是否正确,.mean() 将这些布尔值(True=1, False=0)的平均值作为准确率。
  • if acc > best_acc:
    如果当前模型的准确率 acc 比之前记录的最佳准确率 best_acc 更高,则更新最佳邻居数量 best_n_neighbor、最佳准确率 best_acc 和最佳模型 best_model。

2. 高斯朴素贝叶斯

假设特征服从正态分布,测试集准确率为 88.2%

from sklearn.naive_bayes import GaussianNB
gnb = GaussianNB()
gnb.fit(X=X_train, y=y_train)
y_pred = gnb.predict(X=X_test)
acc = (y_pred == y_test).mean()
acc

输出0.8820335636722606

3. 决策树

决策树模型表现最佳,测试集准确率达 93.4%

from sklearn.tree import DecisionTreeClassifier
dtc = DecisionTreeClassifier()
dtc.fit(X=X_train, y=y_train)
y_pred = dtc.predict(X=X_test)
acc = (y_pred == y_test).mean()
acc
joblib.dump(value=dtc, filename="dtc.lxh")

输出0.9313919052319842

四、推理过程

选择2个客户的数据,赋值给变量x和和x1,写一个predict方法,使用上面训练准确率最好的决策树模型,推理x和和x1所代表的客户,是否会成为流失客户,

x = "787584108,55,M,3,Unknown,Married,$80K - $120K,Blue,47,4,2,3,3436,2016,1420,0.901,1097,33,0.833,0.587"
x1 = "719808558,55,F,2,Graduate,Married,Less than $40K,Blue,43,2,4,3,1438.3,0,1438.3,0.707,886,27,0.421,0"
def predict(x):
    """
        推理函数
    """
    import numpy as np
    # print(f"原始输入:{x}")
    x = x.split(",")[1:]
    # print(f"切分之后:{x}")
    temp = []
    # 1, age
    temp.append(float(x[0]))
    # 2, gender
    temp.append(gender_dict[x[1]])
    # 3, 
    temp.append(float(x[2]))
    # 4, 
    temp.append(education_dict[x[3]])
    # 5, 
    temp.append(marital_dict[x[4]])
    # 6,
    temp.append(income_dict[x[5]])
    # 7, 
    temp.append(card_dict[x[6]])
    # 8 - 19
    temp.extend([float(ele) for ele in x[7: ]])
    # print(f"编码之后:{temp}")
    # 标准化之后
    x = np.array(temp)
    x = (x - mu) / sigma
    # print(f"标准化之后:{x}")
    # 模型推理
    y_pred = dtc.predict(X=[x])
    # print(f"推理结果:{y_pred}")
    # 结果解析
    final_result = idx2label[y_pred[0]]
    # print(f"最终结果:{final_result}")
    return final_result   

predict(x=x1)

输出’Attrited Customer’

五、特征重要性分析

通过决策树的 feature_importances_ 属性评估特征重要性,筛选关键特征优化模型。

# 获取特征重要性排序
import joblib
_, data = joblib.load(filename="./all_data.lxh")
X_train, y_train, X_test, y_test = data
X_train.shape
#输出(8101, 19)
y_train.shape
#输出(8101,)
from sklearn.tree import DecisionTreeClassifier
dtc = DecisionTreeClassifier()
dtc.fit(X=X_train, y=y_train)
dtc.feature_importances_.sort()
feature_importances_ = dtc.feature_importances_
feature_importances_.sort()
feature_importances_
#输出array([0.00140957, 0.00185934, 0.00453397, 0.00491808, 0.00639797,
       0.00720753, 0.00795253, 0.01206429, 0.01775464, 0.02035463,
       0.02083363, 0.02269973, 0.0378027 , 0.04347222, 0.07855794,
       0.09494261, 0.13249454, 0.19180965, 0.29293443])

六、分类问题的评价

准确率 accuracy:

  • 计算公式:预测对了的个数 / 总的测试个数
  • 样本均衡时,可靠!不同类别的样本,数量上不会相差太多!
  • 样本不均衡时,这个指标有欺骗性!

数据中留存客户占比 83.7%,流失客户仅 16.3%。此时需结合 召回率(Recall)精确率(Precision) 综合评估模型。

y_pred = dtc.predict(X=X_test)
(y_test == y_pred).mean()
#输出0.9343534057255676
from sklearn.metrics import accuracy_score
accuracy_score(y_true=y_test, y_pred=y_pred)
#输出0.9343534057255676
# 样本均衡问题
(y_train == 0).mean()
#输出0.8373040365386989
(y_train == 1).mean()
#输出0.16269596346130108
from sklearn.metrics import accuracy_score
from sklearn.metrics import recall_score
from sklearn.metrics import precision_score
from sklearn.metrics import f1_score
# 准确率为0.934,意味着大约93.4%的样本被正确分类。
accuracy_score(y_true=y_test, y_pred=y_pred, )
# 输出0.9343534057255676
# average=None:对于多分类问题,此参数指定不对各个类别的召回率进行平均处理,而是分别计算每个类别的召回率。
# recall_score:返回一个数组,其中每个元素对应一个类别的召回率。这里的输出表明有两个类别,第一个类别的召回率为0.961,第二个类别的召回率为0.786。
recall_score(y_true=y_test, y_pred=y_pred, average=None)
# 输出array([0.96097845, 0.78640777])
# average=None:同样地,这里不对各个类别的精确率进行平均处理。
# precision_score:返回一个数组,表示每个类别的精确率。在这个例子中,第一个类别的精确率为0.962,第二个类别的精确率为0.784。
precision_score(y_true=y_test, y_pred=y_pred, average=None)
# 输出array([0.96153846, 0.78387097])
# average=None:对于多分类问题,不进行平均处理,而是分别计算每个类别的F1分数。
f1_score(y_true=y_test, y_pred=y_pred, average=None)
# 输出array([0.96125837, 0.78513732])

计算模型在测试集上的准确率(accuracy)、召回率(recall)、精确率(precision)和F1分数(F1 score)
accuracy_score:用于计算分类模型的准确率,即正确预测的样本数占总样本数的比例。
recall_score:用于计算分类模型的召回率,即真实为正类的样本中被正确识别出的比例。
precision_score:用于计算分类模型的精确率,即预测为正类的样本中实际为正类的比例。
f1_score:用于计算分类模型的F1分数,它是精确率和召回率的调和平均数,提供了一个平衡两者的方法。

这段代码通过不同的评估指标来全面评价一个分类模型的性能:
准确率:整体上,模型有93.4%的预测是正确的。
召回率:对于不同类别,模型能够正确识别出的实际正例比例分别为96.1%和78.6%。
精确率:当模型预测某一样本属于某个类别时,其确实属于该类别的概率分别为96.2%和78.4%。
F1分数:提供了精确率和召回率的综合评价,适用于需要平衡这两者的场景。

七、特征筛选

干掉一部分不重要的特征,保留一部分重要的特征,剩下的特征还是原来的一部分

X_train.shape
# 输出(8101, 19)
dtc.feature_importances_
#输出array([0.0378027 , 0.00140957, 0.00491808, 0.00453397, 0.00720753,
       0.00795253, 0.00185934, 0.01206429, 0.09494261, 0.02269973,
       0.02083363, 0.01775464, 0.19180965, 0.02035463, 0.04347222,
       0.13249454, 0.29293443, 0.07855794, 0.00639797])
"""
    抽取出 5 个最重要的特征,然后测试准确率
"""
# dtc.feature_importances_:获取训练好的决策树模型 dtc 中每个特征的重要性分数。这些分数表示各个特征在模型中的相对重要性。
feature_importances_ = dtc.feature_importances_
# feature_importances_.argsort()[::-1]:首先使用 argsort() 对特征重要性进行升序排序,然后通过 [::-1] 反转得到降序排序的索引数组。
# [:5]:从排序后的索引中选取前5个最重要的特征索引。在这个例子中,输出的是 [16, 12, 15, 8, 17],即第17、13、16、9和18个特征(注意索引从0开始)。
selected_feature_idxes = feature_importances_.argsort()[::-1][:5]
selected_feature_idxes
# 输出array([16, 12, 15,  8, 17], dtype=int64)
# 根据选定的重要特征索引,从原始训练集和测试集中提取相应的特征列,形成新的训练集和测试集。
X_train1 = X_train[:, selected_feature_idxes]
X_test1 = X_test[:, selected_feature_idxes]
dtc1 = DecisionTreeClassifier()
# 使用新构建的训练集 X_train1 和对应的标签 y_train 来训练这个新的决策树模型。
dtc1.fit(X=X_train1, y=y_train)
y_pred1 = dtc1.predict(X=X_test1)
# 计算预测结果的准确率
acc1 = (y_pred1 == y_test).mean()
acc1
# 输出0.9348469891411648
# 遍历不同数量的重要特征进行评估
# 遍历从1到19的不同特征数量。
for num_features in range(1, 20, 1):
	# 对于每次迭代,根据当前的 num_features 值选择相应数量的最重要特征。
    selected_feature_idxes = feature_importances_.argsort()[::-1][:num_features]
    X_train1 = X_train[:, selected_feature_idxes]
    X_test1 = X_test[:, selected_feature_idxes]
    # 使用选定的特征索引来构建新的训练集和测试集。
    dtc1.fit(X=X_train1, y=y_train)
    y_pred1 = dtc1.predict(X=X_test1)
    acc1 = (y_pred1 == y_test).mean()
    print(num_features, acc1)

输出

1 0.866238894373149
2 0.8845014807502468
3 0.9072063178677197
4 0.930898321816387
5 0.9353405725567621
6 0.9383020730503455
7 0.937314906219151
8 0.9363277393879565
9 0.9318854886475815
10 0.9358341559723593
11 0.9313919052319842
12 0.9264560710760118
13 0.9333662388943732
14 0.9338598223099703
15 0.9299111549851925
16 0.930898321816387
17 0.930898321816387
18 0.9338598223099703
19 0.930898321816387

这段代码的主要目的是通过特征选择来优化决策树模型的性能。具体步骤包括:

  • 根据特征重要性选择最重要的特征。
  • 使用选定的特征重新训练模型,并评估其性能。
  • 通过循环尝试不同数量的重要特征,找到使模型性能最佳的特征数量。

九、结论

  1. 决策树模型在客户流失预测任务中表现最佳,准确率达 93.4%
  2. 特征重要性分析显示,信用卡使用率(Avg_Utilization_Ratio)和交易金额(Total_Trans_Amt)是关键特征。
  3. 针对样本不均衡问题,需结合召回率和精确率评估模型,避免单一准确率的误导。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值