2022 年“泰迪杯”数据分析技能赛B 题银行客户忠诚度分析
完整代码请私聊 博主
一、背景
目前银行产品存在同质化现象,客户选择产品和服务的途径越来越多,对产品的忠诚度越来越低。为了提高客户对银行的忠诚度和银行营销量,商业银行迫切需要转变经营理念,从“产品销售导向”业务模式向“以客户为中心”转变,为客户带来极致体验和价值成长,形成路径依赖,进而实现价值共赢。
客户忠诚度主要体现为客户的行为和态度。客户行为主要表现为产品重复购买的频率,而客户态度主要表现为情感的倾向。为了有效挖掘客户忠诚度,需要从短期客户产品购买数据和长期客户资源信息中分析客户需求指标。其中,短期客户忠诚度分析是通过产品的购买数据,分析不同指标客户对银行产品的购买依赖度从而提供更好的销售服务;长期客户忠诚度分析则是从客户资源信息数据中挖掘客户流失因素、预测可能流失的客户,尽可能留住高价值客户。
二、数据说明
银行客户忠诚度分析数据包括短期客户产品购买数据和长期客户资源信息数据,附件数据详细情况如表 1 所示。
短期客户产品购买数据记录了往期银行营销活动中客户购买产品的信息,包含客户的基本信息、上次活动后拜访客户信息和上次活动产品购买结果等,具体的数据指标说明如表 2 所示。
长期客户资源信息数据记录了往期该银行客户流动状态及客户信息,包括客户基本信息、客户户龄与金融资产、客户活跃状态与流失情况等,具体的数据指标说明如表 3 所示。
三、目标
(1) 对客户数据进行预处理,并对字符型数据进行特征编码。
(2) 基于短期客户产品购买数据,分析不同指标客户对银行产品的购买依
赖度,并进行可视化呈现。
(3) 基于长期客户资源信息数据,分析客户流失因素,并进行可视化呈现。
(4) 依据长期客户资源信息数据的分析结果构建相关指标,对银行客户长
期忠诚度进行预测。
四、任务
任务 1 数据探索与清洗
分别对短期客户产品购买数据“short-customer-data.csv”(简称短期数据)和长期客户资源信息数据的训练集“long-customer-train.csv”(简称长期数据)进行数据探索与清洗。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from pyecharts.charts import Bar
from pyecharts.globals import ThemeType
import seaborn as sns
plt.rcParams['font.sans-serif'] = 'SimHei'
plt.rcParams['axes.unicode_minus'] = False
任务 1.1 数据探索与预处理
(1) 探索短期数据各指标数据的缺失值和“user_id”列重复值,删除缺失值、重复值所在行数据。请在报告中给出处理过程及必要结果,完整的结果保存到文件“result1_1.xlsx”中。
df = pd.read_csv('short-customer-data.csv')
df.head()
df.info()
df.describe()
df.isnull().sum()
df.dropna(inplace=True)
df.isnull().sum()
df.duplicated(subset=['user_id']).sum()
df.drop_duplicates('user_id',keep='first',inplace=True)
df.reset_index(inplace=True,drop=True)
df.to_excel('result1_1.xlsx',index=None)
df
(2) 长期数据中的客户年龄“Age”列存在数值为-1、0 和“-”的异常值,删除存在该情况的行数据;“Age”列存在空格和“岁”等异常字符,删除这些异常字符但须保留年龄数值,将处理后的数值存于“Age”列。请在报告中给出处理过程及必要结果,完整的结果保存到文件“result1_2.xlsx”中。
df2 = pd.read_csv('long-customer-train.csv')
df2
df2 = df2.loc[(df2['Age']!='-1')&(df2['Age']!='0')&(df2['Age']!='-')]
df2
CustomerId CreditScore Gender Age Tenure Balance NumOfProducts HasCrCard IsActiveMember EstimatedSalary Exited
0 15553251 713 1 52 0 185891.54 1 1 1 46369.57 1
1 15553256 619 1 41 8 0.00 3 1 1 79866.73 1
2 15553283 603 1 42 8 91611.12 1 0 0 144675.30 1
3 15553308 589 1 61 1 0.00 1 1 0 61108.56 1
4 15553387 687 1 39 2 0.00 3 0 0 188150.60 1
... ... ... ... ... ... ... ... ... ... ... ...
9295 15815628 711 1 37 8 113899.92 1 0 0 80215.20 0
9296 15815645 481 0 37 8 152303.66 2 1 1 175082.20 0
9297 15815656 541 1 39 9 100116.67 1 1 1 199808.10 1
9298 15815660 758 1 34 1 154139.45 1 1 1 60728.89 0
9299 15815690 614 1 40 3 113348.50 1 1 1 77789.01 0
9198 rows × 11 columns
def clean(x):
return int(x.replace(' ','').replace('岁',''))
df2['Age'] = df2['Age'].apply(lambda x:clean(x))
df2.to_excel('result1_2.xlsx',index=None)
CustomerId CreditScore Gender Age Tenure Balance NumOfProducts HasCrCard IsActiveMember EstimatedSalary Exited
300 15565701 698 1 39 9 161993.89 1 0 0 90212.38 0
302 15565714 601 0 47 1 64430.06 2 0 1 96517.97 0
303 15565779 627 1 30 6 57809.32 1 1 0 188258.49 0
304 15565796 745 0 48 10 96048.55 1 1 0 74510.65 0
305 15565806 532 0 38 9 0.00 2 0 0 30583.95 0
... ... ... ... ... ... ... ... ... ... ... ...
9191 15815615 681 0 36 5 141952.07 1 1 1 185144.08 0
9193 15815628 711 1 37 8 113899.92 1 0 0 80215.20 0
9194 15815645 481 0 37 8 152303.66 2 1 1 175082.20 0
9196 15815660 758 1 34 1 154139.45 1 1 1 60728.89 0
9197 15815690 614 1 40 3 113348.50 1 1 1 77789.01 0
任务 1.2
对短期数据中的字符型数据进行特征编码,如将信用违约情况{‘否’,‘是’}编码为{0,1}。请在报告中给出处理思路、过程及必要结果,完整的结
果保存到文件“result1_3.xlsx”中。
df4 = pd.read_excel('result1_1.xlsx')
df4.head()
df4.columns
for k in['job','marital','education','default','housing','loan','contact','month','day_of_week','poutcome','y']:
m = {}
for i,j in enumerate(df4[k].unique()):
m[j] = i
print(m)
df4[k] = df4[k].map(m)
df4.to_excel('result1_3.xlsx')
df4.head()
user_id age job marital education default housing loan contact month day_of_week duration poutcome y
0 BA2200001 56 0 0 0 0 0 0 0 0 0 261 0 0
1 BA2200077 37 1 0 1 0 1 0 0 0 0 226 0 0
2 BA2200004 40 2 0 0 0 0 0 0 0 0 151 0 0
3 BA2200005 56 1 0 1 0 0 1 0 0 0 307 0 0
4 BA2200007 59 2 0 2 0 0 0 0 0 0 139 0 0
任务 2 产品营销数据可视化分析
基于短期数据分析不同指标客户与购买银行产品行为的关联性,挖掘短期客户对银行的忠诚度。
任务 2.1
计算短期数据所有指标之间的相关性,绘制相关系数热力图,并在报告中对结果进行必要分析。
plt.figure(figsize=(13,10))
corr = df4.iloc[:,1:].corr()
sns.heatmap(corr,annot=True,fmt='.2f',cmap='rainbow')
plt.title('相关系数热力图')
plt.show()
任务 2.2
在同一画布中,绘制反映两种产品购买结果下不同年龄客户量占比的分组柱状图,x 轴为年龄,y 轴为占比数值,并在报告中对结果进行必要分析。
y 0 1
age
[0, 20) 0.000790 0.004149
[20, 30) 0.147241 0.201504
[30, 40) 0.454361 0.387189
[40, 50) 0.239460 0.170124
[50, 60) 0.139042 0.140560
[60, 70) 0.011358 0.052645
[70, 80) 0.005566 0.029564
[80, 90) 0.002031 0.014263
[90, 100) 0.000150 0.000000
任务 2.3
在同一画布中,绘制蓝领(blue-collar)与学生(student)的产品购买情况饼图,并设定饼图的标签,显示产品购买情况的占比。
# 将工作类型转换为字符串,便于识别
df4['job_str'] = df4['job'].map({0: 'admin.', 1: 'blue-collar', 2: 'entrepreneur',
3: 'housemaid', 4: 'management', 5: 'retired',
6: 'self-employed', 7: 'services', 8: 'student',
9: 'technician', 10: 'unemployed'})
任务 2.4
以产品购买结果为 x 轴、拜访客户的通话时长为 y 轴,绘制拜访客户的通话时长箱线图,并在报告中对结果进行必要分析。
df4
user_id age job marital education default housing loan contact month day_of_week duration poutcome y age_group job_str
0 BA2200001 56 0 0 0 0 0 0 0 0 0 261 0 0 [50, 60) admin.
1 BA2200077 37 1 0 1 0 1 0 0 0 0 226 0 0 [30, 40) blue-collar
2 BA2200004 40 2 0 0 0 0 0 0 0 0 151 0 0 [40, 50) entrepreneur
3 BA2200005 56 1 0 1 0 0 1 0 0 0 307 0 0 [50, 60) blue-collar
4 BA2200007 59 2 0 2 0 0 0 0 0 0 139 0 0 [50, 60) entrepreneur
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
30440 BA2241171 29 5 1 0 0 1 0 1 5 4 112 2 0 [20, 30) retired
30441 BA2241173 46 4 0 2 0 0 0 1 5 4 383 0 0 [40, 50) management
30442 BA2241174 56 6 0 3 0 1 0 1 5 4 189 0 0 [50, 60) self-employed
30443 BA2241175 44 3 0 2 0 0 0 1 5 4 442 0 1 [40, 50) housemaid
30444 BA2241176 74 6 0 2 0 1 0 1 5 4 239 1 0 [70, 80) self-employed
30445 rows × 16 columns
任务 3 客户流失因素可视化分析
基于长期数据分析导致银行客户流失的因素,并进行可视化呈现。
任务 3.1
在同一画布中,绘制反映两种流失情况下不同年龄客户量占比的折线图,x 轴为年龄,y 轴为占比数值。
任务 3.2
在同一画布中,绘制反映两种流失情况下客户信用资格与年龄分布的散点图,x 轴为年龄,y 轴为信用资格。
任务 3.3
构造包含各账号户龄在不同流失情况下的客户量占比透视表(详见表 4),并在同一画布中绘制反映两种流失情况的客户各账号户龄占比量的堆叠柱状图,x 轴为客户的户龄,y 轴为占比量。
任务 3.4
新老客户各资产阶段的客户流失情况分析。
(1) 按照表 5 和表 6 对账号户龄和客户金融资产进行划分,并分别进行特征编码作为新的客户特征,其中客户状态存于“Status”列,资产阶段存于“AssetStage”列,编码结果保存到文件“result3.xlsx”中。
def func3_4_1(x):
if x>=0 and x<=3:
return "新客户"
elif x>3 and x<=6:
return "稳定客户"
elif x>6:
return "老客户"
else:
return np.nan
def func3_4_2(x):
if x>=0 and x<=50000:
return "低资产"
elif x>50000 and x<=90000:
return "中下资产"
elif x>90000 and x<= 120000:
return "中上资产"
elif x> 120000:
return "高资产"
else:
return np.nan
CustomerId CreditScore Gender Age Tenure Balance NumOfProducts HasCrCard IsActiveMember EstimatedSalary Exited Status AssetStage
0 15553251 713 1 52 0 185891.54 1 1 1 46369.57 1 0.0 3.0
1 15553256 619 1 41 8 0.00 3 1 1 79866.73 1 2.0 2.0
2 15553283 603 1 42 8 91611.12 1 0 0 144675.30 1 2.0 0.0
3 15553308 589 1 61 1 0.00 1 1 0 61108.56 1 0.0 2.0
4 15553387 687 1 39 2 0.00 3 0 0 188150.60 1 0.0 2.0
... ... ... ... ... ... ... ... ... ... ... ... ... ...
9193 15815628 711 1 37 8 113899.92 1 0 0 80215.20 0 2.0 0.0
9194 15815645 481 0 37 8 152303.66 2 1 1 175082.20 0 2.0 3.0
9195 15815656 541 1 39 9 100116.67 1 1 1 199808.10 1 2.0 0.0
9196 15815660 758 1 34 1 154139.45 1 1 1 60728.89 0 0.0 3.0
9197 15815690 614 1 40 3 113348.50 1 1 1 77789.01 0 0.0 0.0
9198 rows × 13 columns
le.categories_
[array(['新客户', '稳定客户', '老客户'], dtype=object),
array(['中上资产', '中下资产', '低资产', '高资产'], dtype=object)]
data3_1.to_excel("result3.xlsx",index=None)
(2) 统计新、老客户在各资产阶段中流失的客户量,在同一画布中绘制热力图,热力图颜色的最大和最小取值设为 1300 和 100,并在报告中对结果进行必要分析。
任务 4 特征构建
基于长期数据提取影响客户流失的因素,构建与银行客户长期忠诚度相关的特征,将结果保存到文件“result4.xlsx”中。
(1) 根据表 7,构建新老客户活跃程度的特征,并将结果存于“IsActiveStatus”列
data4 = pd.read_excel("result3.xlsx")
data4
def func4_1(x):
s = x.split(" ")
if s[1] == "0":
if s[0]=="0":
return 0
elif s[0]=="1":
return 1
elif s[0]=="2":
return 2
else:
return np.nan
elif s[1] == "1":
if s[0]=="0":
return 3
elif s[0]=="1":
return 4
elif s[0]=="2":
return 5
else:
return np.nan
else:
return np.nan
data4["IsActiveStatus"] = data4["Status"].apply(lambda x:str(x)+" ") + data4["IsActiveMember"].apply(str)
data4["IsActiveStatus"] = data4["IsActiveStatus"].apply(func4_1)
(2) 根据表 8,构建不同金融资产客户活跃程度的特征,并将结果存于“IsActiveAssetStage”列。
#['中上资产', '中下资产', '低资产', '高资产']
def func4_2(x):
s = x.split(" ")
if s[1] == "0":
if s[0]=="2":
return 0
elif s[0]=="1":
return 1
elif s[0]=="0":
return 2
elif s[0]=="3":
return 3
else:
return np.nan
elif s[1] == "1":
if s[0]=="2":
return 6
elif s[0]=="1":
return 7
elif s[0]=="0":
return 8
elif s[0]=="3":
return 9
else:
return np.nan
else:
return np.nan
data4["IsActiveAssetStage"] = data4["AssetStage"].apply(lambda x:str(x)+" ") + data4["IsActiveMember"].apply(str)
data4["IsActiveAssetStage"] = data4["IsActiveAssetStage"].apply(func4_2)
(3) 根据表 9,构建不同金融资产信用卡持有状态的特征,并将结果存于“CrCardAssetStage”列。
def func4_3(x):
s = x.split(" ")
if s[1] == "0":
if s[0]=="2":
return 0
elif s[0]=="1":
return 2
elif s[0]=="0":
return 5
elif s[0]=="3":
return 5
else:
return np.nan
elif s[1] == "1":
if s[0]=="2":
return 6
elif s[0]=="1":
return 7
elif s[0]=="0":
return 9
elif s[0]=="3":
return 9
else:
return np.nan
else:
return np.nan
data4["CrCardAssetStage"] = data4["AssetStage"].apply(lambda x:str(x)+" ") + data4["HasCrCard"].apply(str)
data4["CrCardAssetStage"] = data4["CrCardAssetStage"].apply(func4_3)
data4.to_excel("result4.xlsx",index=None)
data4
CustomerId CreditScore Gender Age Tenure Balance NumOfProducts HasCrCard IsActiveMember EstimatedSalary Exited Status AssetStage IsActiveStatus IsActiveAssetStage CrCardAssetStage
0 15553251 713 1 52 0 185891.54 1 1 1 46369.57 1 0 3 3 9 9
1 15553256 619 1 41 8 0.00 3 1 1 79866.73 1 2 2 5 6 6
2 15553283 603 1 42 8 91611.12 1 0 0 144675.30 1 2 0 2 2 5
3 15553308 589 1 61 1 0.00 1 1 0 61108.56 1 0 2 0 0 6
4 15553387 687 1 39 2 0.00 3 0 0 188150.60 1 0 2 0 0 0
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
9193 15815628 711 1 37 8 113899.92 1 0 0 80215.20 0 2 0 2 2 5
9194 15815645 481 0 37 8 152303.66 2 1 1 175082.20 0 2 3 5 9 9
9195 15815656 541 1 39 9 100116.67 1 1 1 199808.10 1 2 0 5 8 9
9196 15815660 758 1 34 1 154139.45 1 1 1 60728.89 0 0 3 3 9 9
9197 15815690 614 1 40 3 113348.50 1 1 1 77789.01 0 0 0 3 8 9
9198 rows × 16 columns
任务 5 银行客户长期忠诚度预测建模
# pip install --upgrade scikit-learn
长期数据存在“Exited”特征分布不均衡、各项数值分布跨度大等现象。体现为:未流失客户量是已流失客户量的 3 倍以上;客户信用资格最大数值达到 25万,而客户活动状态则为 0 和 1 等。考虑上述现象,对银行客户长期忠诚度进行预测。
(1) 选取适当的客户特征,建立客户长期忠诚度预测模型。客户特征可以从客户信用资格、性别、年龄、户龄、金融资产、客户购买产品数量、持有信用卡状态、活动状态和个人年收入等指标中直接选取,也可以参照任务 4 构建。在报告中给出特征选取的依据、建立预测模型的思路和过程。
(2) 使用混淆矩阵、F1 Score 等方法对预测模型进行评估,在报告中给出评估的方法和结果。
(3) 对“long-customer-test.csv”测试数据进行预测,将全部预测结果以表 10形式保存为文件“result5.xlsx”,其中 0 表示客户没有流失,1 表示客户流失。并将表 11 中的 5 个客户 ID 的预测结果在报告中列出。
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, confusion_matrix
from sklearn.svm import SVC
import matplotlib.pyplot as plt
import seaborn as sns
# 读取数据
data5 = pd.read_excel("result4.xlsx")
# 选择特征和标签
x = data5.iloc[:,1:10]
y = data5["Exited"]
# 对数据进行无量纲化
scaler = MinMaxScaler()
x = scaler.fit_transform(x)
# 划分数据集与测试集
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=422)
# 训练模型
model = SVC(C=10, kernel='poly', gamma=1.0, class_weight={1:3})
model.fit(x_train, y_train)
# 预测
y_pred = model.predict(x_test)
result5.xlsx 预测结果
```bash data_test_save ``` CustomerId Exited 0 15647311 1 1 15737452 1 2 15577657 0 3 15589475 1 4 15687946 1 ... ... ... 995 15732202 0 996 15735078 1 997 15707861 1 998 15594612 1 999 15806360 1 1000 rows × 2 columns指定的 5 个客户 ID 的预测结果
CustomerId Exited
53 15730076 1
359 15674442 0
480 15792228 1
488 15719508 1
907 15579131 0