- Created a new module `utils/__init__.py` to consolidate utility imports. - Added `event_map.py` for mapping apnea event types to numerical values and colors. - Implemented various filtering functions in `filter_func.py`, including Butterworth, Bessel, downsampling, and notch filters. - Developed `operation_tools.py` for dataset configuration loading, event mask generation, and signal processing utilities. - Introduced `split_method.py` for segmenting data based on movement and amplitude criteria. - Added `statistics_metrics.py` for calculating amplitude metrics and generating confusion matrices. - Included a new Excel file for additional data storage.
471 lines
14 KiB
Markdown
471 lines
14 KiB
Markdown
# 项目背景文档(供 Agent 快速上手)
|
||
|
||
## 1. 文档目的
|
||
|
||
这份文档用于帮助新进入仓库的 agent 快速建立项目心智模型,重点回答下面几个问题:
|
||
|
||
- 这个仓库在做什么
|
||
- 核心处理链路是什么
|
||
- 代码从哪里开始看
|
||
- 输入数据长什么样,输出产物长什么样
|
||
- 哪些模块已经可用,哪些模块还只是占位或半成品
|
||
|
||
---
|
||
|
||
## 2. 一句话理解项目
|
||
|
||
`DataPrepare` 是一个睡眠呼吸暂停数据预处理仓库,主要用于把原始的 BCG / PSG 信号与呼吸暂停标签整理成可训练的数据集。
|
||
|
||
当前主线可以概括为两步:
|
||
|
||
1. 在 `event_mask_process/` 中把原始标注和规则检测结果整理成逐秒标签掩码。
|
||
2. 在 `dataset_builder/` 中根据这些掩码切出固定长度窗口,并将信号和窗口索引保存为 `npz` 数据集。
|
||
|
||
---
|
||
|
||
## 3. 仓库总览
|
||
|
||
| 目录 | 作用 | 备注 |
|
||
| --- | --- | --- |
|
||
| `dataset_config/` | 各数据集的 YAML 配置 | 包含路径、采样率、阈值、切窗参数 |
|
||
| `event_mask_process/` | 生成逐秒标签掩码 | 主入口之一 |
|
||
| `dataset_builder/` | 将信号切成窗口并保存数据集 | 主入口之二 |
|
||
| `signal_method/` | 信号预处理、规则检测、特征计算 | 规则逻辑核心 |
|
||
| `utils/` | 读文件、标签转换、切窗、滤波、通用工具 | 底层支撑 |
|
||
| `draw_tools/` | 全夜信号图、分段图、统计图 | 调试与可视化 |
|
||
| `dataset_tools/` | 辅助脚本 | 包括配对拷贝、SHHS 标注检查 |
|
||
| `output/` | 中间结果样例 | 仓库内已有若干处理结果示例 |
|
||
|
||
---
|
||
|
||
## 4. 项目主线流程
|
||
|
||
### 4.1 HYS(BCG 主线)
|
||
|
||
对应文件:
|
||
|
||
- `event_mask_process/HYS_process.py`
|
||
- `dataset_builder/HYS_dataset.py`
|
||
|
||
处理过程:
|
||
|
||
1. 从 `root_path/OrgBCG_Aligned/<id>/OrgBCG_Sync_*.txt` 读取原始 BCG 同步信号。
|
||
2. 从 `root_path/Label/<id>/SA Label_corrected.csv` 读取人工修正后的呼吸暂停标签。
|
||
3. 对原始 BCG 进行 50Hz 陷波,再拆出:
|
||
- 呼吸分量 `resp_data`
|
||
- BCG 分量 `bcg_data`
|
||
4. 根据配置做规则检测,生成逐秒掩码:
|
||
- `Resp_LowAmp_Label`
|
||
- `Resp_Movement_Label`
|
||
- `Resp_AmpChange_Label`
|
||
- `BCG_LowAmp_Label`
|
||
- `BCG_Movement_Label`
|
||
- `BCG_AmpChange_Label`
|
||
- `Disable_Label`(来自 `排除区间.xlsx`)
|
||
- `SA_Label` / `SA_Score`(来自人工标签)
|
||
5. 将这些结果保存为 `output/HYS/<id>/<id>_Processed_Labels.csv`。
|
||
6. 在数据集构建阶段读取上述逐秒标签,按 `window_sec` 和 `stride_sec` 切成窗口。
|
||
7. 保存处理后的信号 `Signals/*.npz`、窗口索引 `Segments_List/*.npz`、标签副本 `Labels/*.csv`。
|
||
|
||
### 4.2 HYS_PSG(PSG 主线)
|
||
|
||
对应文件:
|
||
|
||
- `event_mask_process/HYS_PSG_process.py`
|
||
- `dataset_builder/HYS_PSG_dataset.py`
|
||
|
||
处理过程:
|
||
|
||
1. 从 `root_path/PSG_Aligned/<id>/` 读取 PSG 通道,包括:
|
||
- `Rpeak`
|
||
- `ECG_Sync`
|
||
- `Effort Tho`
|
||
- `Effort Abd`
|
||
- `Flow P`
|
||
- `Flow T`
|
||
- `SpO2`
|
||
- `5_class`
|
||
2. 从同目录读取 `SA Label_Sync.csv`。
|
||
3. 在 `HYS_PSG_process.py` 中生成较简化的逐秒标签:
|
||
- `SA_Label`
|
||
- `SA_Score`
|
||
- `Disable_Label`(主要由睡眠分期里的清醒期生成)
|
||
- 其余 `Resp_*` / `BCG_*` 掩码目前全部置零
|
||
4. 在 `HYS_PSG_dataset.py` 中对胸腹带、流量、SpO2、RRI 做统一重采样与长度对齐。
|
||
5. 对 SpO2 进行异常填补或置空,再切成窗口,保存为 PSG 数据集。
|
||
|
||
### 4.3 设计上的共性
|
||
|
||
无论是 HYS 还是 HYS_PSG,核心设计都是:
|
||
|
||
1. 先把整夜记录整理成逐秒掩码。
|
||
2. 再根据掩码和切窗规则提取训练片段。
|
||
|
||
这意味着以后如果要改“可用性判断”或“切窗逻辑”,优先看:
|
||
|
||
- `event_mask_process/`
|
||
- `utils/split_method.py`
|
||
|
||
---
|
||
|
||
## 5. 数据与目录约定
|
||
|
||
### 5.1 外部数据目录
|
||
|
||
这个仓库本身不包含原始数据,真正的数据目录由 YAML 中的绝对路径指定,例如:
|
||
|
||
- `/mnt/disk_wd/marques_dataset/DataCombine2023/HYS`
|
||
- `/mnt/disk_wd/marques_dataset/shhs/polysomnography/shhs1`
|
||
|
||
因此在新环境运行时,第一件事通常不是改代码,而是先改 `dataset_config/*.yaml` 里的绝对路径。
|
||
|
||
### 5.2 HYS 数据目录约定
|
||
|
||
代码默认外部目录至少包含:
|
||
|
||
- `OrgBCG_Aligned/<id>/OrgBCG_Sync_<fs>.txt`
|
||
- `PSG_Aligned/<id>/...`
|
||
- `Label/<id>/SA Label_corrected.csv`
|
||
|
||
### 5.3 HYS_PSG 数据目录约定
|
||
|
||
代码默认 `PSG_Aligned/<id>/` 内文件命名符合下面的模式:
|
||
|
||
- `Rpeak*.txt`
|
||
- `ECG_Sync*.txt`
|
||
- `Effort Tho*.txt`
|
||
- `Effort Abd*.txt`
|
||
- `Flow P*.txt`
|
||
- `Flow T*.txt`
|
||
- `SpO2*.txt`
|
||
- `5_class*.txt`
|
||
- `SA Label_Sync.csv`
|
||
|
||
### 5.4 处理中间结果目录
|
||
|
||
仓库内 `output/` 保存的是“中间标签与图像结果”,不是最终训练数据集。
|
||
最终数据集通常会写到 YAML 中 `dataset_save_path` 指向的外部目录。
|
||
|
||
---
|
||
|
||
## 6. 核心入口脚本
|
||
|
||
### 6.1 主入口
|
||
|
||
| 场景 | 脚本 |
|
||
| --- | --- |
|
||
| HYS 逐秒标签生成 | `event_mask_process/HYS_process.py` |
|
||
| HYS 数据集构建 | `dataset_builder/HYS_dataset.py` |
|
||
| HYS_PSG 逐秒标签生成 | `event_mask_process/HYS_PSG_process.py` |
|
||
| HYS_PSG 数据集构建 | `dataset_builder/HYS_PSG_dataset.py` |
|
||
|
||
### 6.2 辅助脚本
|
||
|
||
| 脚本 | 作用 |
|
||
| --- | --- |
|
||
| `dataset_tools/resp_pair_copy.py` | 将 HYS/ZD5Y 的 BCG 与 PSG 原始文件拷贝为配对数据集 |
|
||
| `dataset_tools/shhs_annotations_check.py` | 统计 SHHS XML 标注中的事件类型组合 |
|
||
| `event_mask_process/SHHS1_process.py` | SHHS1 处理入口占位,目前未实现 |
|
||
|
||
### 6.3 入口脚本的共同特点
|
||
|
||
- 大多数脚本没有命令行参数接口。
|
||
- 配置文件路径通常直接写在 `if __name__ == '__main__':` 中。
|
||
- 运行逻辑依赖 YAML 中的绝对路径。
|
||
|
||
所以如果 agent 要“跑脚本”,通常需要先确认:
|
||
|
||
1. 当前机器是否存在对应外部数据目录。
|
||
2. YAML 路径是否匹配当前环境。
|
||
|
||
---
|
||
|
||
## 7. 关键模块说明
|
||
|
||
### 7.1 `utils/`
|
||
|
||
#### `utils/HYS_FileReader.py`
|
||
|
||
负责各种输入文件读取,是仓库最重要的底层模块之一:
|
||
|
||
- `read_signal_txt`:读取单通道 txt,并根据文件名中的采样率推断 `fs`
|
||
- `read_label_csv`:读取人工修正的 HYS 标签
|
||
- `read_raw_psg_label`:读取 PSG 原始同步标签
|
||
- `read_disable_excel`:读取 `排除区间.xlsx`
|
||
- `read_mask_execl`:读取处理后的逐秒标签 CSV,并生成事件片段列表
|
||
- `read_psg_channel`:按通道名读取 PSG 文件夹里的多通道数据
|
||
|
||
#### `utils/operation_tools.py`
|
||
|
||
负责标签转换、片段提取和通用处理:
|
||
|
||
- `load_dataset_conf`:读取 YAML
|
||
- `generate_event_mask`:把事件表转换成逐秒 `SA_Label`
|
||
- `generate_disable_mask`:把 Excel 中的排除区间转换成逐秒 `Disable_Label`
|
||
- `event_mask_2_list`:把 0/1 掩码转为 `[start, end]` 列表
|
||
- `merge_short_gaps` / `remove_short_durations`:对逐秒掩码做时长后处理
|
||
- `fill_spo2_anomaly`:修补 SpO2 异常段
|
||
|
||
#### `utils/split_method.py`
|
||
|
||
真正的切窗规则在这里:
|
||
|
||
- 默认按 `window_sec` / `stride_sec` 滑窗
|
||
- 只在 `EnableSegment` 内生成可用窗口
|
||
- 如果一个窗口中 `Resp_Movement_Label | Resp_LowAmp_Label` 超过窗口时长的 2/3,则该窗口转入 `disable_segment_list`
|
||
|
||
#### `utils/filter_func.py`
|
||
|
||
提供滤波和采样率处理:
|
||
|
||
- Butterworth / Bessel
|
||
- 陷波
|
||
- 整数倍降采样
|
||
- 自动升降采样
|
||
- 移动平均去趋势
|
||
|
||
### 7.2 `signal_method/`
|
||
|
||
#### `signal_method/signal_process.py`
|
||
|
||
负责信号预处理:
|
||
|
||
- `signal_filter_split`:把原始 OrgBCG 信号拆成呼吸分量和 BCG 分量
|
||
- `psg_effort_filter`:处理 PSG 努力带 / 流量信号
|
||
- `rpeak2hr` / `rpeak2rri_interpolation`:由 R 峰生成 HR / RRI
|
||
|
||
#### `signal_method/rule_base_event.py`
|
||
|
||
规则检测主逻辑:
|
||
|
||
- `detect_movement`:基于滑窗标准差和局部幅值比较检测体动
|
||
- `movement_revise`:对体动掩码做二次修正
|
||
- `detect_low_amplitude_signal`:检测低幅值
|
||
- `position_based_sleep_recognition_v2/v3`:根据体动前后幅值变化标记姿势/幅值变化段
|
||
|
||
#### `signal_method/normalize_method.py`
|
||
|
||
`normalize_resp_signal_by_segment` 会按“可用片段”做分段 z-score 标准化。
|
||
HYS 中通常按 `Resp_AmpChange_Label` 的反向片段来归一化,目的是减少整夜幅值漂移带来的影响。
|
||
|
||
### 7.3 `draw_tools/`
|
||
|
||
主要用于人工检查处理质量:
|
||
|
||
- `draw_signal_with_mask`:画 HYS 全夜原始信号 + 规则掩码
|
||
- `draw_psg_signal`:画 HYS_PSG 全夜 PSG 信号
|
||
- `draw_psg_label` / `draw_psg_bcg_label`:按窗口导出分段图
|
||
|
||
### 7.4 `dataset_builder/`
|
||
|
||
核心职责是把“整夜记录 + 逐秒掩码”转换成训练样本:
|
||
|
||
- 保存处理后的多通道信号到 `npz`
|
||
- 保存窗口起止列表到 `npz`
|
||
- 把标签 CSV 一并拷贝到数据集目录
|
||
|
||
---
|
||
|
||
## 8. 标签、通道和编码约定
|
||
|
||
### 8.1 呼吸暂停事件编码
|
||
|
||
定义于 `utils/event_map.py`:
|
||
|
||
| 事件 | 编码 |
|
||
| --- | --- |
|
||
| `Hypopnea` | 1 |
|
||
| `Central apnea` | 2 |
|
||
| `Obstructive apnea` | 3 |
|
||
| `Mixed apnea` | 4 |
|
||
|
||
### 8.2 PSG 通道编号映射
|
||
|
||
同样定义于 `utils/event_map.py`:
|
||
|
||
| 编号 | 通道名 |
|
||
| --- | --- |
|
||
| 1 | `Rpeak` |
|
||
| 2 | `ECG_Sync` |
|
||
| 3 | `Effort Tho` |
|
||
| 4 | `Effort Abd` |
|
||
| 5 | `Flow P` |
|
||
| 6 | `Flow T` |
|
||
| 7 | `SpO2` |
|
||
| 8 | `5_class` |
|
||
|
||
### 8.3 睡眠分期编码
|
||
|
||
`5_class` 在读取时会转成整数:
|
||
|
||
| 分期 | 编码 |
|
||
| --- | --- |
|
||
| `N3` | 1 |
|
||
| `N2` | 2 |
|
||
| `N1` | 3 |
|
||
| `R` | 4 |
|
||
| `W` | 5 |
|
||
|
||
### 8.4 逐秒标签 CSV 字段
|
||
|
||
`Processed_Labels.csv` 的核心字段为:
|
||
|
||
- `Second`
|
||
- `SA_Label`
|
||
- `SA_Score`
|
||
- `Disable_Label`
|
||
- `Resp_LowAmp_Label`
|
||
- `Resp_Movement_Label`
|
||
- `Resp_AmpChange_Label`
|
||
- `BCG_LowAmp_Label`
|
||
- `BCG_Movement_Label`
|
||
- `BCG_AmpChange_Label`
|
||
|
||
样例可见仓库内:
|
||
|
||
- `output/HYS/220/220_Processed_Labels.csv`
|
||
- `output/HYS_PSG/220/220_Processed_Labels.csv`
|
||
|
||
---
|
||
|
||
## 9. 主要配置文件
|
||
|
||
### 9.1 HYS
|
||
|
||
`dataset_config/HYS_config.yaml` 主要控制:
|
||
|
||
- 样本 ID 列表
|
||
- 原始数据根目录
|
||
- 中间标签保存目录
|
||
- 呼吸 / BCG 的滤波和降采样参数
|
||
- 低幅值、体动、幅值变化检测阈值
|
||
- 数据集窗口长度和步长
|
||
|
||
### 9.2 HYS_PSG
|
||
|
||
`dataset_config/HYS_PSG_config.yaml` 主要控制:
|
||
|
||
- 样本 ID 列表
|
||
- 目标统一采样率 `target_fs`
|
||
- 努力带 / 流量滤波参数
|
||
- SpO2 异常填补参数
|
||
- 数据集输出路径
|
||
|
||
### 9.3 其他配置
|
||
|
||
- `dataset_config/ZD5Y_config.yaml`:另一套 BCG 规则配置
|
||
- `dataset_config/SHHS1_config.yaml`:SHHS1 预留配置
|
||
- `dataset_config/RESP_PAIR_HYS_config.yaml`:HYS 配对原始数据拷贝
|
||
- `dataset_config/RESP_PAIR_ZD5Y_config.yaml`:ZD5Y 配对原始数据拷贝
|
||
|
||
---
|
||
|
||
## 10. 输出产物说明
|
||
|
||
### 10.1 中间输出
|
||
|
||
典型位置:
|
||
|
||
- `output/HYS/<id>/<id>_Processed_Labels.csv`
|
||
- `output/HYS/<id>/<id>_Signal_Plots.png`
|
||
- `output/HYS_PSG/<id>/<id>_Processed_Labels.csv`
|
||
- `output/HYS_PSG/<id>/<id>_Signal_Plots.png`
|
||
- `output/HYS_PSG/<id>/<id>_Signal_Plots_fill.png`
|
||
|
||
### 10.2 最终数据集输出
|
||
|
||
由 `dataset_builder/*` 保存到 YAML 指定的外部目录,结构通常是:
|
||
|
||
- `Signals/`
|
||
- `Segments_List/`
|
||
- `Labels/`
|
||
|
||
HYS 的 `Signals/*.npz` 里主要保存:
|
||
|
||
- `bcg_signal_notch`
|
||
- `bcg_signal`
|
||
- `resp_signal`
|
||
|
||
HYS_PSG 的 `Signals/*.npz` 里主要保存:
|
||
|
||
- `Effort Tho`
|
||
- `Effort Abd`
|
||
- `Effort`
|
||
- `Flow P`
|
||
- `Flow T`
|
||
- `SpO2`
|
||
- `HR`
|
||
- `RRI`
|
||
- `5_class`
|
||
|
||
`Segments_List/*.npz` 中主要保存:
|
||
|
||
- `segment_list`
|
||
- `disable_segment_list`
|
||
|
||
---
|
||
|
||
## 11. 推荐阅读顺序
|
||
|
||
如果 agent 是第一次接触这个仓库,建议按下面顺序阅读:
|
||
|
||
1. `dataset_config/HYS_config.yaml`
|
||
2. `event_mask_process/HYS_process.py`
|
||
3. `utils/operation_tools.py`
|
||
4. `utils/split_method.py`
|
||
5. `dataset_builder/HYS_dataset.py`
|
||
6. `signal_method/signal_process.py`
|
||
7. `signal_method/rule_base_event.py`
|
||
8. `utils/HYS_FileReader.py`
|
||
|
||
如果要看 PSG 主线,再继续读:
|
||
|
||
1. `dataset_config/HYS_PSG_config.yaml`
|
||
2. `event_mask_process/HYS_PSG_process.py`
|
||
3. `dataset_builder/HYS_PSG_dataset.py`
|
||
4. `draw_tools/draw_label.py`
|
||
|
||
---
|
||
|
||
## 12. 常见修改入口
|
||
|
||
如果你要改不同类型的问题,可以优先从这些文件入手:
|
||
|
||
| 需求 | 优先看哪里 |
|
||
| --- | --- |
|
||
| 调整阈值、采样率、窗口长度 | `dataset_config/*.yaml` |
|
||
| 修改逐秒标签生成逻辑 | `event_mask_process/*.py` |
|
||
| 修改体动 / 低幅值 / 幅值变化规则 | `signal_method/rule_base_event.py` |
|
||
| 修改滤波与重采样 | `signal_method/signal_process.py`、`utils/filter_func.py` |
|
||
| 修改切窗规则 | `utils/split_method.py` |
|
||
| 修改输入文件解析 | `utils/HYS_FileReader.py` |
|
||
| 修改事件编码或通道映射 | `utils/event_map.py` |
|
||
| 修改图像输出样式 | `draw_tools/*.py` |
|
||
|
||
---
|
||
|
||
## 13. 当前实现状态与注意事项
|
||
|
||
下面这些点对 agent 很重要:
|
||
|
||
1. 仓库没有依赖清单文件(如 `requirements.txt` / `pyproject.toml`),依赖需要从源码导入中反推,当前至少涉及 `numpy`、`pandas`、`scipy`、`matplotlib`、`seaborn`、`yaml`、`tqdm`、`rich`、`polars`、`lxml`、`mne`。
|
||
2. 大部分脚本依赖绝对路径,迁移环境时优先修改 YAML。
|
||
3. `event_mask_process/SHHS1_process.py` 目前基本为空,占位多于实现。
|
||
4. `event_mask_process/HYS_PSG_process.py` 当前是“简化版标签生成”,核心只用了 SA 标签和睡眠分期,`Resp_*` / `BCG_*` 掩码还没有真正实现。
|
||
5. `dataset_builder/HYS_dataset.py` 里虽然保留了分段可视化入口,但实际绘图调用被注释掉了,因此默认不会导出分段图。
|
||
6. `utils/signal_process.py` 和 `utils/filter_func.py` 有部分重复实现;当前实际被 `utils/__init__.py` 导出并广泛使用的是 `filter_func.py` 中的版本。
|
||
7. `README.md` 目前非常简略,真正的项目逻辑主要还是要靠源码理解。
|
||
|
||
---
|
||
|
||
## 14. 对 Agent 最有价值的结论
|
||
|
||
如果只能记住几件事,请记住下面这些:
|
||
|
||
1. 这是一个“先做逐秒掩码,再做固定窗口切片”的数据准备仓库。
|
||
2. HYS 主线比 HYS_PSG 更完整,规则检测主要服务于 HYS。
|
||
3. 切窗逻辑集中在 `utils/split_method.py`,不是分散在各个 builder 里。
|
||
4. 原始数据不在仓库里,仓库只是代码和部分中间结果样例。
|
||
5. 大多数运行问题都不是代码逻辑错,而是路径、文件命名、采样率和外部数据结构不匹配。
|
||
|