4、数据采集与清洗:金融机构数据来源与标准化处理
各位同学,咱们今天聊点实在的。
做风险敞口网络分析,最头疼的其实不是算法,而是数据。我见过太多团队,模型建得漂漂亮亮,一跑数据就崩——为什么?数据源没理清楚,清洗环节全是坑。
这一章,我就把我在银行和监管项目里踩过的坑、总结的经验,掰开了讲给你听。
4.1 数据来源:你从哪里拿数据?
金融机构的数据,说白了就三大类。我按实际项目中的使用频率排个序:
- 监管报告数据——最规范,但最滞后
- 交易系统数据——最实时,但最杂乱
- 内部台账数据——最全面,但格式最不统一
我个人习惯,优先用监管报告做底稿。为什么?因为监管报告有标准模板,字段定义清晰。我在某股份制银行做项目时,发现他们的交易数据里「对手方」字段居然有17种写法——「工商银行」「工行」「ICBC」「中国工商银行股份有限公司」……你想想看,这要是直接拿来建网络,出来的图能看吗?
核心原则:数据源选择顺序 = 规范性 > 实时性 > 完整性
4.2 监管报告数据:最靠谱的起点
监管报告通常包括:
- G14 表(大额风险暴露)——记录单家机构对另一家机构的敞口
- G4B 表(表内外信用风险)——包含交易对手信息
- 央行金融稳定报表——系统性重要金融机构的关联数据
这些表的结构基本固定。我截取一个典型的监管报告字段结构:
| 字段名 | 类型 | 说明 |
|---|---|---|
| 报告机构代码 | VARCHAR(20) | 如:BANK_A_001 |
| 对手方机构代码 | VARCHAR(20) | 如:BANK_B_002 |
| 风险敞口金额 | DECIMAL(18,2) | 单位:万元 |
| 敞口类型 | VARCHAR(10) | 贷款/债券/衍生品/担保 |
| 报告日期 | DATE | YYYY-MM-DD |
嗯,这里要注意:监管报告的机构代码通常是内部编码,不同银行之间不互通。你需要一个「机构映射表」来统一。
4.3 交易数据:实时但混乱
交易数据来自核心系统、资金交易系统、衍生品交易平台。字段多、量大,但质量参差不齐。
我记得有一次,从某券商拿到的交易数据里,「交易对手」字段居然混入了「央行」「中债登」「上海清算所」这些清算机构——它们不是交易对手,是中央对手方(CCP)。如果不区分,网络分析里就会多出一堆「超级节点」,把真实的风险结构全扭曲了。
避坑指南:我曾经因为没过滤CCP节点,画出来的网络图里「上海清算所」成了最大节点,占整个网络80%的边——这完全掩盖了银行间的真实风险敞口。后来我加了一个「CCP白名单」过滤逻辑,才恢复正常。
4.4 数据清洗:80%的工作量在这里
数据清洗,说白了就是三件事:去重、标准化、补缺失。
我总结了一个清洗流水线,用Python实现大概这样:
import pandas as pd
import re
def clean_institution_name(name):
"""机构名称标准化"""
# 去除空格和特殊字符
name = re.sub(r'[\s\(\)()]', '', name)
# 统一简称
mapping = {
'工商银行': 'ICBC',
'工行': 'ICBC',
'中国工商银行': 'ICBC',
'农业银行': 'ABC',
'农行': 'ABC',
# ... 更多映射
}
return mapping.get(name, name)
def build_edge_dataset(report_df, trade_df):
"""构建机构-交易对数据集"""
# 1. 合并数据源
combined = pd.concat([report_df, trade_df], ignore_index=True)
# 2. 标准化机构名称
combined['source_clean'] = combined['source'].apply(clean_institution_name)
combined['target_clean'] = combined['target'].apply(clean_institution_name)
# 3. 去重(同一对机构、同一日期、同一类型)
combined = combined.drop_duplicates(
subset=['source_clean', 'target_clean', 'exposure_type', 'report_date']
)
# 4. 过滤CCP节点
ccp_list = ['上海清算所', '中债登', '中央结算公司']
combined = combined[
~combined['source_clean'].isin(ccp_list) &
~combined['target_clean'].isin(ccp_list)
]
return combined[['source_clean', 'target_clean', 'exposure_amount', 'exposure_type']]
小技巧:机构名称映射表,我建议你维护一个CSV文件,每次有新机构就追加。别硬编码在代码里——我吃过这个亏,换了个项目就得改代码,太蠢了。
4.5 构建机构-交易对数据集
清洗完之后,我们要把数据转成「边列表」格式。这是网络分析的标准输入:
| source | target | weight | type |
|---|---|---|---|
| BANK_A | BANK_B | 150000.00 | 贷款 |
| BANK_A | BANK_C | 80000.00 | 债券 |
| BANK_B | BANK_D | 200000.00 | 衍生品 |
每一行代表一条「风险敞口边」。source是敞口暴露方,target是被暴露方,weight是金额。
你想想看,如果数据源里有100家银行,每个银行对另外99家都有敞口,那这个网络就是完全图——但现实中不是这样的。真实网络是稀疏的,大部分银行只跟少数几家有实质性关联。清洗的目的,就是把这些「真实关联」从噪音里捞出来。
4.6 数据质量检查清单
我每次做完清洗,都会跑一遍这个检查:
- ✅ 机构名称是否全部标准化?——用set()检查唯一值数量
- ✅ 是否有负的敞口金额?——如果有,可能是数据录入错误
- ✅ 是否有自环(source == target)?——银行对自身的敞口应该剔除
- ✅ 日期格式是否统一?——我见过YYYYMMDD和YYYY-MM-DD混用的
- ✅ 敞口类型是否在预期范围内?——突然出现「其他」类型要警惕
我的经验:数据清洗阶段,每发现一个异常,就加一条assert断言。这样下次跑数据时,同样的错误会立刻报出来。别问我为什么知道——我曾经被同一个「机构名称空格问题」坑了三次。
4.7 本章知识体系
下面这张图,是我做这个模块时画的流程框架,你一看就明白:
你看,从三个数据源出发,经过合并、清洗,最后输出标准化的边列表。每一步都有坑,但每一步也都有标准解法。
好了,这一章就到这里。数据清洗是个细活,急不得。你只要把流程搭好,后面跑网络分析就是水到渠成的事。
公众号:蓝海资料掘金营,微信deep3321