基于信用卡逾期数据的Spark数据处理与分析

大数据学习路线图

【版权声明】版权所有,严禁转载,严禁用于商业用途,侵权必究。
作者:厦门大学信息学院计算机科学系2019级研究生 陈绍纬
指导老师:厦门大学数据库实验室 林子雨 博士/副教授
相关教材:林子雨、郑海山、赖永炫编著《Spark编程基础(Python版)》(访问教材官网
相关案例:基于Python语言的Spark数据处理分析案例集锦(PySpark)

本案例以和鲸社区的信用卡评分模型构建数据为数据集,以Python为编程语言,使用大数据框架Spark对数据进行处理分析,并对分析结果进行可视化。

一、实验环境

(1)Linux:Ubuntu 18.04
(2)Hadoop3.1.3 (查看安装教程
(3)Python:3.6
(4)Spark:2.4.0 (查看安装教程
(5)Jupyter Notebook (查看安装和使用方法教程
安装上述环境后,为了支持Python可视化分析,还需要执行如下命令安装pyecharts组件:

  1. pip3 install pyecharts==1.7.0
Shell 命令

二、数据集

1. 数据集下载

本次实验数据集来自和鲸社区的信用卡评分模型构建数据,以数据集cs-training.csv为分析主体,其中共有15万条记录,11列属性。
每个数据包含以下字段:
字段名称 字段含义 例子
(1)SeriousDlqin2yrs 是否逾期 0,1
(2)RevolvingUtilizationOfUnsecuredLines 信用卡和个人信贷额度的总余额 0.766126609
(3)Age 年龄 45,20,30
(4)NumberOfTime30-59DaysPastDueNotWorse 借款人逾期30-59天的次数 0,2,3
(5)DebtRatio 负债比率 0.802982129
(6)MonthlyIncome 月收入 9120,3000
(7)NumberOfOpenCreditLinesAndLoans 未偿还贷款数量 ,0,4,13
(8)NumberOfTimes90DaysLate 借款人逾期90天以上的次数 0,1,3
(9)NumberRealEstateLoansOrLines 房地产贷款的数量 3,6
(10)NumberOfTime60-89DaysPastDueNotWorse 借款人逾期60-89天的次数 0,3
(11)NumberOfDependents 家庭中的家属人数 0,1,3

2. 数据预处理

本次实验采用pandas库对数据进行预处理。在实验中,不对信用卡和个人信贷额度的总余额、负债比率、未偿还贷款数量、逾期90天以上的次数这4个属性进行处理分析。
具体处理步骤如下:
(1)读取数据
(2)查看数据是否具有重复值,去除重复值
(3)查看各字段缺失率,缺失值以均值填充
(4)选取要研究的属性,删除不研究的属性
(5)保存文件到本地
使用代码文件data_preprocessing.py对数据预处理,运行data_preprocessing.py文件的步骤如下:

  1. python ~/OverDue/data_preprocessing.py
Shell 命令

代码文件data_preprocessing.py的内容如下:

  1. import pandas as pd
  2.  
  3. # 读取数据
  4. df = pd.read_csv("~/Desktop/cs-training.csv")
  5.  
  6. # 去除重复值
  7. df.duplicated()
  8. df.drop_duplicates()
  9.  
  10. # 查看各字段缺失率
  11. df.info()
  12. # 缺失值按均值填充
  13. for col in list(df.columns[df.isnull().sum() > 0]):
  14. mean_val = df[col].mean()
  15. df[col].fillna(mean_val, inplace=True)
  16.  
  17. # 删除不分析的列
  18. columns = ["RevolvingUtilizationOfUnsecuredLines","DebtRatio","NumberOfOpenCreditLinesAndLoans","NumberOfTimes90DaysLate"]
  19. df.drop(columns,axis=1,inplace=True)
  20.  
  21. # 保存到本地
  22. df.to_csv("~/OverDue/data.csv")
Python

3. 将文件上传至HDFS文件系统

将本地文件系统的数据集“data.csv”上传到HDFS文件系统中,路径为“/OverDue/data.csv”。具体命令如下:

  1. # 启动Hadoop
  2. cd /usr/local/hadoop
  3. ./sbin/start-dfs.sh
  4. # 在HDFS文件系统中创建/OverDue目录
  5. ./bin/hdfs dfs -mkdir /data
  6. # 上传文件到HDFS文件系统中
  7. ./bin/hdfs dfs -put ~/OverDue/data.csv /OverDue/data.csv
Shell 命令

三、使用Spark对数据处理分析

我们将采用Python编程语言和Spark大数据框架对数据集“data.csv”进行处理分析,具体步骤如下:
(1)读取HDFS文件系统中的数据文件,生成DataFrame
(2)修改列名
(3)本次信用卡逾期的总体统计
(4)年龄与本次信用卡逾期的结合统计
(5)两次逾期记录与本次信用卡逾期的结合统计
(6)房产抵押数量与本次信用卡逾期的结合统计
(7)家属人数与本次信用卡逾期的结合统计
(8)月收入与本次信用卡逾期的结合统计
(9)将统计数据返回给数据可视化文件data_web.py
代码文件data_analysis.py的内容如下:

  1. from pyspark.sql import SparkSession
  2. from pyspark import SparkContext,SparkConf
  3. from pyspark.sql import Row
  4. from pyspark.sql.types import *
  5. from pyspark.sql import functions
  6.  
  7. def analyse(filename):
  8. # 读取数据
  9. spark = SparkSession.builder.config(conf = SparkConf()).getOrCreate()
  10. df = spark.read.format("csv").option("header","true").load(filename)
  11.  
  12. # 修改列名
  13. df = df.withColumnRenamed('SeriousDlqin2yrs','y')
  14. df = df.withColumnRenamed('NumberOfTime30-59DaysPastDueNotWorse','30-59days')
  15. df = df.withColumnRenamed('NumberOfTime60-89DaysPastDueNotWorse','60-89days')
  16. df = df.withColumnRenamed('NumberRealEstateLoansOrLines','RealEstateLoans')
  17. df = df.withColumnRenamed('NumberOfDependents','families')
  18.  
  19. # 返回data_web.py的数据列表
  20. all_list = []
  21. # 本次信用卡逾期分析
  22. # 共有逾期10026人,139974没有逾期,总人数150000
  23. total_y = []
  24. for i in range(2):
  25. total_y.append(df.filter(df['y'] == i).count())
  26. all_list.append(total_y)
  27.  
  28. # 年龄分析
  29. df_age = df.select(df['age'],df['y'])
  30. agenum = []
  31. bin = [0,30,45,60,75,100]
  32. # 统计各个年龄段的人口
  33. for i in range(5):
  34. agenum.append(df_age.filter(df['age'].between(bin[i],bin[i+1])).count())
  35. all_list.append(agenum)
  36. # 统计各个年龄段逾期与不逾期的数量
  37. age_y = []
  38. for i in range(5):
  39. y0 = df_age.filter(df['age'].between(bin[i],bin[i+1])).\
  40. filter(df['y']=='0').count()
  41. y1 = df_age.filter(df['age'].between(bin[i],bin[i+1])).\
  42. filter(df['y']=='1').count()
  43. age_y.append([y0,y1])
  44. all_list.append(age_y)
  45.  
  46. # 有逾期记录的人的本次信用卡逾期数量
  47. df_pastDue = df.select(df['30-59days'],df['60-89days'],df['y'])
  48. # 30-59有23982人,4985逾期,18997不逾期
  49. numofpastdue = []
  50. numofpastdue.append(df_pastDue.filter(df_pastDue['30-59days'] > 0).count())
  51. y_numofpast1 = []
  52. for i in range(2):
  53. x = df_pastDue.filter(df_pastDue['30-59days'] > 0).\
  54. filter(df_pastDue['y'] == i).count()
  55. y_numofpast1.append(x)
  56. # 60-89有7604人,2770逾期,4834不逾期
  57. numofpastdue.append(df_pastDue.filter(df_pastDue['60-89days'] > 0).count())
  58. y_numofpast2 = []
  59. for i in range(2):
  60. x = df_pastDue.filter(df_pastDue['60-89days'] > 0).\
  61. filter(df_pastDue['y'] == i).count()
  62. y_numofpast2.append(x)
  63. # 两个记录都有的人有4393人,逾期1907,不逾期2486
  64. numofpastdue.append(df_pastDue.filter(df_pastDue['30-59days'] > 0).
  65. filter(df_pastDue['60-89days'] > 0).count())
  66. y_numofpast3 = []
  67. for i in range(2):
  68. x = df_pastDue.filter(df_pastDue['30-59days'] > 0).\
  69. filter(df_pastDue['60-89days'] > 0).filter(df_pastDue['y'] == i).count()
  70. y_numofpast3.append(x)
  71. all_list.append(numofpastdue)
  72. all_list.append(y_numofpast1)
  73. all_list.append(y_numofpast2)
  74. all_list.append(y_numofpast3)
  75.  
  76. # 房产抵押数量分析
  77. df_Loans = df.select(df['RealEstateLoans'],df['y'])
  78. # 有无抵押房产人数情况
  79. numofrealandnoreal = []
  80. numofrealandnoreal.append(df_Loans.filter(df_Loans['RealEstateLoans']==0).count())
  81. numofrealandnoreal.append(df_Loans.filter(df_Loans['RealEstateLoans']>0).count())
  82. all_list.append(numofrealandnoreal)
  83. ## 房产无抵押共有56188人,逾期4672人,没逾期51516人
  84. norealnum = []
  85. for i in range(2):
  86. x = df_Loans.filter(df_Loans['RealEstateLoans']==0).\
  87. filter(df_Loans['y'] == i).count()
  88. norealnum.append(x)
  89. all_list.append(norealnum)
  90. # 房产抵押共有93812人,逾期5354人,不逾期88458人
  91. realnum = []
  92. for i in range(2):
  93. x = df_Loans.filter(df_Loans['RealEstateLoans']>0).\
  94. filter(df_Loans['y'] == i).count()
  95. realnum.append(x)
  96. all_list.append(realnum)
  97.  
  98. # 家属人数分析
  99. df_families = df.select(df['families'],df['y'])
  100. # 有无家属人数统计
  101. nofamiliesAndfamilies = []
  102. nofamiliesAndfamilies.append(df_families.filter(df_families['families']>0).count())
  103. nofamiliesAndfamilies.append(df_families.filter(df_families['families']==0).count())
  104. all_list.append(nofamiliesAndfamilies)
  105. # 有家属59174人,逾期4752人,没逾期54422人
  106. y_families = []
  107. y_families.append(df_families.filter(df_families['families']>0).
  108. filter(df_families['y']==0).count())
  109. y_families.append(df_families.filter(df_families['families']>0).
  110. filter(df_families['y']==1).count())
  111. all_list.append(y_families)
  112. # 没家属90826人,逾期5274人,没逾期85552人
  113. y_nofamilies = []
  114. y_nofamilies.append(df_families.filter(df_families['families']==0).
  115. filter(df_families['y']==0).count())
  116. y_nofamilies.append(df_families.filter(df_families['families']==0).
  117. filter(df_families['y']==1).count())
  118. all_list.append(y_nofamilies)
  119.  
  120. # 月收入分析
  121. df_income = df.select(df['MonthlyIncome'],df['y'])
  122. # 获取平均值,其中先返回Row对象,再获取其中均值
  123. mean_income = df_income.agg(functions.avg(df_income['MonthlyIncome'])).head()[0]
  124. # 收入分布,105854人没超过均值6670,44146人超过均值6670
  125. numofMeanincome = []
  126. numofMeanincome.append(df_income.filter(df['MonthlyIncome'] < mean_income).count())
  127. numofMeanincome.append(df_income.filter(df['MonthlyIncome'] > mean_income).count())
  128. all_list.append(numofMeanincome)
  129. # 未超过均值的逾期情况分析,97977人没逾期,7877人逾期
  130. y_NoMeanIncome = []
  131. y_NoMeanIncome.append(df_income.filter(df['MonthlyIncome'] < mean_income).filter(df['y']==0).count())
  132. y_NoMeanIncome.append(df_income.filter(df['MonthlyIncome'] < mean_income).filter(df['y']==1).count())
  133. all_list.append(y_NoMeanIncome)
  134. # 超过均值的逾期情况分析,41997人没逾期,2149人逾期
  135. y_MeanIncome = []
  136. y_MeanIncome.append(df_income.filter(df['MonthlyIncome'] > mean_income).filter(df['y']==0).count())
  137. y_MeanIncome.append(df_income.filter(df['MonthlyIncome'] > mean_income).filter(df['y']==1).count())
  138. all_list.append(y_MeanIncome)
  139.  
  140. # 数据可视化data_web.py
  141. return all_list
Python

四、数据可视化

1. 可视化工具与代码

选择使用python第三方库pyecharts作为可视化工具,其中pyecharts版本为1.7.0。采用其中的柱状图和饼图来详细展现分析结果。
代码文件data_web.py的内容如下:

  1. from pyecharts.charts import Bar
  2. from pyecharts.charts import Pie
  3. from pyecharts.charts import Page
  4. from pyecharts import options as opts
  5. import data_analysis
  6. # --------总体逾期人数情况--------------
  7. def draw_total(total_list):
  8. attr = ["未逾期人数", "逾期人数"]
  9. pie = (
  10. Pie()
  11. .add("总体逾期人数", [list(z) for z in zip(attr,total_list)])
  12. .set_global_opts(title_opts=opts.TitleOpts(title="总体逾期人数分布"))
  13. .set_series_opts(
  14. tooltip_opts=opts.TooltipOpts(trigger="item", formatter="{a} <br/>{b}: {c} ({d}%)"),
  15. label_opts=opts.LabelOpts(formatter="{b}: {c} ({d}%)")
  16. )
  17. )
  18. return pie
  19. # --------年龄与逾期人数情况--------------
  20. def draw_age(age_list,y_ageList):
  21. total_pie = draw_total(all_list[0])
  22. attr = ["0-30", "30-45", "45-60", "60-75", "75-100"]
  23. y0_agenum = []
  24. y1_agenum = []
  25. for i in range(5):
  26. y0_agenum.append(y_ageList[i][0])
  27. y1_agenum.append(y_ageList[i][1])
  28.  
  29. bar = (
  30. Bar()
  31. .add_xaxis(attr)
  32. .add_yaxis("人数分布", age_list)
  33. .add_yaxis("未逾期人数分布", y0_agenum)
  34. .add_yaxis("逾期人数分布", y1_agenum)
  35. .set_global_opts(title_opts=opts.TitleOpts(title="各年龄段逾期情况"))
  36. )
  37. attr = ["未逾期","逾期"]
  38. pie1 = (
  39. Pie()
  40. .add("0-30年龄段", [list(z) for z in zip(attr,y_ageList[0])])
  41. .set_global_opts(title_opts=opts.TitleOpts(title="0-30年龄段逾期情况"))
  42. .set_series_opts(
  43. tooltip_opts=opts.TooltipOpts(trigger="item", formatter="{a} <br/>{b}: {c} ({d}%)"),
  44. label_opts=opts.LabelOpts(formatter="{b}: {c} ({d}%)")
  45. )
  46. )
  47. pie2 = (
  48. Pie()
  49. .add("30-45年龄段", [list(z) for z in zip(attr,y_ageList[1])])
  50. .set_global_opts(title_opts=opts.TitleOpts(title="30-45年龄段逾期情况"))
  51. .set_series_opts(
  52. tooltip_opts=opts.TooltipOpts(trigger="item", formatter="{a} <br/>{b}: {c} ({d}%)"),
  53. label_opts=opts.LabelOpts(formatter="{b}: {c} ({d}%)")
  54. )
  55. )
  56. pie3 = (
  57. Pie()
  58. .add("45-60年龄段", [list(z) for z in zip(attr,y_ageList[2])])
  59. .set_global_opts(title_opts=opts.TitleOpts(title="45-60年龄段逾期情况"))
  60. .set_series_opts(
  61. tooltip_opts=opts.TooltipOpts(trigger="item", formatter="{a} <br/>{b}: {c} ({d}%)"),
  62. label_opts=opts.LabelOpts(formatter="{b}: {c} ({d}%)")
  63. )
  64. )
  65. pie4 = (
  66. Pie()
  67. .add("60-75年龄段", [list(z) for z in zip(attr,y_ageList[3])])
  68. .set_global_opts(title_opts=opts.TitleOpts(title="60-75年龄段逾期情况"))
  69. .set_series_opts(
  70. tooltip_opts=opts.TooltipOpts(trigger="item", formatter="{a} <br/>{b}: {c} ({d}%)"),
  71. label_opts=opts.LabelOpts(formatter="{b}: {c} ({d}%)")
  72. )
  73. )
  74. pie5 = (
  75. Pie()
  76. .add("75-100年龄段", [list(z) for z in zip(attr,y_ageList[4])])
  77. .set_global_opts(title_opts=opts.TitleOpts(title="75-100年龄段逾期情况"))
  78. .set_series_opts(
  79. tooltip_opts=opts.TooltipOpts(trigger="item", formatter="{a} <br/>{b}: {c} ({d}%)"),
  80. label_opts=opts.LabelOpts(formatter="{b}: {c} ({d}%)")
  81. )
  82. )
  83.  
  84. page = Page()
  85. page.add(bar)
  86. page.add(total_pie)
  87. page.add(pie1)
  88. page.add(pie2)
  89. page.add(pie3)
  90. page.add(pie4)
  91. page.add(pie5)
  92. page.render('age_OverDue.html')
  93.  
  94. # --------逾期记录与逾期人数情况--------------
  95. def draw_pastdue(numofpastdue,pastdue1num,pastdue2num,pastdue12num):
  96. total_pie = draw_total(all_list[0])
  97. attr = ["有30-59days逾期记录的人数", "有60-89days逾期记录的人数", "有长短期逾期记录的人数"]
  98. bar = (
  99. Bar()
  100. .add_xaxis(attr)
  101. .add_yaxis("人数", numofpastdue)
  102. .set_global_opts(title_opts=opts.TitleOpts(title="有逾期记录的人数"))
  103. )
  104. attr = ["未逾期","逾期"]
  105. pie1 = (
  106. Pie()
  107. .add("有短期逾期记录的人的逾期情况", [list(z) for z in zip(attr,pastdue1num)])
  108. .set_global_opts(title_opts=opts.TitleOpts(title="有短期逾期记录的人的逾期情况"))
  109. .set_series_opts(
  110. tooltip_opts=opts.TooltipOpts(trigger="item", formatter="{a} <br/>{b}: {c} ({d}%)"),
  111. label_opts=opts.LabelOpts(formatter="{b}: {c} ({d}%)")
  112. )
  113. )
  114. pie2 = (
  115. Pie()
  116. .add("有长期逾期记录的人的逾期情况", [list(z) for z in zip(attr,pastdue2num)])
  117. .set_global_opts(title_opts=opts.TitleOpts(title="有长期逾期记录的人的逾期情况"))
  118. .set_series_opts(
  119. tooltip_opts=opts.TooltipOpts(trigger="item", formatter="{a} <br/>{b}: {c} ({d}%)"),
  120. label_opts=opts.LabelOpts(formatter="{b}: {c} ({d}%)")
  121. )
  122. )
  123. pie3 = (
  124. Pie()
  125. .add("长短期逾期记录都有的人的逾期情况", [list(z) for z in zip(attr,pastdue12num)])
  126. .set_global_opts(title_opts=opts.TitleOpts(title="长短期逾期记录都有的人的逾期情况"))
  127. .set_series_opts(
  128. tooltip_opts=opts.TooltipOpts(trigger="item", formatter="{a} <br/>{b}: {c} ({d}%)"),
  129. label_opts=opts.LabelOpts(formatter="{b}: {c} ({d}%)")
  130. )
  131. )
  132. page = Page()
  133. page.add(bar)
  134. page.add(total_pie)
  135. page.add(pie1)
  136. page.add(pie2)
  137. page.add(pie3)
  138. page.render('pastDue_OverDue.html')
  139. # --------房产抵押与逾期人数情况--------------
  140. def draw_realestateLoans(numofrealornoreal,y_norealnum,y_realnum):
  141. total_pie = draw_total(all_list[0])
  142. attr = ["无房产抵押人数", "有房产抵押人数"]
  143. bar = (
  144. Bar()
  145. .add_xaxis(attr)
  146. .add_yaxis("人数", numofrealornoreal)
  147. .set_global_opts(title_opts=opts.TitleOpts(title="房产抵押人数分布"))
  148. )
  149. attr = ["未逾期","逾期"]
  150. pie1 = (
  151. Pie()
  152. .add("无房产抵押的人的逾期情况", [list(z) for z in zip(attr,y_norealnum)])
  153. .set_global_opts(title_opts=opts.TitleOpts(title="无房产抵押的人的逾期情况"))
  154. .set_series_opts(
  155. tooltip_opts=opts.TooltipOpts(trigger="item", formatter="{a} <br/>{b}: {c} ({d}%)"),
  156. label_opts=opts.LabelOpts(formatter="{b}: {c} ({d}%)")
  157. )
  158. )
  159. pie2 = (
  160. Pie()
  161. .add("有房产抵押的人的逾期情况", [list(z) for z in zip(attr,y_realnum)])
  162. .set_global_opts(title_opts=opts.TitleOpts(title="有房产抵押的人的逾期情况"))
  163. .set_series_opts(
  164. tooltip_opts=opts.TooltipOpts(trigger="item", formatter="{a} <br/>{b}: {c} ({d}%)"),
  165. label_opts=opts.LabelOpts(formatter="{b}: {c} ({d}%)")
  166. )
  167. )
  168. page = Page()
  169. page.add(bar)
  170. page.add(total_pie)
  171. page.add(pie1)
  172. page.add(pie2)
  173. page.render('realestateLoans_OverDue.html')
  174.  
  175. # --------家属人数与逾期人数情况--------------
  176. def draw_families(nofamiliesAndfamilies,y_families,y_nofamilies):
  177. total_pie = draw_total(all_list[0])
  178. attr = ["有家属人数", "无家属人数"]
  179. bar = (
  180. Bar()
  181. .add_xaxis(attr)
  182. .add_yaxis("人数", nofamiliesAndfamilies)
  183. .set_global_opts(title_opts=opts.TitleOpts(title="有无家属人数分布"))
  184. )
  185. attr = ["未逾期","逾期"]
  186. pie1 = (
  187. Pie()
  188. .add("无家属的人的逾期情况", [list(z) for z in zip(attr,y_nofamilies)])
  189. .set_global_opts(title_opts=opts.TitleOpts(title="无家属的人的逾期情况"))
  190. .set_series_opts(
  191. tooltip_opts=opts.TooltipOpts(trigger="item", formatter="{a} <br/>{b}: {c} ({d}%)"),
  192. label_opts=opts.LabelOpts(formatter="{b}: {c} ({d}%)")
  193. )
  194. )
  195. pie2 = (
  196. Pie()
  197. .add("有家属的人的逾期情况", [list(z) for z in zip(attr,y_families)])
  198. .set_global_opts(title_opts=opts.TitleOpts(title="有家属的人的逾期情况"))
  199. .set_series_opts(
  200. tooltip_opts=opts.TooltipOpts(trigger="item", formatter="{a} <br/>{b}: {c} ({d}%)"),
  201. label_opts=opts.LabelOpts(formatter="{b}: {c} ({d}%)")
  202. )
  203. )
  204. page = Page()
  205. page.add(bar)
  206. page.add(total_pie)
  207. page.add(pie1)
  208. page.add(pie2)
  209. page.render('families_OverDue.html')
  210. # --------月收入与逾期人数情况--------------
  211. def draw_income(numofMeanincome,y_NoMeanIncome,y_MeanIncome):
  212. total_pie = draw_total(all_list[0])
  213. attr = ["未超过均值收入人数", "超过均值收入人数"]
  214. bar = (
  215. Bar()
  216. .add_xaxis(attr)
  217. .add_yaxis("人数", numofMeanincome)
  218. .set_global_opts(title_opts=opts.TitleOpts(title="有无超过均值收入人数分布"))
  219. )
  220. attr = ["未逾期","逾期"]
  221. pie1 = (
  222. Pie()
  223. .add("未超过均值收入的人的逾期情况", [list(z) for z in zip(attr,y_NoMeanIncome)])
  224. .set_global_opts(title_opts=opts.TitleOpts(title="未超过均值收入的人的逾期情况"))
  225. .set_series_opts(
  226. tooltip_opts=opts.TooltipOpts(trigger="item", formatter="{a} <br/>{b}: {c} ({d}%)"),
  227. label_opts=opts.LabelOpts(formatter="{b}: {c} ({d}%)")
  228. )
  229. )
  230. pie2 = (
  231. Pie()
  232. .add("超过均值收入的人的逾期情况", [list(z) for z in zip(attr,y_MeanIncome)])
  233. .set_global_opts(title_opts=opts.TitleOpts(title="超过均值收入的人的逾期情况"))
  234. .set_series_opts(
  235. tooltip_opts=opts.TooltipOpts(trigger="item", formatter="{a} <br/>{b}: {c} ({d}%)"),
  236. label_opts=opts.LabelOpts(formatter="{b}: {c} ({d}%)")
  237. )
  238. )
  239. page = Page()
  240. page.add(bar)
  241. page.add(total_pie)
  242. page.add(pie1)
  243. page.add(pie2)
  244. page.render('meanIncome_OverDue.html')
  245.  
  246. if __name__ == '__main__':
  247. print("开始总程序")
  248. Filename = "/OverDue/data.csv"
  249. all_list = data_analysis.analyse(Filename)
  250. # 年龄与是否逾期情况
  251. draw_age(all_list[1],all_list[2])
  252. # 有无逾期记录与是否逾期情况
  253. draw_pastdue(all_list[3],all_list[4],all_list[5],all_list[6])
  254. # 房产抵押数量与是否逾期情况
  255. draw_realestateLoans(all_list[7],all_list[8],all_list[9])
  256. # 家属人数与是否逾期情况
  257. draw_families(all_list[10],all_list[11],all_list[12])
  258. # 月收入与是否逾期情况
  259. draw_income(all_list[13],all_list[14],all_list[15])
  260. print("结束总程序")
Python

2. 数据可视化结果

我们从年龄、两次逾期记录、房产抵押数量、家属人数、月收入5个方面来分析统计,得到5个直观的结果统计界面。
将代码文件data_web.py提交到spark-submit后,会在OverDue目录下得到5个.html文件,点击浏览就会得到分析结果。
运行步骤如下:

  1. # 进入OverDue目录
  2. cd ~/OverDue
  3. # 提交data_web.py文件到spark-submit
  4. /usr/local/spark/bin/spark-submit --master local ~/OverDue/data_web.py
Shell 命令

可视化结果如下:

2.1 总体

2.2 年龄

从年龄方面分析,可以得到越年轻的人,逾期的可能性越大。其中0-30年龄段逾期占比11.56%;30-45年龄段逾期占比9.38%;45-60年龄段逾期占比6.9%;60-75年龄段逾期占比3.39%;75-100年龄段占比2.01%。

2.3 逾期记录


通过上方四个图,我们可以清楚的发现有逾期记录的人在本次信用卡逾期的占比大幅度上升。其中,有短期逾期记录的人,本次信用卡逾期占比20.79%;有长期逾期记录的人,本次信用卡逾期占比36.43%;有长短期逾期记录的人,本次信用卡逾期占比43.41%。针对此现象,我们可以得出,如果一个人具有逾期记录,那他信用卡逾期的可能性很大,需要慎重考虑借款给他。

2.4 房产抵押数量


通过上方三个图,可以得到有房产抵押的人本次信用卡逾期占比5.71%,低于总体逾期占比,无房产抵押的人本次信用卡逾期占比8.31%,高于总体逾期占比。可以得出有房产抵押的人,逾期可能性稍微低一点。但占比差不多,也可以说明房产抵押这个属性对于信用卡逾期影响不大。

2.5 家属人数


通过上方三个图,我们可以得出在家属人数属性中,有家属的人逾期可能性较大。其中无家属的人逾期占比5.81%;有家属的人逾期占比8.03%。

2.6 月收入


通过上方三个图,我们可以得到月收入超过人均收入的人逾期的可能性小,其偿还贷款的能力强,月收入低于人均收入的人逾期可能性大。其中,未超过人均收入的人逾期占比7.44%;超过人均收入的人逾期占比4.87%。

3. 分析结果总结

我们从5个方面分析逾期的相关因素。通过图表,可以得到年龄和逾期记录对于本次信用卡是否逾期的影响很大。年龄越小和具有逾期记录的人,信用卡逾期的可能性越大。而是否有房产抵押、是否有家属、是否月收入超过人均水平,具有较小的相关性,影响不是很大。
这些分析结果,可以在后期对其训练预测模型时,提供有效的帮助,如提高年龄和逾期记录的权重,降低其他属性的权重。