项目

一般

简介

CR-011-特征矩阵Parquet存储形态由长表变更为宽表 » 历史记录 » 版本 2

Huarui Lin, 2026-04-17 16:37

1 1 Huarui Lin
# CR-011-特征矩阵Parquet存储形态由长表变更为宽表
2
3
| 字段 | 内容 |
4
|------|------|
5
| **CR ID** | CR-004 |
6
| **标题** | 特征矩阵落盘形态由长表(EAV)变更为宽表(38列并排) |
7
| **发起人** | Henry Lin (PM) |
8
| **日期** | 2026-04-17 |
9
| **状态** | ✅ Approved |
10
| **影响范围** | 规约 4.1 节特征目录、S3-04 产出物 Schema、`docs/data_contract.md`、下游 Step 5 数据加载逻辑 |
11
12
### 1. 变更描述
13
14
**原规约约束**:
15
> 上述 38 维特征在 `features.parquet` 中必须为**长表形态**(Schema: `fund_id`, `net_value_date`, `feature_name`, `feature_value`)。严禁 Pivot 成宽表。
16 2 Huarui Lin
17 1 Huarui Lin
**变更为**:
18
> `features_YYYY.parquet` 必须为**宽表形态**。Schema 固定为:`fund_id`(String), `net_value_date`(Date), `segment_id`(UInt32), 以及 38 个特征列(如 `price_vs_ma_ratio_12w` Float64, ..., `calmar_ratio_52w` Float64)。严禁在落盘阶段使用 `unpivot` 转换为长表。
19
20
### 2. 变更原因(根因追溯)
21
22
在 M3a 架构评审中发现,原“长表落盘”红线与高性能计算架构存在严重冲突:
23
1. **计算层冲突**:截面 Z-Score 标准化与共线性剔除(IC 计算)的纯函数,在 Polars 中必须基于“行=样本,列=特征”的宽表矩阵才能进行高效向量化运算。若坚持长表,需在每次计算前后频繁执行 `pivot/unpivot`,带来巨额的 CPU 与内存开销。
24
2. **训练层冲突**:LightGBM 等树模型原生要求宽表输入,长表形态迫使 Step 5 必须额外维护一套高频 Pivot 读取逻辑。
25
3. **列式存储劣势**:对于 Parquet 这种列式格式,在特征维度固定(38维)且有限的情况下,宽表能完美利用“按列裁剪”优势;而长表(EAV)的 `feature_name` 字符串列会破坏列式压缩率,且按特征筛选时产生大量冗余 IO。
26
4. **维度确定性**:本系统特征已通过《特征字典基线文档》刚性冻结为 38 维,不存在“频繁增删特征导致宽表 Schema 失控”的 EAV 适用前提。
27
28
### 3. 影响分析
29
30
| 受影响模块 | 影响说明 | 应对措施 |
31
|-----------|---------|---------|
32
| `feature_engineering.py` | 取消落盘前的 `unpivot()` 逻辑 | 内存中全程维持宽表,计算完毕后直接调用 PyArrow `write_parquet()` |
33
| `data_contract.md` | 存储形态红线描述变更 | 更新《特征矩阵接口契约单》,明确宽表 Schema 及 38 列的严格类型映射 |
34
| `trainer.py` (Step 5) | 简化数据加载逻辑 | 直接读取宽表,废弃原有的 GroupBy+Pivot 还原逻辑 |
35
| 元数据校验 | Hash 与审计逻辑适配 | `features_hash.json` 基于宽表文件计算,无需变更工具函数 |