【数据分析】信用卡用户画像及违约预测逻辑回归模型

article/2025/10/28 19:14:48

1、数据源说明

kaggle上比较经典的数据集,来源某银行个人信贷业务,包含客户数据、信用卡数据、交易订单等基本数据,通过这些数据可以了解银行信贷业务及风险防控相关内容。

2、数据库导入及宽表建立

为便于理解及跨软件处理,已将六张表数据表导入mysql,并将账户、卡、客户信息聚合形成宽表(办卡时长和年龄字段为虚构,交易表最后交易日期为1998.12.31,故以1999.1.1为基准日期分别减去发卡日期和生日得到两个字段),E-R图如下:
在这里插入图片描述

--宽表形成逻辑
CREATE table card_client_totle as
SELECT c.card_id,c.issued,TIMESTAMPDIFF(year,c.issued,'1999-01-01') as 'used_duration',c.type,cl.sex,cl.birth_date,TIMESTAMPDIFF(year,cl.birth_date,'1999-01-01') as 'age', a.account_id,dt.A1 as district_id
FROM  card c LEFT JOIN disp d on c.disp_id = d.disp_id
left join clients cl on d.client_id = cl.client_id
LEFT JOIN accounts a on d.account_id = a.account_id
LEFT join district dt on cl.district_id = dt.A1
where d.type = '所有者'

3、tableau可视化

(1)发卡情况总体描述

在这里插入图片描述
由以上仪表盘可知:
1)金卡与普通卡用户的年龄分布比青年卡更均匀,且年龄更大,年龄分布基本符合不同类型卡的目标用户群体;
2)各类卡发卡量均呈逐年增加的趋势,增加率金卡>普通卡>青年卡,增加量普通卡>金卡>青年卡;
3)各类卡持有量普通卡>金卡>青年卡,普通卡持有量总占比达到总量的四分之三;
4)普通卡、青年卡的持卡量性别差异不明显,金卡用户男性>女性。

4、python违约预测模型建立

(1)数据提取

前期建立宽表过程中,发现card表和accounts表数据量并不匹配,即开通账户的可能并没有开通任何一种类型的卡,所以可视化中信用卡分析用到的card表不能再作为建模的基表。且其他表的外键基本上都是account_id,因此重写了宽表,以accounts表为左表,命名为card_client_totle_1。数据提取代码:

import pandas as pd
import sqlalchemy
import cryptography
import numpy as npengine = sqlalchemy.create_engine("mysql+pymysql://root:数据库密码@localhost:3306/库名",echo=True)
model_sql = '''SELECTt.age,t.sex,d.GDP,d.A11 as 'average_salary',d.A10 as 'city_rate',d.A15 'crime_rate_1995',d.a16 'crime_rate_1996',d.A12 'unemployment_rate_1995',d.A13 'unemployment_rate_1996',d.A14 'entrepreneur_counts',d.A4 'resident_population',od.amount as od_amount,l.amount as loan_amount,l.duration,l.date,l.`status`,l.account_id	
FROMloans l LEFT JOIN `card_client_totle_1` t ON l.account_id = t.account_idleft JOIN district d ON t.district_id = d.A1LEFT JOIN (select ord.account_id,sum(ord.amount) as amount from `order` ord GROUP BY ord.account_id) od on t.account_id = od.account_id;'''df_main_info = pd.read_sql(model_sql,engine)
df_main_info.head()

获得结果:
在这里插入图片描述
将宽表与trans表连接,并增加一个索引列。

df_trans = pd.read_sql("select t.account_id,t.date,t.type,t.amount,t.balance from trans t",engine)
df_trans.head()df_all = df_main_info.merge(df_trans,how='left',on='account_id')
df_all =  df_all.reset_index()
df_all.columns.values[0] = 'New_id'
df_all.info()

查看表信息:
在这里插入图片描述

(2)数据清洗

由上一步可知1995年的失业率和犯罪率有空值,使用中位数进行填充:

#data cleaning
#fill rate_1995 null with median
df_main_info['unemployment_rate_1995'].fillna(df_all['unemployment_rate_1995'].median(),inplace=True)
df_main_info['crime_rate_1995'].fillna(df_all['crime_rate_1995'].median(),inplace=True)df_all['unemployment_rate_1995'].fillna(df_all['unemployment_rate_1995'].median(),inplace=True)
df_all['crime_rate_1995'].fillna(df_all['crime_rate_1995'].median(),inplace=True)
#df_all.crime_rate_1995 =df_all.crime_rate_1995.fillna(df_all[7].median(),inplace=True)
#df_all = df_all['unemployment_rate_1995'].fillna(df_all['unemployment_rate_1995'].median())
df_main_info['unemployment_rate_1995'].isnull().any()

输出为false,即已经没有空值。接下来进行格式转换及去除多余符号。

#日期格式转换,为取近一年数据做准备
df_all['date_x'] = pd.to_datetime(df_all['date_x'])
df_all['date_y'] = pd.to_datetime(df_all['date_y'])
#账户余额、交易额度去掉dollar符,转为数值型
df_all['balance'] = df_all['balance'].map(lambda x:int(''.join(x[1:].split(','))))
df_all['amount'] = df_all['amount'].map(lambda x:int(''.join(x[1:].split(','))))
#df_all.info()
#df_all.isnull().any()
#np.all(np.isfinite(df_all))

(3)数据预处理:建立衍生指标,筛选模型特征

#取贷款前一年交易数据
import datetime
df_year = df_all[df_all['date_y']<df_all['date_x']][df_all['date_x']<df_all['date_y']+datetime.timedelta(days=365)]
#df_year.iloc[:20,15:]
df_year.info()
#df_year.sort_values(by=['account_id','date_y'])
#计算每个账户前一年平均账户余额(衡量财富水平)、余额标准差和变异系数(衡量财富稳定情况)
df_year_acct = df_year.groupby('account_id')['balance'].agg(['mean','std'])
df_year_acct.columns = ['avg_balance','stdev_balance']
# # df_year_acct = df_year.groupby('account_id')['balance'].agg([('avg_balance','mean'),('stdev_balance','std')])
df_year_acct['cv_balance'] = df_year_acct[['avg_balance','stdev_balance']].apply(lambda x:x[1]/x[0],axis=1)
df_year_acct.head()
##np.isfinite().count() #682个
# df_year_acct.info()

计算结果:
在这里插入图片描述
计算收入支出比

#计算平均入账和平均出账比例,对每个账户借贷交易金额汇总
tran_type_dict = {'借':'out','贷':'income'}
df_year['tran_type'] = df_year.type.map(tran_type_dict)
df_year_acct_am = df_year.groupby(['account_id','tran_type'])[['amount']].sum()
#转置表,将入账出账作为字段,方便计算比率
df_year_acct_am_rev = pd.pivot_table(df_year_acct_am,values='amount',index='account_id',columns='tran_type')df_year_acct_am_rev.fillna(0,inplace=True)#避免分母为0
df_year_acct_am_rev['io_rate'] = df_year_acct_am_rev[['out','income']].apply(lambda x:x[0]/x[1],axis=1)df_year_acct_am_rev.head()
#np.isnan(df_year_acct_am_rev).any()

在这里插入图片描述

#合并最初以账户id为主表的表,而非带交易明细的表!
df_total = pd.merge(df_main_info,df_year_acct,left_on='account_id',right_index=True,how='left')
df_total = pd.merge(df_total,df_year_acct_am_rev,left_on='account_id',right_index=True,how='left')
df_total.head()
# df_total.groupby(['account_id','type'])['ammount'].sum()#计算存贷比、贷收比
df_total['loan_amount'] =  df_total['loan_amount'].astype(float)
df_total['ab_rate'] = df_total[['loan_amount','avg_balance']].apply(lambda x:x[0]/x[1],axis=1)
df_total['ai_rate'] = df_total[['loan_amount','income']].apply(lambda x:x[0]/x[1],axis=1)
df_total.info()
#df_all['balance'] = df_all['balance'].map(lambda x:int(''.join(x[1:].split(','))))
#因出现数据类型报错,将报错字段类型改为float,便于计算增长率
df_total['crime_rate_1995'] =  df_total['crime_rate_1995'].astype(float)
df_total['crime_rate_1996'] = df_total['crime_rate_1996'].astype(float)
df_total['unemployment_rate_1995'] = df_total['unemployment_rate_1995'].astype(float)
df_total['unemployment_rate_1996'] = df_total['unemployment_rate_1996'].astype(float)
df_total['crime_chan'] = df_total[['crime_rate_1995','crime_rate_1996']].apply(lambda x:x[1]-x[0],axis = 1)
df_total['unemployment_chan'] = df_total[['unemployment_rate_1995','unemployment_rate_1996']].apply(lambda x:x[1]-x[0],axis = 1)
#将合同状态编码为数字,A代表合同终止无问题,B代表合同终止贷款未支付,
#C代表合同执行中至今正常,D代表处于违约执行期欠债状态。因此BD为违约账户
state_dic = {'A':0,'B':1,'C':2,'D':1}
df_total['status'] = df_total.status.map(state_dic)
df_total.iloc[:20,15:]
#df_total.info()
df_total.head()

在这里插入图片描述
绘制相关矩阵,查看各特征值相关性

#相关矩阵
import matplotlib.pyplot as plt
import seaborn as sns
corr = df_inmodel_all[['age','sex','GDP','average_salary','city_rate','entrepreneur_counts','resident_population','crime_chan','unemployment_chan','duration','loan_amount','avg_balance','stdev_balance','cv_balance','io_rate','ab_rate','ai_rate']].corr()
plt.figure(figsize=(12,9))
sns.heatmap(corr,vmax=1,annot=True)
plt.show()

在这里插入图片描述
根据相关矩阵,说明地区指标相关性较强,取相关性较低的两个指标即可,这里取人均GDP和失业增长率。由于部分特征是相互计算得出,因此要避免重复入模。

#取人均GDP、失业增长率、贷款期限、余额变异系数、消费占比、存贷比
import numpy as np
df_inmodel_all['GDP_avg'] = df_inmodel_all.loc[:,'GDP']/df_inmodel_all.loc[:,'resident_population']
df_inmodel = df_inmodel_all[['sex','age','GDP_avg','unemployment_chan','duration','cv_balance','io_rate','ab_rate','status']]
data_model = df_inmodel[df_inmodel.status !=2] #排除合同还在进行中的账户
#data_model.describe()#查看是否筛选成功
for_predict = df_inmodel[df_inmodel.status==2]
#np.all(np.isfinite(df_inmodel_all))#用来确定参数是否无穷大
#np.isfinite(df_inmodel_all).all()

(4)模型建立:

X = data_model.drop(columns='status')
y = data_model['status']
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size = 0.2,random_state=123)#划分训练集和测试集
X_train.head()
from sklearn.linear_model import LogisticRegression
model = LogisticRegression(penalty='l2',solver='liblinear')#l1范式存在系数为0的情况,本项目中预测准确率高于l2范式,但一般使用l2
model.fit(X_train,y_train)
y_pred = model.predict(X_test)
from sklearn.metrics import accuracy_score
score = accuracy_score(y_pred,y_test)
print(score)

得出测试集的预测准确度:0.875

#查看各系数
print(model.coef_)
print(model.intercept_)

在这里插入图片描述

绘制ROC曲线图:

import sklearn.metrics as metrics
import matplotlib.pyplot as plt
y_pred_proba = model.predict_proba(X_test)
print(y_pred_proba[:10])
fpr, tpr, th = metrics.roc_curve(y_test,y_pred_proba[:,1])
plt.plot(fpr,tpr)
plt.title('ROC')
plt.xlabel('FPR')
plt.ylabel('tpr')
plt.show()

在这里插入图片描述
根据ROC曲线及预测准确度,说明模型效果较好,但在实际业务中,仍需对模型进行调整及迭代。

(5)对合同正在履行的用户(贷款状态为C)进行预测

for_predict_x = for_predict.drop(columns='status')
df_tt_c = df_total[df_total.status==2][['account_id']]#直接写['account_id']一个括号会导致后面报错,即dataframe取列名都要双括号
df_tt_c['pre_result'] = model.predict(for_predict_x)
df_tt_c.head()

结果输出:
在这里插入图片描述

5、总结

(1)做完俩月了才来写总结,很多当时遇到调了好久的东西都忘了不少。存在问题的一部分是处理了指标之后没有关联最初的600来条的账户信息而是关联了带交易数据的大宽表,最后好多重复数据,distinct半天才发现不对劲。所以,还是要明白自己研究的是啥啊。
(2)很多基础性的错误总是不可避免。比如,报无穷大的错误,可能是某个得出某个数的分母分子反了以及分母有0;‘A value is trying to be set on a copy of a slice from a DataFrame’这个错误,可能是引用字段名时少加了一个中括号。算是提高了问题排查能力吧。
(3)因为是练习项目,更多是练习了工具的使用,希望以后能有实际场景的分析需求,能够增加一些思维的锻炼和相关的行业知识。

6、参考资料

(1)和鲸社区https://www.heywhale.com/mw/project/5ed9c13fb772f5002d6dc07c/content
(2)CSDN
https://blog.csdn.net/agoldminer/article/details/111460627
(3)电子书《Python大数据分析与机器学习商业案例实战》


http://chatgpt.dhexx.cn/article/jVQdPbqU.shtml

相关文章

「BUAA SE 个人作业-2」软件案例分析

项目内容这个作业属于哪个课程2023年北航敏捷软件工程这个作业的要求在哪里个人作业-软件案例分析我在这个课程的目标是学习并掌握现代软件工程的开发模式和流程&#xff0c;锻炼个人能力及团队协作能力这个作业在哪个具体方面帮助我实现目标深入分析软件功能&#xff0c;了解软…

山东大学软件学院软件工程作业

第一章 1、请谈一下关于软件工程的整体认识和印象。 软件工程是指用系统化&#xff0c;模块化的工程性方法解决软件开发中的问题。简单来说&#xff0c;软件工程就是使用一种系统化的方法&#xff0c;解决软件开发中的各种困难。解决耦合度高等等的问题。 软件工程的目的是为了…

软件案例分析:商业音乐软件还是开源音乐软件?

项目内容这个作业属于哪个课程2023北航敏捷软件工程这个作业的要求在哪里个人作业-软件案例分析我在这个课程的目标是积累软工经验&#xff0c;进行软工方法论实践&#xff0c;提高工程能力这个作业在哪个具体方面帮助我实现目标从软件工程角度看待一款软件&#xff0c;了解一款…

软件测试质量或时效,版本质量总结的纬度

在一些大的团队,一般会有专职的角色来负责质量管理,即QA。QA在每个项目或版本结束时,追本溯源,重新审视项目过程,从不同纬度来分析版本的各种数据,从而挖掘出整个研发流程和团队存在的问题,进行流程改善和质量、效率提升。 那么通常可以从哪些方面来进行版本质量分析呢。…

一文搞懂用户画像︱敏捷软件开发之用户故事

用户画像&#xff0c;最简单理解为一堆用户特征或者标签的组合。 关于“用户特征” 特征&#xff0c;是指对于同一个指标&#xff0c;在某个维度上的表现特别与众不同。并且这种与众不同在营销上也具备较强的知道意义。所以&#xff0c;用户画像中的维度并不是什么阿猫阿狗都能…

Profile_Day05:企业级360全方位用户画像

Profile_Day05:企业级360全方位用户画像 1昨日内容回顾 主要讲解2个方面的内容: 如何基于SparkSQL实现自定义外部数据源HBase和统计类型标签模型开发. 1, SparkSQL外部数据源HBase 按照SparkSQL模块提供的外部数据源接口,实现HBase表中加载和保存数据 2,今日课程内容提纲 3,…

从理论到工程实践——用户画像入门宝典

用户画像是大数据顶层应用中最重要的一环&#xff0c;搭建一套适合本公司体系的用户画像尤为重要。但是&#xff0c;用户画像的资料往往理论居多&#xff0c;实践少&#xff0c;更少有工程化的实战案例。 本文档结合了常见的用户画像架构&#xff0c;使用Elasticsearch作为底层…

实战案例:场景测试之ATM机取款业务测试

本期&#xff0c;我们通过经典案例——ATM机的操作&#xff0c;来为大家详细说说如何撰写对应的测试用例。 【案例】 在我们日常生活中&#xff0c;ATM机是个大家都非常熟悉的事物。银行为例提高工作效率&#xff0c;方便客户随时办理基础的储蓄和提现业务&#xff0c;于是&a…

干货收藏!快速掌握用户画像项目的开发流程(附流程图)

导读&#xff1a;随着大数据技术的深入研究与应用&#xff0c;企业的关注点日益聚焦在如何利用大数据来为精细化运营和精准营销服务&#xff0c;而要做精细化运营&#xff0c;首先要建立本企业的用户画像。 在画像系统的项目规划阶段需要明确好项目的开发上线流程以及项目各个阶…

音乐软件案例分析

项目内容这个作业属于哪个课程2023年北航敏捷软件工程这个作业的要求在哪里软件案例分析我在这个课程的目标是学习软件工程理论&#xff0c;在实践中体会并运用软件工程理论&#xff0c;收获团队开发和软件工程实践经验这个作业在哪个具体方面帮助我实现目标实践了软件案例分析…

计算机转岗测试,软件测试人员转岗哪些岗位

对于大多数的大龄程序员来说&#xff0c;转岗也是比较常见的一种职业发展方向&#xff0c;而今天我们就通过案例分析来了解一下&#xff0c;软件测试人员转岗哪些岗位。 1.项目经理 测试人员&#xff0c;尤其是敏捷团队的测试人员&#xff0c;涉及到项目质量相关的方方面面&…

如何构建用户画像?

在《4个问题带你了解用户画像》中&#xff0c;我们了解了用户画像的定义、作用及使用注意事项等。 就有用户留言问了&#xff1a;在实际工作中&#xff0c;构建用户画像的方法有哪些&#xff1f;如何构建用户画像呢&#xff1f; 下面我将结合通过案例&#xff0c;带你了解构建用…

软件工程-案例分析

软件工程-案例分析 Mashiroln 项目内容这个作业属于哪个课程https://bbs.csdn.net/forums/buaa-ase2023这个作业的要求在哪里https://bbs.csdn.net/topics/613598122我在这个课程的目标是通过案例分析总结经验和教训&#xff0c;学习以现代软件工程的视角思考问题这个作业在哪…

python用户画像_python用户画像

广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! 06某用户画像案例这里通过一个实践案例来将大家更好地带入实际开发画像、应用画像标签的场景中。 本节主要介绍案例背景及相关的元数据,以及开发标签中可以设计…

实战项目:Java分布式优惠券系统后台(持续更新)

前言 陆陆续续总算是把Java基础、计算机网络基础以及数据库相关技术初步看完了&#xff0c;离深层次的理解还有一定的距离。今天开始准备跟着做一个实际项目吧&#xff0c;也希望在过程中能够把相关技术跟实际应用结合起来&#xff0c;俗话说实践出真知&#xff0c;希望通过这次…

Java生鲜电商平台-优惠券系统设计详解

Java生鲜电商平台-优惠券系统设计详解 Java生鲜电商平台-优惠券系统设计详解 优惠券作为电商最常用的营销手段&#xff0c;对于商家而言可以起到拉新、促活、提高转化的作用&#xff0c;对用户而言也可以获得实惠&#xff0c;今天就来谈谈优惠券系统的设计逻辑。 我对于优惠…

vivo全球商城优惠券系统架构设计与实践

业务背景 优惠券是电商常见的营销手段&#xff0c;具有灵活的特点&#xff0c;既可以作为促销活动的载体&#xff0c;也是重要的引流入口。优惠券系统是vivo商城营销模块中一个重要组成部分&#xff0c;早在15年vivo商城还是单体应用时&#xff0c;优惠券就是其中核心模块之一。…

电商系统之优惠券设计

优惠券在电商系统中&#xff0c;主要起着营销、促销等作用&#xff0c;是给以用户一定的优惠额度&#xff0c;以吸引用户下次继续购买。 优惠券框架 优惠券设计和开发的难点在于各种条件的限制&#xff0c;如图&#xff1a; 上图基本涵盖了整个优惠券系统里要设计的内容&#…

构架稳定与可扩展的优惠券系统

每次打完滴滴, 我们都可以分享领券页面到朋友圈, 让大家一起来领券. 而领完券后, 一大堆5折券到账的感觉一定很爽(可惜现在的折扣越来越少了). 想必大家都对滴滴的优惠券影响深刻. 滴滴的用户规模如此之大, 送券力度如此之高, 如果由我们来做,该如何构架这样一个稳定且有扩展性…