如何用Matplotlib和Python创建贷款组合分析用分组堆叠比例拆分柱状图?
实现贷款组合风险敞口的复杂堆叠拆分柱状图
以下是满足所有需求的完整实现代码,同时附带关键逻辑的详细说明:
import pandas as pd import numpy as np import matplotlib.pyplot as plt import matplotlib.ticker as ticker def loan_analysis(data, ax): # 1. 数据预处理:转换单位、排序优先级、计算占比 chart_data = data.copy() # 统一转换为百万美元单位 chart_data[['FMV', 'Amount']] = chart_data[['FMV', 'Amount']] / 1000000 # 定义Level的优先级顺序,确保堆叠严格按1st→2nd→3rd排序 level_order = ['1st', '2nd', '3rd'] chart_data['Level'] = pd.Categorical(chart_data['Level'], categories=level_order, ordered=True) chart_data = chart_data.sort_values(['Loan', 'Level', 'Fund']) # 计算每个Loan-Level组合的总金额,用于拆分柱子宽度 chart_data['Level_Total'] = chart_data.groupby(['Loan', 'Level'])['Amount'].transform('sum') # 计算单个Fund在对应Level中的金额占比,确定柱子宽度比例 chart_data['Fund_Share'] = chart_data['Amount'] / chart_data['Level_Total'] # 获取唯一贷款列表,分配x轴位置 unique_loans = chart_data['Loan'].unique() loan_x_pos = np.arange(len(unique_loans)) bar_width = 0.3 # 风险敞口柱子的总宽度 fmv_bar_width = 0.2 # FMV柱子的宽度 fmv_x_offset = 0.4 # FMV柱子相对于贷款位置的偏移量 # 为每个Fund分配唯一配色 unique_funds = chart_data['Fund'].unique() fund_colors = plt.cm.tab10(np.linspace(0, 1, len(unique_funds))) fund_color_map = dict(zip(unique_funds, fund_colors)) # 2. 绘制风险敞口的复杂堆叠拆分柱状图 for loan_idx, loan in enumerate(unique_loans): loan_data = chart_data[chart_data['Loan'] == loan] bottom = 0 # 堆叠的底部初始位置 # 按Level优先级顺序处理堆叠 for level in level_order: level_data = loan_data[loan_data['Level'] == level] if level_data.empty: continue level_total = level_data['Level_Total'].iloc[0] current_x = loan_x_pos[loan_idx] - bar_width/2 # 当前Level的起始x坐标 # 遍历每个Fund,绘制对应宽度的分段柱子 for _, row in level_data.iterrows(): fund_bar_width = bar_width * row['Fund_Share'] ax.bar(current_x, row['Amount'], width=fund_bar_width, bottom=bottom, color=fund_color_map[row['Fund']], edgecolor='white') # 更新下一个Fund的起始x坐标 current_x += fund_bar_width # 更新堆叠的底部位置 bottom += level_total # 3. 绘制FMV柱状图 fmv_data = chart_data.drop_duplicates(subset=['Loan']).set_index('Loan')['FMV'] ax.bar(loan_x_pos + fmv_x_offset, fmv_data.values, width=fmv_bar_width, color='darkgreen', edgecolor='white', label='FMV') # 4. 图表格式化 ax.set_xticks(loan_x_pos) ax.set_xticklabels(unique_loans) ax.set_xlabel('LOAN') # 自定义y轴为百万美元格式 def dollar_fmt(x, pos): return f'${x:.2f}M' ax.yaxis.set_major_formatter(ticker.FuncFormatter(dollar_fmt)) # 网格与样式优化 ax.grid(axis='y', which='major', color='lightblue', lw=0.1) ax.grid(axis='y', which='minor', color='lightblue', lw=0.05) ax.tick_params(which='minor', length=0) ax.tick_params(axis='x', length=0) ax.tick_params(axis='y', length=0) ax.minorticks_on() # 手动构建图例,包含Fund颜色标识和FMV项 fund_patches = [plt.Rectangle((0,0),1,1, color=fund_color_map[fund]) for fund in unique_funds] ax.legend(fund_patches + [plt.Rectangle((0,0),1,1, color='darkgreen')], list(unique_funds) + ['FMV'], loc='upper right') return ax # 示例数据 sample_data = pd.DataFrame({ "Loan": ["123", "123", "124", "124", "124", "124"], "Level": ["1st", "1st", "1st", "2nd", "2nd", "3rd"], "FMV": [1000000, 1000000, 3000000, 3000000, 3000000, 3000000], "Fund": ["A", "B", "B", "A", "C", "D"], "Amount": [250000, 250000, 1000000, 700000, 300000, 200000] }) # 生成图表 fig = plt.figure(figsize=(14, 8.5)) ax = plt.axes() loan_analysis(sample_data, ax) plt.tight_layout() plt.show()
关键实现说明
1. 数据预处理逻辑
- 将金额单位统一转换为百万美元,降低图表数值量级,提升可读性
- 把
Level设为有序分类类型,确保堆叠顺序严格遵循优先级 - 计算每个
Loan-Level的总金额,以及单个Fund在该层级的金额占比,这是实现宽度拆分的核心依据
2. 复杂柱状图绘制逻辑
- 手动分配每个贷款的x轴位置,控制风险敞口柱子与FMV柱子的位置偏移,避免视觉重叠
- 按
1st→2nd→3rd的顺序逐层堆叠,累计每层总金额作为下一层的堆叠起点 - 同一层级内,根据
Fund的金额占比计算分段柱子的宽度,逐个绘制并分配对应颜色
3. 样式优化
- 为每个
Fund分配唯一的tab10色系,保证颜色区分度 - 自定义y轴格式为百万美元显示,简化数值阅读
- 手动构建图例,同时包含所有
Fund的颜色标识和FMV的图例项
内容的提问来源于stack exchange,提问作者randose
相关产品推荐
相关产品推荐

