银行客户流失预测分析

客户流失是银行业面临的重大挑战,获取新客户的成本往往是保留现有客户的5倍。本文将通过Python实现一个完整的客户流失预测分析系统,帮助银行识别高风险客户并采取针对性措施。kaggle数据集

一、环境准备与库导入

首先,我们需要导入数据分析和机器学习所需的各类库。这些库将帮助我们完成数据加载、预处理、可视化和模型构建等任务。

python 复制代码
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score, confusion_matrix, classification_report, roc_curve
import warnings
warnings.filterwarnings('ignore')
plt.style.use('seaborn-whitegrid')

代码原理解释

  • 通过pandasnumpy提供数据结构和数值计算支持
  • 使用matplotlibseaborn实现数据可视化
  • sklearn导入机器学习相关模块,包括数据集拆分、特征预处理、模型和评估指标
  • 设置忽略警告信息以保持输出整洁,并使用seaborn-whitegrid风格美化图表

二、数据集介绍

本分析使用的客户流失数据集包含10000条客户记录,涵盖客户基本信息、账户特征和行为数据等维度。各字段含义如下:

2.1 标识信息

字段名称 中文描述 对流失的影响
RowNumber 记录编号 无影响
CustomerId 客户ID 无影响
Surname 客户姓氏 无影响

2.2 客户特征

字段名称 中文描述 对流失的影响
CreditScore 信用评分 评分越高,流失可能性越低
Geography 所在地区 地区差异会影响流失率
Gender 性别 需要探索其与流失的关系
Age 年龄 年长客户更不易流失
Tenure 账户年限 年限越长,客户忠诚度越高

2.3 账户信息

字段名称 中文描述 对流失的影响
Balance 账户余额 余额越高,流失可能性越低
NumOfProducts 产品数量 购买产品越多,客户粘性越高
HasCrCard 是否有信用卡 有信用卡的客户更不易流失
IsActiveMember 是否活跃会员 活跃会员流失率更低
EstimatedSalary 估计薪资 薪资越低,流失可能性越高

2.4 服务质量与反馈

字段名称 中文描述 对流失的影响
Complain 是否有投诉 有投诉客户流失风险高
Satisfaction Score 满意度评分 评分低的客户易流失
Card Type 卡片类型 影响客户使用体验
Points Earned 积分获取 积分越多,客户忠诚度可能越高

2.5 目标变量

字段名称 中文描述 说明
Exited 是否流失 1=流失,0=未流失

数据集特点:包含18个特征字段,既有数值型也有分类型变量,目标变量为二分类(是否流失)。数据无缺失值,可直接用于建模分析。

三、数据加载与初步探索

了解数据集结构后,我们开始加载客户数据并进行初步探索,验证数据质量。

python 复制代码
# 加载数据
df = pd.read_csv('Customer-Churn-Records.csv')

# 查看数据基本信息
print(f"数据集形状: {df.shape}")
print("\n数据集前5行:\n")
print(df.head())
print("\n数据集统计信息:\n")
print(df.describe())
print("\n数据集缺失值情况:\n")
print(df.isnull().sum())

代码原理解释

  • 使用pd.read_csv()读取CSV格式的客户数据
  • 通过df.shape查看数据集的行数和列数,了解数据规模
  • 利用df.head()显示前5行数据,初步了解数据结构和内容
  • 使用df.describe()获取数值型特征的统计信息,包括均值、标准差、最值等
  • 通过df.isnull().sum()检查各列缺失值情况,因为缺失值会影响模型性能,需要提前处理

三、数据预处理

因为原始数据可能包含噪声、冗余信息或格式不统一的问题,所以在建模前需要进行预处理,确保数据质量。

python 复制代码
# 删除不需要的列
df = df.drop(['RowNumber', 'CustomerId', 'Surname'], axis=1)

# 定义特征和目标变量
X = df.drop('Exited', axis=1)
y = df['Exited']

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)

# 定义特征预处理管道
numeric_features = ['CreditScore', 'Age', 'Tenure', 'Balance', 'NumOfProducts', 'EstimatedSalary', 'Point Earned']
categorical_features = ['Geography', 'Gender', 'HasCrCard', 'IsActiveMember', 'Complain', 'Satisfaction Score', 'Card Type']

preprocessor = ColumnTransformer(
    transformers=[
        ('num', StandardScaler(), numeric_features),
        ('cat', OneHotEncoder(drop='first', sparse_output=False), categorical_features)
    ])

代码原理解释

  • 首先删除无关特征(RowNumber、CustomerId、Surname),因为这些个人标识信息对预测客户流失没有实际意义
  • 将数据集分为特征变量(X)和目标变量(y),其中目标变量是'Exited'(是否流失)
  • 使用train_test_split将数据按8:2比例划分为训练集和测试集,设置stratify=y确保分层抽样,保持与原始数据相似的流失率分布
  • 定义数值型和分类型特征列表,因为这两类特征需要不同的预处理方式
  • 创建预处理管道:通过StandardScaler对数值特征进行标准化(均值为0,标准差为1),通过OneHotEncoder对分类特征进行独热编码,drop='first'参数用于避免多重共线性问题

四、探索性数据分析

通过探索性数据分析,我们可以发现数据中的模式和关系,为后续建模提供指导。

python 复制代码
# 设置图形大小
plt.figure(figsize=(18, 12))

# 1. 客户流失率总体情况
plt.subplot(2, 3, 1)
churn_rate = df['Exited'].value_counts(normalize=True)
ax = sns.barplot(x=churn_rate.index, y=churn_rate.values)
ax.set_title('客户流失率总体情况', fontsize=12)
ax.set_xlabel('是否流失 (1=流失)')
ax.set_ylabel('比例')
for p in ax.patches:
    ax.annotate(f'{p.get_height():.2%}', (p.get_x() + p.get_width() / 2., p.get_height()),
                ha='center', va='bottom', fontsize=10)

# 2. 不同地区的客户流失率
plt.subplot(2, 3, 2)
geo_churn = df.groupby('Geography')['Exited'].mean().sort_values(ascending=False)
ax = sns.barplot(x=geo_churn.index, y=geo_churn.values)
ax.set_title('不同地区的客户流失率', fontsize=12)
ax.set_xlabel('地区')
ax.set_ylabel('流失率')
for p in ax.patches:
    ax.annotate(f'{p.get_height():.2%}', (p.get_x() + p.get_width() / 2., p.get_height()),
                ha='center', va='bottom', fontsize=10)

# 3. 不同性别的客户流失率
plt.subplot(2, 3, 3)
gender_churn = df.groupby('Gender')['Exited'].mean().sort_values(ascending=False)
ax = sns.barplot(x=gender_churn.index, y=gender_churn.values)
ax.set_title('不同性别的客户流失率', fontsize=12)
ax.set_xlabel('性别')
ax.set_ylabel('流失率')
for p in ax.patches:
    ax.annotate(f'{p.get_height():.2%}', (p.get_x() + p.get_width() / 2., p.get_height()),
                ha='center', va='bottom', fontsize=10)

# 4. 年龄与流失率的关系
plt.subplot(2, 3, 4)
df['AgeGroup'] = pd.cut(df['Age'], bins=[18, 30, 40, 50, 60, 100], labels=['18-30', '31-40', '41-50', '51-60', '60+'])
age_churn = df.groupby('AgeGroup')['Exited'].mean().sort_index()
ax = sns.barplot(x=age_churn.index, y=age_churn.values)
ax.set_title('不同年龄段的客户流失率', fontsize=12)
ax.set_xlabel('年龄段')
ax.set_ylabel('流失率')
for p in ax.patches:
    ax.annotate(f'{p.get_height():.2%}', (p.get_x() + p.get_width() / 2., p.get_height()),
                ha='center', va='bottom', fontsize=10)

# 5. 账户余额与流失率的关系
plt.subplot(2, 3, 5)
df['BalanceGroup'] = pd.cut(df['Balance'], bins=[0, 50000, 100000, 200000, 300000], labels=['0-5万', '5-10万', '10-20万', '20万+'])
balance_churn = df.groupby('BalanceGroup')['Exited'].mean().sort_index()
ax = sns.barplot(x=balance_churn.index, y=balance_churn.values)
ax.set_title('不同账户余额的客户流失率', fontsize=12)
ax.set_xlabel('账户余额')
ax.set_ylabel('流失率')
for p in ax.patches:
    ax.annotate(f'{p.get_height():.2%}', (p.get_x() + p.get_width() / 2., p.get_height()),
                ha='center', va='bottom', fontsize=10)

# 6. 活跃会员与流失率的关系
plt.subplot(2, 3, 6)
active_churn = df.groupby('IsActiveMember')['Exited'].mean().sort_values(ascending=False)
ax = sns.barplot(x=active_churn.index, y=active_churn.values)
ax.set_title('活跃会员与非活跃会员流失率对比', fontsize=12)
ax.set_xlabel('是否活跃会员 (1=活跃)')
ax.set_ylabel('流失率')
ax.set_xticklabels(['活跃', '非活跃'])
for p in ax.patches:
    ax.annotate(f'{p.get_height():.2%}', (p.get_x() + p.get_width() / 2., p.get_height()),
                ha='center', va='bottom', fontsize=10)

plt.tight_layout()
plt.savefig('churn_eda.png', dpi=300, bbox_inches='tight')
plt.show()

# 删除临时创建的分组列
df = df.drop(['AgeGroup', 'BalanceGroup'], axis=1)

代码原理解释

  • 首先创建一个2行3列的图形布局,用于同时展示多个可视化结果
  • 通过value_counts(normalize=True)计算总体流失率,使用柱状图可视化
  • 利用groupbymean()计算不同分组(地区、性别、年龄段等)的流失率,通过柱状图比较差异
  • 使用pd.cut()将连续变量(年龄、余额)分箱,转换为类别变量以便分析
  • 在每个柱状图上添加数值标签,使结果更直观
  • 通过plt.tight_layout()调整布局,避免图形重叠
  • 使用plt.savefig()保存图形,并通过plt.show()显示图形
  • 最后删除临时创建的分组列,保持数据整洁

五、特征相关性分析

特征之间的相关性会影响模型解释性和性能,通过相关性分析可以识别多重共线性问题。

python 复制代码
# 计算特征相关性
plt.figure(figsize=(12, 10))
correlation = df[numeric_features + ['Exited']].corr()
mask = np.triu(np.ones_like(correlation, dtype=bool))
sns.heatmap(correlation, annot=True, fmt='.2f', cmap='coolwarm', mask=mask, vmin=-1, vmax=1)
plt.title('特征相关性热力图', fontsize=14)
plt.tight_layout()
plt.show()

代码原理解释

  • 首先使用corr()计算数值型特征与目标变量之间的相关系数,衡量线性关系强度
  • 创建上三角掩码(mask),用于在热力图中只显示下三角部分,避免重复信息
  • 通过sns.heatmap()绘制热力图,annot=True显示具体相关系数值,fmt='.2f'保留两位小数
  • 使用coolwarm颜色映射,红色表示正相关,蓝色表示负相关,颜色深浅表示相关程度
  • 添加标题并调整布局,使图形更易读

六、构建和训练模型

通过前面的数据分析,我们已经对数据有了深入了解,现在可以构建分类模型来预测客户流失。

python 复制代码
# 定义模型字典
models = {
    '逻辑回归': LogisticRegression(max_iter=1000, random_state=42),
    '决策树': DecisionTreeClassifier(random_state=42),
    '随机森林': RandomForestClassifier(random_state=42)
}

# 训练和评估所有模型
results = {}
print("各模型性能评估结果:\n")

for name, model in models.items():
    # 创建并训练管道
    pipeline = Pipeline([
        ('preprocessor', preprocessor),
        ('classifier', model)
    ])
    pipeline.fit(X_train, y_train)

    # 预测
    y_pred = pipeline.predict(X_test)
    y_prob = pipeline.predict_proba(X_test)[:, 1]

    # 评估
    accuracy = accuracy_score(y_test, y_pred)
    precision = precision_score(y_test, y_pred)
    recall = recall_score(y_test, y_pred)
    f1 = f1_score(y_test, y_pred)
    roc_auc = roc_auc_score(y_test, y_prob)

    # 存储结果
    results[name] = {
        '模型': pipeline,
        '准确率': accuracy,
        '精确率': precision,
        '召回率': recall,
        'F1分数': f1,
        'AUC': roc_auc
    }

    # 打印结果
    print(f"{name}模型评估结果:")
    print(f"准确率: {accuracy:.4f}")
    print(f"精确率: {precision:.4f}")
    print(f"召回率: {recall:.4f}")
    print(f"F1分数: {f1:.4f}")
    print(f"AUC: {roc_auc:.4f}\n")

    # 打印分类报告
    print(f"{name}模型分类报告:\n")
    print(classification_report(y_test, y_pred, target_names=['未流失(0)', '流失(1)']))
    print("-" * 50 + "\n")

代码原理解释

  • 定义一个包含三种常用分类算法的字典:逻辑回归、决策树和随机森林
  • 使用Pipeline将预处理和模型训练整合,确保测试数据使用训练数据的预处理参数,避免数据泄露
  • 对每个模型进行训练,并使用测试集进行预测
  • 计算多种评估指标:准确率、精确率、召回率、F1分数和AUC值,全面评估模型性能
  • 特别输出分类报告,显示流失客户(1类)的详细评估指标
  • 将模型和评估结果存储在results字典中,便于后续比较


七、特征重要性分析

对于表现最佳的随机森林模型,我们分析特征对客户流失预测的重要性,找出影响客户流失的关键因素。

python 复制代码
# 获取最佳模型(随机森林)
best_model = results['随机森林']['模型']

# 获取特征名称(需要考虑独热编码后的特征)
preprocessor = best_model.named_steps['preprocessor']
cat_features_output = preprocessor.named_transformers_['cat'].get_feature_names_out(categorical_features)
all_feature_names = list(numeric_features) + list(cat_features_output)

# 获取特征重要性
importances = best_model.named_steps['classifier'].feature_importances_
indices = np.argsort(importances)[::-1]

# 绘制特征重要性条形图
plt.figure(figsize=(12, 8))
sns.barplot(x=importances[indices], y=[all_feature_names[i] for i in indices])
plt.title('随机森林模型特征重要性', fontsize=14)
plt.xlabel('特征重要性得分')
plt.ylabel('特征名称')
plt.tight_layout()
plt.savefig('feature_importance.png', dpi=300, bbox_inches='tight')
plt.show()

# 打印特征重要性排名
print("特征重要性排名:\n")
for i, idx in enumerate(indices[:10]):
    print(f"{i+1}. {all_feature_names[idx]}: {importances[idx]:.4f}")

代码原理解释

  • 从最佳模型(随机森林)中提取特征重要性分数,这些分数表示每个特征对模型预测的贡献程度
  • 处理特征名称,特别是独热编码后的分类特征,确保图表中的特征名称清晰易懂
  • 使用条形图可视化特征重要性,直观展示哪些因素对客户流失影响最大
  • 打印前10个重要的特征,帮助业务人员理解关键影响因素

八、结论与业务建议

基于我们的分析结果,提出针对性的客户保留策略和业务建议。

python 复制代码
# 分析高风险客户群体特征
print("高风险客户群体特征分析:\n")

# 地区分析
high_risk_geo = df[df['Exited'] == 1]['Geography'].value_counts(normalize=True)
print("流失客户地区分布:\n", high_risk_geo)

# 年龄分析
df['AgeGroup'] = pd.cut(df['Age'], bins=[18, 30, 40, 50, 60, 100], labels=['18-30', '31-40', '41-50', '51-60', '60+'])
high_risk_age = df[df['Exited'] == 1]['AgeGroup'].value_counts(normalize=True)
print("\n流失客户年龄分布:\n", high_risk_age)

# 会员活跃度分析
high_risk_active = df[df['Exited'] == 1]['IsActiveMember'].value_counts(normalize=True)
print("\n流失客户会员活跃度分布:\n", high_risk_active)

# 删除临时列
df = df.drop('AgeGroup', axis=1)

# 业务建议总结
print("\n业务建议:\n")
print("1. 区域化客户保留策略:针对德国地区客户实施专项保留计划,提高该区域客户满意度")
print("2. 高龄客户关怀计划:为51岁以上客户提供专属金融产品和服务,增强忠诚度")
print("3. 会员活跃度提升计划:通过个性化服务和互动活动提高非活跃会员的参与度")
print("4. 产品优化:基于满意度分数较低的客户反馈,改进产品功能和用户体验")
print("5. 差异化定价:为高余额客户提供更有竞争力的利率和优惠条件")

代码原理解释

  • 深入分析流失客户的关键特征,包括地区、年龄和会员活跃度等维度
  • 通过比例分布量化不同群体的流失风险,为业务决策提供数据支持
  • 基于分析结果提出五项具体可行的业务建议,涵盖区域策略、客户关怀、会员管理等方面
  • 建议具有明确的针对性,可直接指导客户保留工作的实施

九、模型部署与应用

将训练好的最佳模型保存为文件,以便在生产环境中部署和应用。

python 复制代码
import joblib

# 保存最佳模型
best_model = results['随机森林']['模型']
joblib.dump(best_model, 'customer_churn_model.pkl')
print("模型已保存为customer_churn_model.pkl")

# 演示模型加载和预测
loaded_model = joblib.load('customer_churn_model.pkl')

# 使用新客户数据进行预测示例
new_customer = X_test.iloc[0:1].copy()
churn_probability = loaded_model.predict_proba(new_customer)[0, 1]

print("\n新客户流失预测示例:\n")
print(f"客户特征:\n{new_customer}\n")
print(f"流失概率: {churn_probability:.2%}")
print(f"预测结果: {'高风险流失客户' if churn_probability > 0.5 else '低风险客户'}")

代码原理解释

  • 使用joblib库保存最佳模型,便于后续部署和应用
  • 演示模型加载过程,展示如何在生产环境中使用保存的模型
  • 创建新客户预测示例,展示模型如何为实际业务提供决策支持
  • 根据流失概率设定阈值(0.5),将客户分为高风险和低风险两类,便于采取不同策略

总结

本文通过完整的数据分析流程,从数据探索到模型部署,构建了一个客户流失预测系统。主要工作包括:

  1. 数据预处理:清洗数据并构建特征工程管道
  2. 探索性分析:识别出德国客户、高龄人群和非活跃会员是高风险群体
  3. 模型构建与评估:比较三种分类算法,发现随机森林模型性能最佳(AUC=0.8639)
  4. 特征重要性分析:揭示影响客户流失的关键因素
  5. 业务建议:提出针对性的客户保留策略
  6. 模型部署:将最佳模型保存为文件,便于实际应用

通过实施本文提出的策略,银行可以有效识别高风险客户,采取针对性措施降低流失率,提高客户满意度和忠诚度。

相关推荐
闻道且行之7 分钟前
Windows|CUDA和cuDNN下载和安装,默认安装在C盘和不安装在C盘的两种方法
windows·深度学习·cuda·cudnn
我的ID配享太庙呀1 小时前
Django 科普介绍:从入门到了解其核心魅力
数据库·后端·python·mysql·django·sqlite
小蒋的技术栈记录1 小时前
网易大模型算法岗面经80道
算法
Ress Matthew2 小时前
重塑优化建模与算法设计:2025年大模型(LLM)在优化领域的应用盘点 - 2
算法
找不到、了2 小时前
Java排序算法之<插入排序>
java·算法·排序算法
@蓝莓果粒茶2 小时前
LeetCode第350题_两个数组的交集II
c++·python·学习·算法·leetcode·职场和发展·c#
设计师小聂!2 小时前
力扣热题100----------53最大子数组和
java·数据结构·算法·leetcode
生医转码,四海为家2 小时前
零基础-动手学深度学习-6.6 卷积神经网络(LeNet)
人工智能·深度学习·cnn
FinAnalyzer2 小时前
如何在 InsCodeAI 上搭建并使用 Jupyter Notebook 环境?
ide·python·jupyter
java1234_小锋2 小时前
【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts) 视频教程 - 微博文章数据可视化分析-文章分类下拉框实现
python·自然语言处理·flask