# -*- coding:utf-8 -*-import pandas as pd # 用于 数据清洗 和 数据整理import os # 工作路径 及 文件夹操作import datetimepd.set_option('display.max_columns',None)# 分别设置好 数据集路径 和 之后保存生成的文件 的路径data_path = r'D:\Data_Sets_of_Analysis\Project_1_Analysis_of_Credit_Card_User_Portrait_and_Personal_Loan_Overdue\Data_Set_of_Credit_Card_User_Portrait_and_Personal_Loan_Overdue'save_file_path = r'D:\Data_Sets_of_Analysis\Project_1_Analysis_of_Credit_Card_User_Portrait_and_Personal_Loan_Overdue'os.chdir(data_path)load_file = os.listdir()print('\n' + '-'*25 + '打印目录列表' + '-'*25) # 提示性分割线,方便阅读print(load_file) # 打印目录列表print('\n'*2 + '='*100) # 打印模块分割线,方便阅读'''【 #%% 】 打开 View → Scientific Model 后点击其左边的绿色剪头 可分块执行'''# # 因文件较多,故采用for循环结合locals函数动态生成多个变量table_name = []columns_name_express = []columns_name = []sql_statement = []dataframe_table_columns_name = pd.DataFrame(columns=['表名','列名']) # 生成一个空的DataFrame,其列名为'表名'和'列名'for i in load_file:
# 仅读取后缀为csv的表
if i.split('.')[1] == 'csv':
# locals()方法动态生成多个变量
locals()[i.split('.')[0]] = pd.read_csv(i,encoding='gbk',error_bad_lines=False,low_memory=False)
# 将 每一个表的名字 都添加到 table_name 列表中
table_name.append(i.split('.')[0])
# 将 每一个表的名字 连同 每一列的名字都添加到 columns_name_express 和 columns_name 列表中
columns_name.append(list(locals()[i.split('.')[0]]))
columns_name_express.append([i.split('.')[0] + '表的列名如下:' + str(list(locals()[i.split('.')[0]])) + '\n' + '-'*125])
''' i.split('.')[0] → 每一个表的名字 str(list(locals()[i.split('.')[0]])) → 1.locals[]中填入表示 表名 的i.split('.')[0],表示选中该locals中的该表 2.list(DataFrame)表示DataFrame的列名,故list(locals()[i.split('.')[0]]) 表示的是 取 表名 为i.split('.')[0] 的 表 的 列名 3.最后因为要连接字符串,所以用str()函数将以上进行转换 '\n' → 表示换行 '-'*125 → 将字符串- 乘以125 表示打印 - 125次 作用为画一个分割线,方便观察,不易串行 最后将以上字段转换为列表,用append添加到 columns_name 中 '''
# 为方便用在线工具画ER图,在此顺便生成sql语句
tn_str =''
for j in range(len(list(locals()[i.split('.')[0]]))):
tn_str += '\n' + list(locals()[i.split('.')[0]])[j] + ' ' + 'TEXT' + ','
sql_statement.append('CREATE TABLE' + ' ' + i.split('.')[0] + '\n' + '(' + tn_str.strip(',') + '\n' + ');')
# 生成一个 表格、列名透视表 并输出excel文件 方便作 列名分析
dataframe_table_columns_name = dataframe_table_columns_name.append(pd.DataFrame({'表名':i.split('.')[0],'列名':columns_name[-1]}))# 生成sql语句,复制后进入freego通过导入MySQL DDL自动生成ER表格,再添加关系线即可生成ER图# # 注释:列属性在此统一设置为【 TEXT 】仅为方便观察使用print('在线MySQL语句绘制ER图链接:https://www.freedgo.com/erd-index.html,复制以下生成的sql语句')for i in sql_statement:
print(i)dataframe_table_columns_name['列名含义分析'] = 0 # 为作pivot图,增加全为0的数值列,之后再用replace(),将0全部替换为空warning = '🔺 注意:.to_excel重复执行会覆盖原有表,故在正式编辑excel时应注意另存为新表,或创建一个副本' # 增加一列提示,方便表格使用pivot_table_column_name = pd.pivot_table(dataframe_table_columns_name,index=['表名','列名']).replace(0,'').append(pd.DataFrame(columns=[warning]))print('\n'*2 + '='*100) # 打印模块分割线,方便阅读# 将以上得到的表格保存到路径为最开始已经设定好的【 save_file_path 】中,表格名称为【 列名含义分析表(请另存).xls 】try:
pivot_table_column_name.to_excel(save_file_path +r'\列名含义分析表(请另存).xls')
print('表格已保存完毕')except Exception:
print('错误,‘列名含义分析表(请另存).xls’未能成功保存,请检查报错原因(如:重复执行时,原先文件打开未关闭,则后一次执行代码时,无法覆盖原文件报错)')print('\n'*2 + '='*100) # 打印模块分割线,方便阅读print('所有表格的名称如下:' + str(table_name))print('\n'*2 + '='*100) # 打印模块分割线,方便阅读for i in range(len(columns_name_express)):
print(columns_name_express[i][0])print('\n'*2 + '='*100) # 打印模块分割线,方便阅读for tn in load_file:
col_num = len(list(locals()[tn.split('.')[0]]))
pd.set_option('display.max_columns', col_num) # 设置显示最大列数为 表的列数
print('\n' + '-'*25 + '以下为%s表'% tn.split('.')[0] + '-'*25)
print(locals()[tn.split('.')[0]].head()) # 默认打印前五行
print(locals()[tn.split('.')[0]].count()) # 打印各列数据数# 创建一个函数用于 有选择性地 查询 某个表 中 某个列 的 可选取值(通过该函数还能查看是否有空值)# # 1) 因def中无法直接引用local,故将所有的表格保存到一个字典中,键 为 表名,值 为 表dict_of_tables = dict()for i in load_file:
dict_of_tables[i.split('.')[0]] = locals()[i.split('.')[0]] # 使用for循环将每一个表都增加到dict_of_tables中# # 2) 进行函数定义def check_distinct_column_value():
while True:
t_name = input('请输入 表名 :')
c_name = input('请输入 列名 :')
try:
import sqlite3
con = sqlite3.connect(':memory:')
dict_of_tables[t_name].to_sql(t_name,con)
c_value = pd.read_sql_query('select distinct %s from %s' % (c_name, t_name), con)
print(c_value)
break
except Exception:
print('发生错误,请检查所输入的 表名 和 列名 及其对应关系 是否正确,并重新输入')# # 3) 选择是否调用该查询函数while True:
answer = input('是否调用“列名取值查看”函数?回答 Y 或 N:')
if answer == 'Y':
check_distinct_column_value() #函数调用
elif answer == 'N':
break
else:
print('输入有误,请重新输入回答,仅可回答Y 或 N')'''通过 信用卡客户画像 的 目标拆解 和 ER图,需要用到 card、clients、disp(连接关系用)、trans根据之前的数据查看,这三个表中均无缺失的情况'''import sqlite3con = sqlite3.connect(':memory:')locals()['card'].to_sql('card',con)locals()['clients'].to_sql('clients',con)locals()['disp'].to_sql('disp',con)locals()['trans'].to_sql('trans',con)sql_card_client = '''SELECT cd.*,ctdp.birth_date,ctdp.district_id,ctdp.sexFROM card AS cd JOIN (SELECT ct.birth_date,ct.district_id,ct.sex,dp.disp_id FROM clients AS ctJOIN disp AS dp ON ct.client_id = dp.client_id WHERE dp.type == "所有者")AS ctdpON cd.disp_id = ctdp.disp_id'''card_client = pd.read_sql_query(sql_card_client,con)print(card_client)# # 因作图可能涉及到中文,在此先设置字体from pylab import mplmpl.rcParams['font.sans-serif'] = ['SimHei'] # 指定默认字体mpl.rcParams['axes.unicode_minus'] = False # 解决保存图像负号'-'显示为方块的问题# # # 探究信用卡总量 不同年份 的总量变化import datetimeimport matplotlib.pyplot as plt# # # 新增一列‘issue_year’,提取出年份card_client['issue_year'] = pd.to_datetime(card_client['issued']).map(lambda x:x.year)# # # 创建一个交叉表,显示不同类别卡,不同年份的发行数量cross_tab = pd.crosstab(card_client.issue_year,card_client.type)print(cross_tab)# # # 画趋势面积图labels = ['青年卡','普通卡','金卡']y1 = cross_tab.loc[:,'青年卡'].astype('int') # 将'青年卡'这列的每行的字符都转换成inty2 = cross_tab.loc[:,'普通卡'].astype('int')y3 = cross_tab.loc[:,'金卡'].astype('int')x = cross_tab.index # 将index列,也就是issue_year转换成intplt.stackplot(x,y1,y2,y3,labels=labels,colors=['#f89588','#3b6291','#f8cb7f'])plt.title('信用卡发卡量随年份的时间变化趋势图')plt.legend(loc = 'upper left') # 设置图例位置在左上角plt.ylim(0,500) # 设置y轴刻度最大值# # # 设置 数字标签,表明对应的发卡量for a,b in zip(x,y1):
plt.text(a,0.5*b-10,b,ha='center', va= 'bottom',fontsize=7)for a,b in zip(x,y2):
plt.text(a,0.5*b+list(y1)[list(x).index(a)]-10,b,ha='center', va= 'bottom',fontsize=7)for a,b in zip(x,y3):
plt.text(a,0.5*b+list(y1)[list(x).index(a)]+list(y2)[list(x).index(a)]-10,b,ha='center', va= 'bottom',fontsize=7)plt.show()'''设置 数字标签,格式示例: for a,b in zip(x,y): plt.text(a, b+0.05, '%.0f' % b, ha='center', va= 'bottom',fontsize=7) a:数字标签的横轴坐标 b+0.05:数字标签的纵轴坐标 '%.0f' % b:格式化的数字标签(保留一位小数) ha='center':horizontalalignment(水平对齐) va= 'bottom':verticalalignment(垂直对齐)的方式 fontsize:文字大小本案例中的实例说明: 因本例为堆叠图,故对数字标签的纵轴坐标相应做了值叠加 y1对应的数字标签的纵坐标放中间,并下移10(下移10是为了看起来更美观) y2对应的数字标签的纵坐标,要使其同样放中间,则需用y2对应的b值乘以0.5再加上y1对应的b值,再下移10 y3对应的数字标签的纵坐标,要使其同样放中间,则需用y3对应的b值乘以0.5再加上y1和y2对应的b值,再下移10'''cross_tab1 = cross_tabcross_tab1.loc['Sum'] = 0for i in list(cross_tab):
cross_tab1.loc['Sum'][i] = sum(cross_tab1[i])print(cross_tab1)plt.pie(cross_tab1.loc['Sum'],labels=list(cross_tab1),autopct='%1.1f%%',startangle=100,colors=['#3b6291','#f8cb7f','#f89588'])plt.title('不同种类卡的占比情况')plt.show()# 如遇中文显示问题可加入以下代码from pylab import mplmpl.rcParams['font.sans-serif'] = ['SimHei'] # 指定默认字体mpl.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题def stack2dim(raw, i, j, rotation=0, location='upper right'):
''' 此函数是为了画两个维度标准化的堆积柱状图 raw为pandas的DataFrame数据框 i、j为两个分类变量的变量名称,要求带引号,比如"school" rotation:水平标签旋转角度,默认水平方向,如标签过长,可设置一定角度,比如设置rotation = 40 location:分类标签的位置,如果被主体图形挡住,可更改为'upper left' '''
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import math
data_raw = pd.crosstab(raw[i], raw[j])
data = data_raw.div(data_raw.sum(1), axis=0) # 交叉表转换成比率,为得到标准化堆积柱状图
# 计算x坐标,及bar宽度
createVar = locals()
x = [0] # 每个bar的中心x轴坐标
width = [] # bar的宽度
k = 0
for n in range(len(data)):
# 根据频数计算每一列bar的宽度
createVar['width' + str(n)] = list(data_raw.sum(axis=1))[n] / sum(data_raw.sum(axis=1))
width.append(createVar['width' + str(n)])
if n == 0:
continue
else:
k += createVar['width' + str(n - 1)] / 2 + createVar['width' + str(n)] / 2 + 0.05
x.append(k)
# 以下是通过频率交叉表矩阵生成一串对应堆积图每一块位置数据的数组,再把数组转化为矩阵
y_mat = []
n = 0
y_level = len(data.columns)
for p in range(data.shape[0]):
for q in range(data.shape[1]):
n += 1
y_mat.append(data.iloc[p, q])
if n == data.shape[0] * data.shape[1]:
break
elif n % y_level != 0:
y_mat.extend([0] * (len(data) - 1))
elif n % y_level == 0:
y_mat.extend([0] * len(data))
y_mat = np.array(y_mat).reshape(-1, len(data))
y_mat = pd.DataFrame(y_mat) # bar图中的y变量矩阵,每一行是一个y变量
# 通过x,y_mat中的每一行y,依次绘制每一块堆积图中的每一块图
from matplotlib import cm
cm_subsection = [level for level in range(y_level)]
colors = [cm.Pastel1(color) for color in cm_subsection]
bottom = [0] * y_mat.shape[1]
createVar = locals()
for row in range(len(y_mat)):
createVar['a' + str(row)] = y_mat.iloc[row, :]
color = colors[row % y_level]
if row % y_level == 0:
bottom = bottom = [0] * y_mat.shape[1]
if math.floor(row / y_level) == 0:
label = data.columns.name + ': ' + str(data.columns[row])
plt.bar(x, createVar['a' + str(row)],
width=width[math.floor(row / y_level)], label=label, color=color)
else:
plt.bar(x, createVar['a' + str(row)],
width=width[math.floor(row / y_level)], color=color)
else:
if math.floor(row / y_level) == 0:
label = data.columns.name + ': ' + str(data.columns[row])
plt.bar(x, createVar['a' + str(row)], bottom=bottom,
width=width[math.floor(row / y_level)], label=label, color=color)
else:
plt.bar(x, createVar['a' + str(row)], bottom=bottom,
width=width[math.floor(row / y_level)], color=color)
bottom += createVar['a' + str(row)]
plt.title(j + ' vs ' + i)
group_labels = [str(name) for name in data.index]
plt.xticks(x, group_labels, rotation=rotation)
plt.ylabel(j)
plt.legend(shadow=True, loc=location)
plt.show()from stack2dim import *stack2dim(card_client,'type','sex')import seaborn as snsimport timecard_client['age']=(pd.to_datetime(card_client['issued'])-pd.to_datetime(card_client['birth_date']))card_client['age1']=card_client['age'].map(lambda x:x.days/365)ax_age = sns.boxplot(x = 'type', y = 'age1', data = card_client,palette=sns.xkcd_palette(['gold','windows blue','coral']))ax_age.set_title('不同卡类型的 年龄 比较')plt.show()sql_card_client_trans = '''select a.card_id,a.issued,a.type,c.type as t_type,c.amount,c.balance,c.date as t_date from card as a left join disp as b on a.disp_id=b.disp_id left join trans as c on b.account_id=c.account_id where b.type="所有者" order by a.card_id,c.date'''card_client_trans = pd.read_sql_query(sql_card_client_trans,con)# print(card_client_trans.head())# # 标准化日期card_client_trans['issued']=pd.to_datetime(card_client_trans['issued'])card_client_trans['t_date']=pd.to_datetime(card_client_trans['t_date'])print(card_client_trans)# # 对帐户余额进行清洗:去掉金额单位和逗号分隔,便于计算card_client_trans['balance_1'] = card_client_trans['balance'].map(lambda x:int(x.strip('$').replace(',','')))print(card_client_trans)# # 筛选出开卡前一年的数据card_client_trans_1 = card_client_trans[card_client_trans.issued > card_client_trans.t_date][
card_client_trans.t_date >= card_client_trans.issued-datetime.timedelta(days=365)]print(card_client_trans_1)# # 分组计算余额均值card_client_trans_1['avg_balance'] = card_client_trans_1.groupby('card_id')['balance_1'].mean()card_client_trans_2 = card_client_trans_1.groupby(['type','card_id'])['balance_1'].agg([('avg_balance','mean')])# print(card_client_trans_1)# print(card_client_trans_2)card_client_trans_2.to_sql('card_client_trans_2',con)card_client_trans_3 = card_client_trans_2.reset_index()card_client_trans_3 = pd.read_sql('select * from card_client_trans_2', con)colors = ['windows blue','gold','coral']ax_balance = sns.boxplot(x='type',y='avg_balance',data=card_client_trans_3,palette=sns.xkcd_palette(colors))ax_balance.set_title('不同类型卡的持卡人在办卡前一年内的 平均帐户余额 对比')plt.show()# # # # # 先将 借、贷 转换成 更易理解的 out、incometype_dict = {'借':'out','贷':'income'}card_client_trans_1['type_1'] = card_client_trans_1.t_type.map(type_dict)# # # 将amount金额列的 金额单位 和 逗号分隔 去掉card_client_trans_1['amount_1'] = card_client_trans_1['amount'].apply(lambda x:int(x.strip('$').replace(',','')))card_client_trans_4 = card_client_trans_1.groupby(['type','card_id','type_1'])[['amount_1']].sum()card_client_trans_4.head()card_client_trans_4.to_sql('card_client_trans_4',con)card_client_trans_5 = card_client_trans_4.reset_index()card_client_trans_5.to_sql('card_client_trans_5',con)card_client_trans_6 = pd.read_sql_query('select * from card_client_trans_5 where type_1 = "income"',con)ax_amount_income = sns.boxplot(x='type',y='amount_1',data=card_client_trans_6,palette=sns.xkcd_palette(['gold','windows blue','coral']))ax_amount_income.set_title('不同类型持卡人在办卡前一年内的 平均收入 对比')plt.show()card_client_trans_7 = pd.read_sql_query('select * from card_client_trans_5 where type_1 = "out"',con)ax_amount_out = sns.boxplot(x='type',y='amount_1',data=card_client_trans_6,palette=sns.xkcd_palette(['gold','windows blue','coral']))ax_amount_out.set_title('不同类型持卡人在办卡前一年内的 平均支出 对比')plt.show()'''【信用卡客户画像总结分析】:(一)总体趋势(近六年): 1.逐年发卡量:金卡、普通卡均呈逐年上升趋势,青年卡在1997年的发行量同比降低,但总体为上升趋势; 逐年发行量占比排名为:普通卡 > 青年卡 > 金卡 2.总发卡量:总体发卡量占比排名为:普通卡 > 青年卡 > 金卡,其中普通卡占比接近总发卡量的3/4(二)基本属性特征 1.不同卡类型的 性别 比较: 普通卡和青年卡 男女性比例较为均衡,基本为1:1;金卡的男性持有者比例相较女性持有者明显更多 2.不同卡类型的 年龄 比较: 普通卡和金卡的持有者年龄主要集中在30~60岁之间;而青年卡则普遍集中在25岁以内,卡类型设计与目标对象相符 3.不同类型卡的持卡人在办卡前一年内的 平均帐户余额 对比: 金卡持有者的办卡前一年的 平均余额 是要显著高于 普通卡 和 青年卡 的,卡类型设计与目标对象相符 4.不同类型持卡人在办卡前一年内的 平均收入和平均支出 对比: 三种类型的 平均收入、平均支出 排序均符合:金卡 > 普通卡 > 青年卡,金卡的持有人群为收入较高的群体, 同样其支出情况也相应高于普通持卡人群,而青年卡,由于其持卡人群多为年龄层较小的人群,收入支出均较低, 卡类型设计与目标对象情况相符'''# # 1)在loans表中增加一列,用数字来代替贷款状态,方便后续分析loan_status = {'B':1,'D':1,'A':0,'C':2}locals()['loans']['loan_status'] = locals()['loans']['status'].map(loan_status)print(locals()['loans'])# # 2) 进行列添加'''通过disp表连接loans表和clients表'''data_1 = pd.merge(locals()['loans'],locals()['disp'],># # 1)添加地区状态信息data_5 = pd.merge(data_4,locals()['district'],left_on='district_id',right_on='A1',how='left')# # # 将需要的列筛选出来trans_clients_district = data_5[['account_id','amount','duration','payments','loan_status','type_y','sex',
'age', 'district_id','GDP','A4','A10','A11','A12','A13','A14','A15','a16']]# # 2) 客户个人经济状况信息trans_loans = pd.merge(locals()['trans'],locals()['loans'],on='account_id')print(trans_loans.head())print(list(trans_loans))'''合并后出现的列名后带_x和_y是因为合并的两张表中有有相同的列名,为示区分加上的后缀;_x表示的是合并前'trans'表中的列,_y表示的是合并前'loans'表中的列'''# # 筛选出贷款前一年的交易数据trans_loans['date_x'] = pd.to_datetime(trans_loans['date_x'])trans_loans['date_y'] = pd.to_datetime(trans_loans['date_y'])# # 将 amount_x 和 balance 由字符串类型,去掉$符号和逗号分隔,转化为数值类型trans_loans['amount_x'] = trans_loans['amount_x'].apply(lambda x:int(x.strip('$').replace(',','')))trans_loans['balance'] = trans_loans['balance'].apply(lambda x:int(x.strip('$').replace(',','')))# # 筛选出放款日期按1年内至前一天的交易记录trans_loans = trans_loans[(trans_loans['date_x']<trans_loans['date_y'])&((trans_loans['date_x']+datetime.timedelta(days=365))>trans_loans['date_y'])]# # 筛选用户前一年内结息总额trans_loans_1 = trans_loans[trans_loans['k_symbol']=='利息所得']trans_loans_1 = pd.DataFrame(trans_loans_1.groupby('account_id')['amount_x'].sum())trans_loans_1.columns = ['interest']# # 筛选在本行是否由养老金和房屋贷款trans_loans_2 = trans_loans[(trans_loans['k_symbol']=='养老金')|(trans_loans['k_symbol']=='房屋贷款')]# # 标记是否在本行有房屋贷款trans_loans_2 = pd.DataFrame(trans_loans_2.groupby('account_id')['k_symbol'].count())trans_loans_2['house_loan'] = '1'del trans_loans_2['k_symbol']# # 筛选客户一年内收入和支出(总和)print(trans_loans_2.head())trans_loans_3 = pd.DataFrame(trans_loans.pivot_table(values='amount_x',index='account_id',columns='type'))trans_loans_3.columns = ['out','income']# # 筛选客户一年内余额的均值和标准差trans_loans_4 = pd.DataFrame(trans_loans.groupby('account_id')['balance'].agg(['mean','std']))trans_loans_4.columns = ['balance_mean','balance_std']# # 合并数据data_temp = pd.merge(trans_loans_1,trans_loans_2,how='left',left_index=True,right_index=True)data_temp = pd.merge(data_temp,trans_loans_3,left_index=True,right_index=True)data_temp = pd.merge(data_temp,trans_loans_4,left_index=True,right_index=True)print(len(data_temp)) # 查看数据条数是否与贷款表条数一致data_model = pd.merge(trans_clients_district,data_temp,left_on='account_id',right_index=True)print(data_model)print(data_model.isnull().sum()/len(data_model))'''分析缺失情况、原因及处理: 总共有 列存在缺失信息 type_y:缺失信息接近75%,信息缺失过多,删除列; A12 和 A15:A12 1995年失业率 和 A15 1995犯罪率(千人) 有极小部分数据缺失,因为是连续数据,使用中位数填充; house_loan:是否有房屋贷款,缺失值为没有房屋贷款,填充字符‘0’'''del data_model['type_y']data_model['A12'].fillna(data_model['A12'].median(),inplace=True)data_model['A15'].fillna(data_model['A15'].median(),inplace=True)data_model['house_loan'].fillna('0',inplace=True)# # # <1> 因变量y = 'loan_status'# # # <2> 连续变量var_c = ['amount', 'duration', 'payments', 'GDP',
'A4', 'A10', 'A11', 'A12', 'A13', 'A14', 'A15', 'a16', 'age',
'interest', 'out', 'income', 'balance_mean',
'balance_std']# # # <3> 分类变量var_d = ['sex','house_loan']# # # # 对 sex 和 house_loan 两个分类变量二值化,方便分析data_model['sex_kind'] = data_model['sex'].map({'男':1,'女':0})data_model['house_loan_kind'] = data_model['house_loan'].map({'1':1,'0':0})import matplotlib.pyplot as pltimport seaborn as snscorr = data_model[var_c+var_d].corr()plt.figure(figsize=(12,9)) # 指定宽和高(单位:英寸)sns.heatmap(corr,vmax=1,annot=True) # vmax设置热力图颜色取值的最大值;annot设置是否显示格子数字plt.show()'''从热力图观察可知: 贷款信息、居住地信息、经济状况信息内各变量具有高相关性,对变量进行筛选及转换 1. 贷款信息中:保留amount 2. 居住地信息: 1) 采用人均GDP,即对变量进行转换; 2) 采用失业增长率 3. 经济状况信息: 1) 客户放款前近一年总结息(反应实际存款数额) 2) 收支比(反应客户消费水平) 3) 可用余额变异系数(反应客户生活状态稳定系数)'''data_model['GDP_per'] = data_model['GDP']/data_model['A4'] # 人均GDP人民生活水平的一个标准data_model['unemployment'] = data_model['A13']/data_model['A12'] # 失业增长率一定程度上反应经济增长率data_model['out/in'] = data_model['out']/data_model['income'] # 消费占收入比重,一定程度反应客户消费水平data_model['balance_a'] = data_model['balance_std']/data_model['balance_mean'] # 可用余额变异系数var = ['account_id','sex_kind','age','amount','GDP_per','unemployment','out/in','balance_a']# print(data_model)# print(list(data_model))data_model = data_model[var+[y]]for_predict = data_model[data_model[y]==2] # loan_status 为2表示状态C,即:待定data_model = data_model[data_model[y]!=2]# # 定义自变量和因变量import numpy as npX = data_model[var]Y = data_model[y]# # 将样本数据建立训练集和测试集,测试集取20%的数据from sklearn.model_selection import train_test_splitx_train, x_test, y_train, y_test = train_test_split(X, Y, test_size = 0.2, random_state = 1234)from sklearn.linear_model import LogisticRegressionLR = LogisticRegression(penalty='l1',solver='liblinear')var_temp = ['sex_kind','age','amount','GDP_per','unemployment','out/in','balance_a']x_train_temp = x_train[var_temp]clf = LR.fit(x_train_temp,y_train) # 拟合x_test_temp = x_test[var_temp]y_pred = clf.predict(x_test_temp) # 预测测试集数据test_result = pd.DataFrame({'account_id':x_test['account_id'],'y_predict':clf.predict(x_test_temp)})new_test_result = test_result.reset_index(drop=True)print(test_result) # 输出测试集中 account_id 对应的贷款状态预测print(new_test_result) # 输出测试集中 account_id 对应的贷款状态预测print(clf.coef_) #查看各变量的回归系数from sklearn.metrics import classification_reportprint(classification_report(y_test, y_pred))'''模型的精确率0.87,召回率0.84,f1_score为0.82'''from sklearn.metrics import roc_curve, aucfpr, tpr, threshold = roc_curve(y_test, y_pred)roc_auc = auc(fpr, tpr)plt.plot(fpr, tpr, color='darkorange',label='ROC curve (area = %0.2f)' % roc_auc)plt.plot([0, 1], [0, 1], color='navy', linestyle='--')plt.xlim([0.0, 1.0])plt.ylim([0.0, 1.0])plt.xlabel('False Positive Rate')plt.ylabel('True Positive Rate')plt.title('ROC_curve')plt.legend(loc="lower right")plt.show()
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69977871/viewspace-2700681/ ,如需转载,请注明出处,否则将追究法律责任。