资讯动态

别光看理论了!用Python手把手教你生成Vintage报表(附完整代码与避坑点)

发布时间:2026/9/10 2:23:36 来源:尧图企业网站定制
实战指南用Python构建信贷风控Vintage报表的完整流程信贷风控领域的Vintage报表是评估资产质量的核心工具但很多从业者在理论学习后往往卡在如何用代码实现这一关键环节。本文将手把手带你用Python从原始数据开始一步步构建专业级Vintage报表并分享实际项目中积累的宝贵经验。1. 环境准备与数据理解在开始编码前我们需要明确几个关键概念和工具准备。Vintage报表本质上是一种群组分析Cohort Analysis它追踪不同放款月份资产在各账龄阶段的表现。与常见的即期报表不同Vintage采用递延视角能够更真实反映资产质量。必备工具栈# 核心库安装 pip install pandas numpy matplotlib seaborn openpyxl典型数据源结构借据表loan_book包含贷款编号、放款日期、合同金额、产品类型等还款计划表repayment_schedule应还款日、应还本金、应还利息还款记录表repayment_record实际还款日、实还金额import pandas as pd # 示例数据结构 loan_data pd.DataFrame({ loan_id: [L1001, L1002], disbursal_date: [2023-01-15, 2023-02-20], loan_amount: [50000, 30000], product_type: [消费贷, 现金贷] }) repayment_data pd.DataFrame({ loan_id: [L1001, L1001, L1002], due_date: [2023-02-15, 2023-03-15, 2023-03-20], principal_due: [4167, 4166, 2500], interest_due: [500, 450, 300] })2. 数据处理关键步骤2.1 数据清洗与合并原始数据往往存在各种问题需要先进行标准化处理def clean_data(loan_df, repay_df): # 日期格式标准化 loan_df[disbursal_date] pd.to_datetime(loan_df[disbursal_date]) repay_df[due_date] pd.to_datetime(repay_df[due_date]) # 合并数据 merged_df pd.merge(repay_df, loan_df, onloan_id, howleft) # 处理空值 merged_df.fillna({principal_due:0, interest_due:0}, inplaceTrue) return merged_df2.2 逾期状态判定逻辑逾期计算是Vintage的核心需要明确定义几个关键时点观察日报表生成日期如2023-12-31MOBMonth on Book放款后的月份数逾期天数应还日与实际还款日的差值def calculate_overdue_status(row, observation_date): obs_date pd.to_datetime(observation_date) due_date row[due_date] if pd.isna(row[actual_payment_date]): overdue_days (obs_date - due_date).days else: overdue_days (row[actual_payment_date] - due_date).days if overdue_days 0: return M0 elif 1 overdue_days 30: return M1 elif 31 overdue_days 60: return M2 else: return M33. Vintage报表核心计算3.1 账龄MOB计算每个放款月需要计算其在各个月份的表现def calculate_mob(df, observation_date): obs_date pd.to_datetime(observation_date) df[mob] ((obs_date - df[disbursal_date]).dt.days // 30) 1 df[mob] df[mob].clip(upper12) # 假设产品期限最长12个月 return df3.2 逾期金额汇总按放款月份和MOB分组计算逾期金额def calculate_vintage(df): vintage_result df.pivot_table( indexdisbursal_date, columnsmob, valuesoverdue_amount, aggfuncsum, fill_value0 ) # 计算各月放款总额 loan_amounts df.groupby(disbursal_date)[loan_amount].first() # 计算逾期率 vintage_rate vintage_result.div(loan_amounts, axis0) * 100 return vintage_rate4. 可视化呈现技巧4.1 热力图展示使用seaborn生成直观的热力图import seaborn as sns import matplotlib.pyplot as plt def plot_vintage_heatmap(vintage_df): plt.figure(figsize(12, 8)) sns.heatmap(vintage_df, annotTrue, fmt.2f, cmapYlOrRd, linewidths.5, cbar_kws{label: 逾期率(%)}) plt.title(Vintage Analysis - M2逾期率, pad20) plt.xlabel(MOB (账龄月份)) plt.ylabel(放款月份) plt.tight_layout() plt.show()4.2 趋势线分析补充折线图展示不同放款月的表现def plot_vintage_lines(vintage_df): plt.figure(figsize(12, 6)) for month in vintage_df.index: plt.plot(vintage_df.columns, vintage_df.loc[month], markero, labelmonth.strftime(%Y-%m)) plt.title(各放款月逾期率趋势, pad15) plt.xlabel(MOB) plt.ylabel(M2逾期率(%)) plt.legend(bbox_to_anchor(1.05, 1), locupper left) plt.grid(True, alpha0.3) plt.show()5. 实战中的常见问题与解决方案5.1 月末时点处理金融系统通常采用月末时点观测需要特别注意# 获取月末日期 def get_month_end(date): next_month date.replace(day28) timedelta(days4) return next_month - timedelta(daysnext_month.day) # 在计算MOB时使用 df[observation_date] df[due_date].apply(get_month_end)5.2 逾期口径一致性确保团队对逾期定义达成共识口径类型计算方式适用场景当前逾期观察日未结清实时监控曾经逾期历史最高逾期状态客户全生命周期分析5.3 性能优化技巧处理大规模数据时的建议向量化计算避免使用apply改用NumPy/Pandas内置方法分块处理对大数据使用chunksize参数内存优化转换数据类型减少内存占用# 内存优化示例 dtypes { loan_id: category, product_type: category, principal_due: float32 } df pd.read_csv(large_file.csv, dtypedtypes)6. 进阶应用迁徙率与滚动率6.1 迁徙率计算实现def calculate_flow_rate(overdue_df): # 计算各逾期状态间的转化 flow_rate overdue_df.groupby([month, prev_status])[current_status].value_counts(normalizeTrue) return flow_rate.unstack().fillna(0)6.2 滚动率分析def calculate_roll_rate(df): # 计算客户逾期状态恶化比例 roll_rate df.pivot_table(indexprevious_status, columnscurrent_status, valuesloan_id, aggfunccount, fill_value0) roll_rate roll_rate.div(roll_rate.sum(axis1), axis0) return roll_rate在实际项目中我们发现最耗时的环节往往是数据清洗和逾期状态判断。一个实用的建议是先将核心逻辑实现为小规模原型验证无误后再扩展到全量数据。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价