4、数据采集与清洗:金融机构数据来源与标准化处理

各位同学,咱们今天聊点实在的。

做风险敞口网络分析,最头疼的其实不是算法,而是数据。我见过太多团队,模型建得漂漂亮亮,一跑数据就崩——为什么?数据源没理清楚,清洗环节全是坑。

这一章,我就把我在银行和监管项目里踩过的坑、总结的经验,掰开了讲给你听。

4.1 数据来源:你从哪里拿数据?

金融机构的数据,说白了就三大类。我按实际项目中的使用频率排个序:

  1. 监管报告数据——最规范,但最滞后
  2. 交易系统数据——最实时,但最杂乱
  3. 内部台账数据——最全面,但格式最不统一

我个人习惯,优先用监管报告做底稿。为什么?因为监管报告有标准模板,字段定义清晰。我在某股份制银行做项目时,发现他们的交易数据里「对手方」字段居然有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 本章知识体系

下面这张图,是我做这个模块时画的流程框架,你一看就明白:

数据采集与清洗流程框架 监管报告数据 交易系统数据 内部台账数据 数据合并与去重 清洗:名称标准化 → CCP过滤 → 异常值处理 → 字段统一 输出:机构-交易对边列表(source, target, weight, type)

你看,从三个数据源出发,经过合并、清洗,最后输出标准化的边列表。每一步都有坑,但每一步也都有标准解法。

好了,这一章就到这里。数据清洗是个细活,急不得。你只要把流程搭好,后面跑网络分析就是水到渠成的事。


公众号:蓝海资料掘金营,微信deep3321