0


7.Python数据分析项目之银行客户流失分析

1.总结

预测类数据分析项目
流程具体操作基本查看查看缺失值(可以用直接查看方式isnull、图像查看方式查看缺失值missingno)、查看数值类型特征与非数值类型特征、一次性绘制所有特征的分布图像、单独绘制目标值与所有数值型参数之间的关系、单独绘制目标值与所有字符型参数之间的关系预处理缺失值处理(填充)拆分数据(获取有需要的值) 、统一数据格式、特征工程(特征编码、0/1字符转换、自定义、特征衍生) 、降维(特征相关性、PCA降维)、数据分析groupby分组求最值数据、seaborn可视化预测拆分数据集、建立模型(机器学习:RandomForestRegressor、LogisticRegression、GradientBoostingRegressor、GradientBoostingClassifier、RandomForest)、训练模型、预测、评估模型(ROC曲线、MSE、MAE、RMSE、R2)、调参(GridSearchCV)
数量查看:条形图
占比查看:饼图
数据分区分布查看:概率密度函数图
查看相关关系:条形图、热力图
分布分析:分类直方图(countplot)、分布图-带有趋势线的直方图(distplot)

自然语言处理项目:
流程具体操作基本查看导入数据(surprise.Dataset 、pickle)预处理获取数据转换为对应的数据格式、Dataset构建训练集、数据可视化(绘制词云图)分组统计数量、训练模型(学习词频信息)、使用自定义背景图、绘制词云图建模(文本分类)文本分类(LDA模型)、机器学习(朴素贝叶斯)、深度学习(cnn、LSTM、GRU)
推荐系统
流程具体操作基本查看导入数据 、获取数据转换为列表、预处理删除空值、关键词抽取(基于 TF-IDF、基于TextRank )、分词(jieba) 、关键词匹配(词袋模型)、处理分词结果(删除特殊字符、去除停用词)建模(推荐系统surprise)与预测KNNBaseline算法

2.银行业客群及产品类别

银行客户群

  • 个人客户 银行对个人客户的业务主要是以合理安排客户的个人财物为手段,为之提供存取款、小额贷款、代理投资理财、信息咨询及其他各类中介服务,由此为客户取得收益并帮助其防范风险,同时提高银行自身效益。
  • 公司客户 公司客户主要指与银行发生业务关系的各企事业单位及政府机关,其中以企业单位为主体。公司客户能为银行带来大量存款、贷款和收费业务,并成为银行利润的重要来源。
  • 零售客户 包括消费信贷客户、信用卡客户、贵宾理财客户等

银行产品类别

  • 信贷类资产- 信用贷款- 抵押贷款- 保证书担保贷款- 贷款证券化- 负债业务
  • 活期存款- 定期存款- 储蓄存款- 可转让定期存单- 其他种类

3.客户流失预警的模型建立分析

3.1 客户流失预警模型的业务意义

  • 严格地讲,客户流失指的是客户在该行所有业务终止,并销号。但是具体业务部门可单独定义在该部门的全部或某些业务上,客户的终止行为
  • 对专家及金融业业内人士的走访及调研结果表明,商业银行客户流失较为严重。国内商业银行,客户流失率可达20%甚至更高。而获得新客户的成本,可达维护现有客户的5倍。
  • 因此,从海量客户交易记录中挖掘出对流失有影响的信息,建立高效的客户流失预警体系尤为重要。

3.2 客户流失主要原因

  • 价格流失
  • 产品流失
  • 服务流失
  • 市场流失
  • 促销流失
  • 技术流失
  • 政治流失

3.3 维护客户关系的基本方法

  • 追踪制度
  • 产品跟进
  • 扩大销售
  • 维护访问
  • 机制维护

3.4 建立量化模型,合理预测客群的潜在流失风险

  • 常用的风险因子
  • 客户持有的产品数量、种类
  • 客户的年龄、性别
  • 受地理区域的影响
  • 受产品类别的影响
  • 交易的间隔时间
  • 营销、促销手段
  • 银行的服务方式和态度

4.数据描述

  1. import warnings
  2. warnings.filterwarnings('ignore')import pandas as pd
  3. from pandas.plotting import scatter_matrix
  4. import numbers
  5. import numpy as np
  6. import math
  7. import matplotlib.pyplot as plt
  8. import random
  9. from numpy import*import operator
  10. import numbers
  11. import datetime
  12. import time
  13. import seaborn as sns
  14. from statsmodels.formula.api import ols
  15. from statsmodels.stats.anova import anova_lm
  16. from scipy.stats import chisquare
  17. from sklearn.ensemble import GradientBoostingClassifier
  18. from sklearn.model_selection import train_test_split
  19. from sklearn import ensemble, metrics
  20. from sklearn.model_selection import KFold
  21. from sklearn.model_selection import train_test_split
  22. from sklearn.model_selection import GridSearchCV
  23. %matplotlib inline
  24. # 读取银行内部数据 重点关注CHURN_CUST_IND(是否流失客户,0流失,1未流失)
  25. banChurn = pd.read_csv('bankChurn.csv')
  26. externaData = pd.read_csv('ExternalData.csv')
  1. banChurn.head()# 数值型数据的描述
  2. banChurn.describe()# 字符型数据的描述
  3. banChurn.describe(include=np.object_)
  4. externaData.head()# 数值型数据的描述
  5. externaData.describe()# 字符型数据的描述
  6. externaData.describe(include=np.object_)

5.数据分析

5.1 绘制每个特征的分布

  1. # 绘制每个特征的分布# dataset:数据集 cols:绘图中每行显示的列数defplot_distribution(dataset, cols=5, width=20, height=15, hspace=0.2, wspace=0.5):
  2. plt.style.use('seaborn-whitegrid')
  3. fig = plt.figure(figsize=(width,height))
  4. fig.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=wspace, hspace=hspace)
  5. rows = math.ceil(float(dataset.shape[1])/ cols)# 绘图的行数for i, column inenumerate(dataset.columns):# 遍历数据集中的每一列(特征)
  6. ax = fig.add_subplot(rows, cols, i +1)
  7. ax.set_title(column)#非数值型数据if dataset.dtypes[column]== np.object_:
  8. g = sns.countplot(y=column, data=dataset)# 绘制数量描述图
  9. substrings =[s.get_text()[:18]for s in g.get_yticklabels()]
  10. g.set(yticklabels=substrings)
  11. plt.xticks(rotation=25)else:# 数值型数据
  12. g = sns.distplot(dataset[column])# 直方图
  13. plt.xticks(rotation=25)
  14. plot_distribution(bankChurn, cols=5, width=20, height=160, hspace=0.45, wspace=0.5)

在这里插入图片描述

5.2 数值型变量绘图分析

  1. # 将数值型变量绘图分析# target 目标,是否流失# 分析df数据集中的col列对target的影响defNumVarPerf(df,col,target,truncation=False):'''
  2. :param df: the dataset containing numerical independent variable and dependent variable
  3. :param col: independent variable with numerical type
  4. :param target: dependent variable, class of 0-1
  5. :param truncation: indication whether we need to do some truncation for outliers
  6. :return: the descriptive statistics
  7. '''#extract target variable and specific indepedent variable
  8. validDf = df.loc[df[col]== df[col]][[col,target]]# 提取非空(col列值非nan)样本的col列和target列#the percentage of valid elements
  9. validRcd = validDf.shape[0]*1.0/df.shape[0]# 计算有效样本(col列值非nan)的比例#format the percentage in the form of percent
  10. validRcdFmt ="%.2f%%"%(validRcd*100)#the descriptive statistics of each numerical column
  11. descStats = validDf[col].describe()
  12. mu ="%.2e"% descStats['mean']
  13. std ="%.2e"% descStats['std']
  14. maxVal ="%.2e"% descStats['max']
  15. minVal ="%.2e"% descStats['min']#we show the distribution by churn/not churn state
  16. x = validDf.loc[validDf[target]==1][col]# 提取流失客户的col列
  17. y = validDf.loc[validDf[target]==0][col]# 提取现有未流失客户的col列
  18. xweights =100.0* np.ones_like(x)/ x.size
  19. yweights =100.0* np.ones_like(y)/ y.size
  20. #if need truncation, truncate the numbers in 95th quantileif truncation ==True:
  21. pcnt95 = np.percentile(validDf[col],95)# 获取col列数据的95分位数
  22. x = x.map(lambda x:min(x,pcnt95))# 如果超过95分位数,则说明是极端值,使用95分位数
  23. y = y.map(lambda x:min(x,pcnt95))# 如果超过95分位数,则说明是极端值,使用95分位数
  24. fig, ax = plt.subplots()# weights参数:与x形状相同的权重数组;将x中的每个元素乘以对应权重值再计数
  25. ax.hist(x, weights=xweights, alpha=0.5,label='Attrition')# Attrition流失
  26. ax.hist(y, weights=yweights, alpha=0.5,label='Retained')# Retained 保持
  27. titleText ='Histogram of '+ col +'\n'+'valid pcnt ='+validRcdFmt+', Mean ='+mu +', Std='+std+'\n max='+maxVal+', min='+minVal
  28. ax.set(title= titleText, ylabel='% of Dataset in Bin')
  29. ax.margins(0.05)
  30. ax.set_ylim(bottom=0)
  31. plt.legend(loc='upper right')
  32. plt.show()# 未截断极端值,绘制的图不能很好刻画存款余额与是否流失的关系
  33. NumVarPerf(bankChurn,'SAV_CUR_ALL_BAL','CHURN_CUST_IND',truncation=False)

在这里插入图片描述

5.3 字符型变量绘图分析

  1. # 字符型变量绘图分析defCharVarPerf(df,col,target):'''
  2. :param df: the dataset containing numerical independent variable and dependent variable
  3. :param col: independent variable with numerical type
  4. :param target: dependent variable, class of 0-1
  5. :return: the descriptive statistics
  6. '''
  7. validDf = df.loc[df[col]== df[col]][[col, target]]
  8. validRcd = validDf.shape[0]*1.0/df.shape[0]
  9. recdNum = validDf.shape[0]
  10. validRcdFmt ="%.2f%%"%(validRcd*100)
  11. freqDict ={}
  12. churnRateDict ={}#for each category in the categorical variable, we count the percentage and churn ratefor v inset(validDf[col]):# 遍历去重后的列值(集合去重)
  13. vDf = validDf.loc[validDf[col]== v]
  14. freqDict[v]= vDf.shape[0]*1.0/recdNum # 某特征值占的比率
  15. churnRateDict[v]=sum(vDf[target])*1.0/vDf.shape[0]# 某特征的值对应的流失率
  16. descStats = pd.DataFrame({'percent':freqDict,'churn rate':churnRateDict})
  17. fig = plt.figure()# Create matplotlib figure
  18. ax = fig.add_subplot(111)# Create matplotlib axes
  19. ax2 = ax.twinx()# Create another axes that shares the same x-axis as ax.
  20. plt.title('The percentage and churn rate for '+col+'\n valid pcnt ='+validRcdFmt)
  21. descStats['churn rate'].plot(kind='line', color='red', ax=ax)
  22. descStats.percent.plot(kind='bar', color='blue', ax=ax2, width=0.2,position =1)
  23. ax.set_ylabel('churn rate')
  24. ax2.set_ylabel('percentage')
  25. plt.show()# GENDER_CD(性别代码)与“是否流失”的关系
  26. CharVarPerf(bankChurn,'GENDER_CD','CHURN_CUST_IND')

在这里插入图片描述

6.数据预处理

6.1 填充缺失值(针对数值型数据)

  1. defMakeupMissing(df,col,types,method):# 针对数值型数据填充缺失值'''
  2. df: 数据集DataFrame
  3. col:列名
  4. types:判断类型
  5. method:填充方式
  6. '''
  7. validDf = df.loc[df[col]== df[col]][[col]]# 获取col列非空(非nan)的记录if validDf.shape[0]== df.shape[0]:# 没有缺失值记录return"{} 列没有缺失值".format(col)
  8. missingList =[i for i in df[col]]# 取出col列的所有值(包括了缺失值)if types =="Continuous":# 只针对数值型列的数据if method notin["Mean","Random"]:# 填充方式仅限于“平均值填充”与“随机值填充”return"填充方式仅限于平均值填充(Mean)与随机值填充(Random)"
  9. descStats = validDf[col].describe()# 获取有效数据集的描述信息数据
  10. mu = descStats["mean"]# 有效数据集列的均值
  11. std = descStats["std"]# 有效数据集列的标准差
  12. maxVal = descStats["max"]# 有效数据集列的最大值# 检测极端值(使用3-sigma方式检测)if maxVal > mu +3* std:# 先判断最大有效值是否超过指定的边界(是否为极端值)for i inlist(validDf.index):# 逐行遍历validDfif validDf.loc[i][col]> mu +3* std:# 判断当前行的col列是否是极端值
  13. validDf.loc[i][col]= mu +3* std # 替换掉当前的极端值
  14. mu = validDf[col].describe()['mean']# 重新计算col列的均值for i inlist(df.index):# 遍历原始df数据集if df.loc[i][col]!= df.loc[i][col]:# 如果当前行的col列值是缺失值if method =="Mean":# 判断填充方式
  15. missingList[i]= mu # 填充当前的缺失值elif method =="Random":# 从validDf[col]有效数据列中随机选择一个数,作为当前缺失值的填充值
  16. missingList[i]= random.sample(validDf[col],1)[0]print("{}列的缺失值填充完毕".format(col))return missingList

6.2 数字编码

  1. ## 对类别变量使用数字编码: 计算出每一个列中的不同值对应的客户流失率defEncoder(df,col,target):
  2. encoder ={}for v inset(df[col]):# 取出df数据集中col列的所有不重复值(集合去重)if v == v:# 如果正在遍历的不是缺失值
  3. subDf = df[df[col]== v]# 获取原始数据集中col列的值为当前遍历值的记录else:# 如果正在遍历的是缺失值
  4. xList =list(df[col])# 获取df[col]所有缺失值对应的索引
  5. nanInd =[i for i inrange(len(xList))if xList[i]!= xList[i]]
  6. subDf = df.loc[nanInd]# 所有缺失值的记录# 记录col列的每个值与其对应的客户流失率
  7. encoder[v]=sum(subDf[target])*1.0/ subDf.shape[0]
  8. newCol =[encoder[i]for i in df[col]]# 获取每个值对应的客户流失率return newCol

6.3 两变量的比

  1. ## 计算两个变量比的函数defColumnDivide(df,colNumerator,colDenominator):
  2. N = df.shape[0]# 数据集的行数
  3. rate =[0]* N
  4. xNum =list(df[colNumerator])
  5. xDenom =list(df[colDenominator])for i inrange(N):if xDenom[i]>0:
  6. rate[i]= xNum[i]*1.0/ xDenom[i]# rate填充比值else:
  7. rate[i]=0return rate

6.4 合并数据集

  1. ALLData = pd.merge(bankChurn,externaData,on='CUST_ID')
  2. ALLData

在这里插入图片描述

6.5 调用预处理函数

  1. modelData = AllData.copy()
  2. indepCols =list(modelData.columns)#移除目标列
  3. indepCols.remove('CHURN_CUST_IND')# 移除客户ID
  4. indepCols.remove('CUST_ID')
  5. except_var =[]# 存放处理过程发生异常的列名for var in indepCols:try:# 将当前遍历的列的值去重,然后存储到x0列表中
  6. x0 =list(set(modelData[var]))#forgntvl(是否有境外旅行)列的处理if var =='forgntvl':
  7. x00 =[np.nan]# 如果正在遍历的'forgntvl'列的值非空[x00.append(i)for i in x0 if i notin x00 and i==i]
  8. x0 = x00
  9. iflen(x0)==1:# 如果当前列的值只有一种值,则移除当前列print('Remove the constant column {}'.format(var))
  10. indepCols.remove(var)# 移除列continue# x0去除空值,保存有效值到x列表中
  11. x =[i for i in x0 if i==i]# 如果当前列的值属于数值型ifisinstance(x[0],numbers.Real)andlen(x)>4:if np.nan in x0:# 如果当前列中存在缺失值,则填充print('nan is found in column {}, so we need to make up the missing value'.format(var))
  12. modelData[var]= MakeupMissing(modelData,var,'Continuous','Random')else:# 如果当前列的值属于字符型print('Encode {} using numerical representative'.format(var))
  13. modelData[var]= Encoder(modelData, var,'CHURN_CUST_IND')except:print("something is wrong with {}".format(var))
  14. except_var.append(var)continue

7.特征工程

生成新的特征(特征衍生)的常用方法
根据业务,求相关比率
根据业务,将相关列可以进行加和
根据业务,取多列特征中有代表性的特征 (以max为例)
根据业务,删除有极大相关性特征等

  1. # 计算相关比,添加到新列(新特征)
  2. modelData['AVG_LOCAL_CUR_TRANS_TX_AMT']= ColumnDivide(modelData,'LOCAL_CUR_TRANS_TX_AMT','LOCAL_CUR_TRANS_TX_NUM')
  3. modelData['AVG_LOCAL_CUR_LASTSAV_TX_AMT']= ColumnDivide(modelData,'LOCAL_CUR_LASTSAV_TX_AMT','LOCAL_CUR_LASTSAV_TX_NUM')#### 计算每个样本的指定五个列的最大值,添加到新列(新特征)volatilityMax
  4. maxValueFeatures =['LOCAL_CUR_SAV_SLOPE','LOCAL_BELONEYR_FF_SLOPE','LOCAL_OVEONEYR_FF_SLOPE','LOCAL_SAV_SLOPE','SAV_SLOPE']# 提取每个样本的相关波动率最大值作为新的特征volatilityMax
  5. modelData['volatilityMax']= modelData[maxValueFeatures].apply(max, axis =1)## 删除LOCAL_CUR_MON_AVG_BAL_PROP这个冗余特征 #本币活期月日均余额占比 = 1 - 本币定期月日均余额占比del modelData['LOCAL_CUR_MON_AVG_BAL_PROP']## 对指定的特征列相加,得到新的特征
  6. sumupCols0 =['LOCAL_CUR_MON_AVG_BAL','LOCAL_FIX_MON_AVG_BAL']
  7. sumupCols1 =['LOCAL_CUR_WITHDRAW_TX_NUM','LOCAL_FIX_WITHDRAW_TX_NUM']
  8. sumupCols2 =['LOCAL_CUR_WITHDRAW_TX_AMT','LOCAL_FIX_WITHDRAW_TX_AMT']
  9. sumupCols3 =['COUNTER_NOT_ACCT_TX_NUM','COUNTER_ACCT_TX_NUM']
  10. sumupCols4 =['ATM_ALL_TX_NUM','COUNTER_ALL_TX_NUM']
  11. sumupCols5 =['ATM_ACCT_TX_NUM','COUNTER_ACCT_TX_NUM']
  12. sumupCols6 =['ATM_ACCT_TX_AMT','COUNTER_ACCT_TX_AMT']
  13. sumupCols7 =['ATM_NOT_ACCT_TX_NUM','COUNTER_NOT_ACCT_TX_NUM']
  14. modelData['TOTAL_LOCAL_MON_AVG_BAL']= modelData[sumupCols0].apply(sum, axis =1)
  15. modelData['TOTAL_WITHDRAW_TX_NUM']= modelData[sumupCols1].apply(sum, axis =1)
  16. modelData['TOTAL_WITHDRAW_TX_AMT']= modelData[sumupCols2].apply(sum, axis =1)
  17. modelData['TOTAL_COUNTER_TX_NUM']= modelData[sumupCols3].apply(sum, axis =1)
  18. modelData['TOTAL_ALL_TX_NUM']= modelData[sumupCols4].apply(sum, axis =1)
  19. modelData['TOTAL_ACCT_TX_NUM']= modelData[sumupCols5].apply(sum, axis =1)
  20. modelData['TOTAL_ACCT_TX_AMT']= modelData[sumupCols6].apply(sum, axis =1)
  21. modelData['TOTAL_NOT_ACCT_TX_NUM']= modelData[sumupCols7].apply(sum, axis =1)## 根据指定列的比,创建新列(新特征)# 分子列
  22. numeratorCols =['LOCAL_SAV_CUR_ALL_BAL','SAV_CUR_ALL_BAL','ASSET_CUR_ALL_BAL','LOCAL_CUR_WITHDRAW_TX_NUM','LOCAL_CUR_WITHDRAW_TX_AMT','COUNTER_NOT_ACCT_TX_NUM','ATM_ALL_TX_NUM','ATM_ACCT_TX_AMT','ATM_NOT_ACCT_TX_NUM']# 分母列
  23. denominatorCols =['LOCAL_SAV_MON_AVG_BAL','SAV_MON_AVG_BAL','ASSET_MON_AVG_BAL','TOTAL_WITHDRAW_TX_NUM','TOTAL_WITHDRAW_TX_AMT','TOTAL_COUNTER_TX_NUM','TOTAL_ACCT_TX_NUM','TOTAL_ACCT_TX_AMT','TOTAL_NOT_ACCT_TX_NUM']
  24. newColName =["RATIO_"+str(i)for i inrange(len(numeratorCols))]# 分别求比,添加新特征for i inrange(len(numeratorCols)):
  25. modelData[newColName[i]]= ColumnDivide(modelData, numeratorCols[i], denominatorCols[i])

8.建模

本项目通过读取modelData.csv文件的数据(该文件已进行更细化的特征工程处理)进行建模,使用GradientBoostingClassifier模型进行建模。

  1. # 通过读取modelData.csv文件(已进行了进一步特征处理)的数据进行建模
  2. modelData = pd.read_csv("modelData.csv")
  3. allFeatures =list(modelData.columns)# 所有特征名称转换为列表# 移除建模不需要的特征列
  4. allFeatures.remove("CUST_ID")# 移除客户ID
  5. allFeatures.remove("CHURN_CUST_IND")# 移除客户流失标签列# 拆分数据集为训练集和测试集
  6. X_train,X_test,y_train,y_test = train_test_split(modelData[allFeatures],
  7. modelData["CHURN_CUST_IND"],random_state=10)# 建模
  8. gbc = GradientBoostingClassifier(random_state=10)# 创建模型对象
  9. gbc.fit(X_train,y_train)# 拟合训练集(通过训练集对模型训练)
  10. y_pred = gbc.predict(X_test)# 对测试集进行预测print("在测试集上的准确率",metrics.accuracy_score(y_test,y_pred))

9.调参

所谓调参,就是对使用模型的相关参数进行调整,已达到更好的预测准确率。
本项目使用GridSearchCV对象实现交叉验证的方式进行调参。

  1. # 使用交叉验证的方式调参# 提前将GradientBoostingClassifier需要调整的参数的范围设置好
  2. param_test ={'n_estimators':range(20,81,10),'max_depth':range(3,8,2),'min_samples_split':range(100,500,200)}# 交叉验证,用来搜索最佳参数组合
  3. gsearch1 = GridSearchCV(estimator = GradientBoostingClassifier(learning_rate=0.1, min_samples_split=300,
  4. min_samples_leaf=20,max_depth=8,max_features='sqrt', subsample=0.8,random_state=10),
  5. param_grid = param_test, scoring='roc_auc',cv=5)
  6. gsearch1.fit(X_train,y_train)# 在训练集上调参# 查看最佳参数组合与分数
  7. gsearch1.best_params_, gsearch1.best_score_

再将调到最好的参数给到测试集

  1. # 将最佳参数设置到模型中
  2. gbc = GradientBoostingClassifier(learning_rate=0.1, min_samples_split=100,
  3. min_samples_leaf=20,max_depth=3,max_features='sqrt', subsample=0.8,random_state=10,
  4. n_estimators=60)
  5. gbc.fit(X_train,y_train)# 拟合训练集(通过训练集对模型训练)
  6. y_pred = gbc.predict(X_test)# 对测试集进行预测print("在测试集上的准确率",metrics.accuracy_score(y_test,y_pred))

本文转载自: https://blog.csdn.net/m0_63953077/article/details/129261503
版权归原作者 想成为数据分析师的开发工程师 所有, 如有侵权,请联系我们删除。

“7.Python数据分析项目之银行客户流失分析”的评论:

还没有评论