shapg2p 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- shapg2p-0.1.0/LICENSE +21 -0
- shapg2p-0.1.0/PKG-INFO +120 -0
- shapg2p-0.1.0/README.md +101 -0
- shapg2p-0.1.0/pyproject.toml +30 -0
- shapg2p-0.1.0/setup.cfg +4 -0
- shapg2p-0.1.0/shapg2p/__init__.py +13 -0
- shapg2p-0.1.0/shapg2p/core.py +195 -0
- shapg2p-0.1.0/shapg2p/data/genes.json +1 -0
- shapg2p-0.1.0/shapg2p/data/hallmark_mean_dist.npz +0 -0
- shapg2p-0.1.0/shapg2p/data/hallmark_min_dist.npz +0 -0
- shapg2p-0.1.0/shapg2p/data/hallmark_pathways.json +52 -0
- shapg2p-0.1.0/shapg2p/data/info_gene.csv +17614 -0
- shapg2p-0.1.0/shapg2p/data/kegg_mean_dist.npz +0 -0
- shapg2p-0.1.0/shapg2p/data/kegg_min_dist.npz +0 -0
- shapg2p-0.1.0/shapg2p/data/kegg_pathways.json +371 -0
- shapg2p-0.1.0/shapg2p/data/wikipathway_mean_dist.npz +0 -0
- shapg2p-0.1.0/shapg2p/data/wikipathway_min_dist.npz +0 -0
- shapg2p-0.1.0/shapg2p/data/wikipathway_pathways.json +936 -0
- shapg2p-0.1.0/shapg2p.egg-info/PKG-INFO +120 -0
- shapg2p-0.1.0/shapg2p.egg-info/SOURCES.txt +21 -0
- shapg2p-0.1.0/shapg2p.egg-info/dependency_links.txt +1 -0
- shapg2p-0.1.0/shapg2p.egg-info/requires.txt +5 -0
- shapg2p-0.1.0/shapg2p.egg-info/top_level.txt +1 -0
shapg2p-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ShapG2P
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
shapg2p-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: shapg2p
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: ShapG2P: biomarker pathway enrichment with PPI network topology and SHAP analysis
|
|
5
|
+
Author: ShapG2P
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/yourname/shapg2p
|
|
8
|
+
Project-URL: Issues, https://github.com/yourname/shapg2p/issues
|
|
9
|
+
Keywords: SHAP,pathway enrichment,PPI network,biomarker,XGBoost
|
|
10
|
+
Requires-Python: >=3.9
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: numpy>=1.21
|
|
14
|
+
Requires-Dist: pandas>=1.3
|
|
15
|
+
Requires-Dist: scipy>=1.7
|
|
16
|
+
Requires-Dist: scikit-learn>=1.0
|
|
17
|
+
Requires-Dist: xgboost>=1.6
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# ShapG2P
|
|
21
|
+
|
|
22
|
+
ShapG2P: a strategy for biomarker pathway enrichment with PPI network topology and SHAP analysis.
|
|
23
|
+
|
|
24
|
+
输入一个基因符号列表(文件或直接传 list),输出 `{通路名: SHAP 分数}` 字典,按分数从大到小排序。
|
|
25
|
+
|
|
26
|
+
## 方法
|
|
27
|
+
|
|
28
|
+
- 每个基因以其到各通路基因的 PPI 网络距离(`exp(-d/2)` 相似度)为特征;
|
|
29
|
+
- XGBoost 分类器区分 biomarker 与背景基因(全局 1:1 欠采样,SEED=42);
|
|
30
|
+
- 用 SHAP(`pred_contribs`)计算每条通路特征的平均绝对贡献 = 通路 SHAP 分数。
|
|
31
|
+
|
|
32
|
+
内置数据:STRING 人类 PPI 网络(17613 基因)、KEGG / Hallmark / WikiPathway 通路。
|
|
33
|
+
|
|
34
|
+
## 安装
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install shapg2p
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
或本地开发安装:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
cd shapg2p_pkg
|
|
44
|
+
pip install -e .
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## 用法
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from shapg2p import score_pathways
|
|
51
|
+
|
|
52
|
+
# 方式 1: 直接传基因符号列表
|
|
53
|
+
scores = score_pathways(['TP53', 'ATM', 'APOE', 'SOD1', 'CDKN2A'])
|
|
54
|
+
|
|
55
|
+
# 方式 2: 传文件路径 (CSV/TSV/TXT, 含常见基因列如 gene symbol / gene / symbol)
|
|
56
|
+
scores = score_pathways('my_biomarkers.csv')
|
|
57
|
+
|
|
58
|
+
# 方式 3: 传逗号/空格/换行分隔的字符串
|
|
59
|
+
scores = score_pathways('TP53, ATM, APOE')
|
|
60
|
+
|
|
61
|
+
# 输出: 字典, 按 SHAP 分数从大到小
|
|
62
|
+
print(scores)
|
|
63
|
+
# {'p53 signaling pathway': 0.42, 'Alzheimer disease': 0.31, ...}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
运行约需 1–3 分钟(一次 XGBoost 训练 + 全基因 SHAP 计算)。
|
|
67
|
+
|
|
68
|
+
## 输出说明
|
|
69
|
+
|
|
70
|
+
返回 `dict[str, float]`:key 为通路名,value 为该通路的 mean |SHAP| 分数,按分数降序;
|
|
71
|
+
仅包含分数 > 0 的通路(无关通路不返回)。同名通路(出现在多个数据库中)取最大分数。
|
|
72
|
+
|
|
73
|
+
## 上传到 PyPI(供他人 pip install)
|
|
74
|
+
|
|
75
|
+
### 1. 注册账号并创建 API token
|
|
76
|
+
|
|
77
|
+
- 注册:https://pypi.org/account/register/
|
|
78
|
+
- 创建 token:https://pypi.org/manage/account/token/ (Scope 选整个账号即可)
|
|
79
|
+
- token 形如 `pypi-AgEIcHlwaS5vcmcC...`,只显示一次,保存好
|
|
80
|
+
|
|
81
|
+
### 2. 构建
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
pip install --upgrade build twine
|
|
85
|
+
cd shapg2p_pkg
|
|
86
|
+
python -m build
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
生成 `dist/shapg2p-0.1.0.tar.gz` 和 `dist/shapg2p-0.1.0-py3-none-any.whl`。
|
|
90
|
+
|
|
91
|
+
### 3. 先传 TestPyPI 验证(可选但推荐)
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
python -m twine upload --repository testpypi dist/*
|
|
95
|
+
# 用户名输入: __token__ 密码输入: pypi-xxx (你的 API token)
|
|
96
|
+
|
|
97
|
+
# 验证安装
|
|
98
|
+
pip install --index-url https://test.pypi.org/simple/ shapg2p
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 4. 上传正式 PyPI
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
python -m twine upload dist/*
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
同样输入 `__token__` + API token。上传成功后即可:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
pip install shapg2p
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 5. 更新版本
|
|
114
|
+
|
|
115
|
+
修改 `pyproject.toml` 的 `version`(如 `0.1.1`),重新 `python -m build` 并 `twine upload dist/*`。
|
|
116
|
+
PyPI 不允许重复上传相同版本号。
|
|
117
|
+
|
|
118
|
+
## 依赖
|
|
119
|
+
|
|
120
|
+
numpy / pandas / scipy / scikit-learn / xgboost(`pip install shapg2p` 时自动安装)。
|
shapg2p-0.1.0/README.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# ShapG2P
|
|
2
|
+
|
|
3
|
+
ShapG2P: a strategy for biomarker pathway enrichment with PPI network topology and SHAP analysis.
|
|
4
|
+
|
|
5
|
+
输入一个基因符号列表(文件或直接传 list),输出 `{通路名: SHAP 分数}` 字典,按分数从大到小排序。
|
|
6
|
+
|
|
7
|
+
## 方法
|
|
8
|
+
|
|
9
|
+
- 每个基因以其到各通路基因的 PPI 网络距离(`exp(-d/2)` 相似度)为特征;
|
|
10
|
+
- XGBoost 分类器区分 biomarker 与背景基因(全局 1:1 欠采样,SEED=42);
|
|
11
|
+
- 用 SHAP(`pred_contribs`)计算每条通路特征的平均绝对贡献 = 通路 SHAP 分数。
|
|
12
|
+
|
|
13
|
+
内置数据:STRING 人类 PPI 网络(17613 基因)、KEGG / Hallmark / WikiPathway 通路。
|
|
14
|
+
|
|
15
|
+
## 安装
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install shapg2p
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
或本地开发安装:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
cd shapg2p_pkg
|
|
25
|
+
pip install -e .
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 用法
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from shapg2p import score_pathways
|
|
32
|
+
|
|
33
|
+
# 方式 1: 直接传基因符号列表
|
|
34
|
+
scores = score_pathways(['TP53', 'ATM', 'APOE', 'SOD1', 'CDKN2A'])
|
|
35
|
+
|
|
36
|
+
# 方式 2: 传文件路径 (CSV/TSV/TXT, 含常见基因列如 gene symbol / gene / symbol)
|
|
37
|
+
scores = score_pathways('my_biomarkers.csv')
|
|
38
|
+
|
|
39
|
+
# 方式 3: 传逗号/空格/换行分隔的字符串
|
|
40
|
+
scores = score_pathways('TP53, ATM, APOE')
|
|
41
|
+
|
|
42
|
+
# 输出: 字典, 按 SHAP 分数从大到小
|
|
43
|
+
print(scores)
|
|
44
|
+
# {'p53 signaling pathway': 0.42, 'Alzheimer disease': 0.31, ...}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
运行约需 1–3 分钟(一次 XGBoost 训练 + 全基因 SHAP 计算)。
|
|
48
|
+
|
|
49
|
+
## 输出说明
|
|
50
|
+
|
|
51
|
+
返回 `dict[str, float]`:key 为通路名,value 为该通路的 mean |SHAP| 分数,按分数降序;
|
|
52
|
+
仅包含分数 > 0 的通路(无关通路不返回)。同名通路(出现在多个数据库中)取最大分数。
|
|
53
|
+
|
|
54
|
+
## 上传到 PyPI(供他人 pip install)
|
|
55
|
+
|
|
56
|
+
### 1. 注册账号并创建 API token
|
|
57
|
+
|
|
58
|
+
- 注册:https://pypi.org/account/register/
|
|
59
|
+
- 创建 token:https://pypi.org/manage/account/token/ (Scope 选整个账号即可)
|
|
60
|
+
- token 形如 `pypi-AgEIcHlwaS5vcmcC...`,只显示一次,保存好
|
|
61
|
+
|
|
62
|
+
### 2. 构建
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install --upgrade build twine
|
|
66
|
+
cd shapg2p_pkg
|
|
67
|
+
python -m build
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
生成 `dist/shapg2p-0.1.0.tar.gz` 和 `dist/shapg2p-0.1.0-py3-none-any.whl`。
|
|
71
|
+
|
|
72
|
+
### 3. 先传 TestPyPI 验证(可选但推荐)
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
python -m twine upload --repository testpypi dist/*
|
|
76
|
+
# 用户名输入: __token__ 密码输入: pypi-xxx (你的 API token)
|
|
77
|
+
|
|
78
|
+
# 验证安装
|
|
79
|
+
pip install --index-url https://test.pypi.org/simple/ shapg2p
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 4. 上传正式 PyPI
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
python -m twine upload dist/*
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
同样输入 `__token__` + API token。上传成功后即可:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pip install shapg2p
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### 5. 更新版本
|
|
95
|
+
|
|
96
|
+
修改 `pyproject.toml` 的 `version`(如 `0.1.1`),重新 `python -m build` 并 `twine upload dist/*`。
|
|
97
|
+
PyPI 不允许重复上传相同版本号。
|
|
98
|
+
|
|
99
|
+
## 依赖
|
|
100
|
+
|
|
101
|
+
numpy / pandas / scipy / scikit-learn / xgboost(`pip install shapg2p` 时自动安装)。
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "shapg2p"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "ShapG2P: biomarker pathway enrichment with PPI network topology and SHAP analysis"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
authors = [{ name = "ShapG2P" }]
|
|
12
|
+
keywords = ["SHAP", "pathway enrichment", "PPI network", "biomarker", "XGBoost"]
|
|
13
|
+
requires-python = ">=3.9"
|
|
14
|
+
dependencies = [
|
|
15
|
+
"numpy>=1.21",
|
|
16
|
+
"pandas>=1.3",
|
|
17
|
+
"scipy>=1.7",
|
|
18
|
+
"scikit-learn>=1.0",
|
|
19
|
+
"xgboost>=1.6",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[project.urls]
|
|
23
|
+
Homepage = "https://github.com/yourname/shapg2p"
|
|
24
|
+
Issues = "https://github.com/yourname/shapg2p/issues"
|
|
25
|
+
|
|
26
|
+
[tool.setuptools]
|
|
27
|
+
packages = ["shapg2p"]
|
|
28
|
+
|
|
29
|
+
[tool.setuptools.package-data]
|
|
30
|
+
shapg2p = ["data/*.npz", "data/*.json", "data/*.csv"]
|
shapg2p-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""ShapG2P: biomarker pathway enrichment with PPI network topology and SHAP.
|
|
3
|
+
|
|
4
|
+
用法:
|
|
5
|
+
from shapg2p import score_pathways
|
|
6
|
+
|
|
7
|
+
scores = score_pathways(['TP53', 'ATM', 'APOE', ...]) # 基因列表
|
|
8
|
+
scores = score_pathways('biomarkers.csv') # 或文件路径
|
|
9
|
+
"""
|
|
10
|
+
from .core import score_pathways
|
|
11
|
+
|
|
12
|
+
__version__ = '0.1.0'
|
|
13
|
+
__all__ = ['score_pathways']
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""ShapG2P 核心: 输入基因列表 (文件或 gene symbol list),
|
|
3
|
+
输出 {通路名: SHAP 分数} 字典, 按分数从大到小。
|
|
4
|
+
|
|
5
|
+
方法: 每个基因用其到各通路基因的 PPI 网络距离 (1/(d+1)-类相似度,
|
|
6
|
+
exp(-d/2)) 作为特征, XGBoost 区分 biomarker 与背景基因 (全局 1:1
|
|
7
|
+
欠采样, SEED=42), 用 SHAP (pred_contribs) 得到每条通路特征对分类的
|
|
8
|
+
平均绝对贡献, 即为通路 SHAP 分数。
|
|
9
|
+
"""
|
|
10
|
+
import csv
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
import pandas as pd
|
|
16
|
+
import xgboost as xgb
|
|
17
|
+
from xgboost import DMatrix
|
|
18
|
+
|
|
19
|
+
__all__ = ['score_pathways']
|
|
20
|
+
|
|
21
|
+
SEED = 42
|
|
22
|
+
XGB_PARAMS = dict(
|
|
23
|
+
n_estimators=300,
|
|
24
|
+
learning_rate=0.1,
|
|
25
|
+
max_depth=4,
|
|
26
|
+
tree_method='hist',
|
|
27
|
+
random_state=SEED,
|
|
28
|
+
eval_metric='logloss',
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# final 模型 = 下列单特征拼接 (与论文工作流一致)
|
|
32
|
+
FINAL_FEATS = ['kegg_min', 'hallmark_min', 'hallmark_mean',
|
|
33
|
+
'wikipathway_min', 'wikipathway_mean']
|
|
34
|
+
DB_NAME = {'kegg': 'KEGG', 'hallmark': 'Hallmark',
|
|
35
|
+
'wikipathway': 'WikiPathway'}
|
|
36
|
+
DIST_SIGMA = 2.0
|
|
37
|
+
DATA_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data')
|
|
38
|
+
|
|
39
|
+
GENE_COLS = ['string name', 'gene symbol', 'gene', 'symbol', 'marker',
|
|
40
|
+
'marker symbol', 'gene name', 'hgnc symbol', 'sym',
|
|
41
|
+
'gene id symbol']
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _dist_to_sim(X):
|
|
45
|
+
"""距离矩阵 → 相似度矩阵 (0-1), inf (不连通) 映射为 0。"""
|
|
46
|
+
out = np.exp(-X / DIST_SIGMA)
|
|
47
|
+
out[np.isinf(X)] = 0.0
|
|
48
|
+
return out
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _load_json(name):
|
|
52
|
+
with open(os.path.join(DATA_DIR, name), encoding='utf-8') as f:
|
|
53
|
+
return json.load(f)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _open_csv(path):
|
|
57
|
+
"""按 utf-8-sig -> cp1252 顺序尝试打开 CSV。"""
|
|
58
|
+
for enc in ('utf-8-sig', 'cp1252'):
|
|
59
|
+
try:
|
|
60
|
+
f = open(path, encoding=enc, newline='')
|
|
61
|
+
f.readline()
|
|
62
|
+
f.seek(0)
|
|
63
|
+
return f, csv.DictReader(f)
|
|
64
|
+
except UnicodeDecodeError:
|
|
65
|
+
f.close()
|
|
66
|
+
return open(path, encoding='cp1252', errors='replace', newline=''), None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _load_pos_genes(path, gene_set):
|
|
70
|
+
"""读标志物 CSV (兼容常见基因列名 / STRING_ID / 第一列兜底)。"""
|
|
71
|
+
ensp2sym = {}
|
|
72
|
+
with open(os.path.join(DATA_DIR, 'info_gene.csv'),
|
|
73
|
+
encoding='utf-8') as f:
|
|
74
|
+
r = csv.reader(f)
|
|
75
|
+
next(r)
|
|
76
|
+
for row in r:
|
|
77
|
+
if len(row) > 2 and row[2] not in ensp2sym:
|
|
78
|
+
ensp2sym[row[2].replace('9606.', '')] = row[1]
|
|
79
|
+
|
|
80
|
+
f, reader = _open_csv(path)
|
|
81
|
+
if reader is None:
|
|
82
|
+
raise ValueError(f'无法解码文件: {path}')
|
|
83
|
+
norm = lambda s: s.strip().lower().replace(' ', '') \
|
|
84
|
+
.replace('.', '').replace('_', '')
|
|
85
|
+
fields = reader.fieldnames or []
|
|
86
|
+
col = next((c for c in fields if norm(c) in GENE_COLS), None)
|
|
87
|
+
id_col = 'STRING_ID' if 'STRING_ID' in fields else None
|
|
88
|
+
if col is None and id_col is None:
|
|
89
|
+
col = fields[0]
|
|
90
|
+
|
|
91
|
+
pos = set()
|
|
92
|
+
for row in reader:
|
|
93
|
+
sym = (row.get(col) or '').strip()
|
|
94
|
+
if sym in ('', 'No Data') and id_col:
|
|
95
|
+
sym = ensp2sym.get((row.get(id_col) or '').strip()
|
|
96
|
+
.replace('9606.', ''), '')
|
|
97
|
+
if sym in gene_set:
|
|
98
|
+
pos.add(sym)
|
|
99
|
+
f.close()
|
|
100
|
+
return pos
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _parse_input(genes, gene_set):
|
|
104
|
+
"""输入归一化: 文件路径 / 基因列表 / 分隔符字符串 -> 网络内基因集合。"""
|
|
105
|
+
if isinstance(genes, str) and os.path.isfile(genes):
|
|
106
|
+
return _load_pos_genes(genes, gene_set)
|
|
107
|
+
if isinstance(genes, str):
|
|
108
|
+
items = [g.strip() for g in
|
|
109
|
+
genes.replace(',', ' ').replace('\t', ' ').replace(';', ' ')
|
|
110
|
+
.split()]
|
|
111
|
+
else:
|
|
112
|
+
items = [str(g).strip() for g in genes if str(g).strip()]
|
|
113
|
+
return set(items) & gene_set
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _load_features():
|
|
117
|
+
"""返回 final 特征矩阵 (17613 x N) 与通路元数据 [(db, stat, pathway)]。"""
|
|
118
|
+
parts, meta = [], []
|
|
119
|
+
for m in FINAL_FEATS:
|
|
120
|
+
db, stat = m.rsplit('_', 1)
|
|
121
|
+
X = np.load(os.path.join(DATA_DIR, f'{m}_dist.npz'))['arr_0']
|
|
122
|
+
parts.append(_dist_to_sim(X.astype(np.float64)).astype(np.float32))
|
|
123
|
+
with open(os.path.join(DATA_DIR, f'{db}_pathways.json'),
|
|
124
|
+
encoding='utf-8') as f:
|
|
125
|
+
meta.extend((db, stat, pw) for pw in json.load(f))
|
|
126
|
+
return np.hstack(parts), meta
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def score_pathways(genes, verbose=True):
|
|
130
|
+
"""对输入基因列表做 SHAP 通路评分。
|
|
131
|
+
|
|
132
|
+
参数
|
|
133
|
+
----
|
|
134
|
+
genes : list[str] 或 str
|
|
135
|
+
基因符号列表, 或包含基因符号的 CSV/TSV/TXT 文件路径
|
|
136
|
+
(文件需含常见基因列, 如 "gene symbol"/"gene"/"symbol" 等),
|
|
137
|
+
或逗号/空格/换行分隔的字符串。
|
|
138
|
+
verbose : bool
|
|
139
|
+
是否打印进度。
|
|
140
|
+
|
|
141
|
+
返回
|
|
142
|
+
----
|
|
143
|
+
dict[str, float]
|
|
144
|
+
{通路名: mean_abs_shap}, 按 SHAP 分数从大到小排序;
|
|
145
|
+
仅保留分数 > 0 的通路。
|
|
146
|
+
"""
|
|
147
|
+
gene_list = _load_json('genes.json')
|
|
148
|
+
gene_set = set(gene_list)
|
|
149
|
+
|
|
150
|
+
pos = _parse_input(genes, gene_set)
|
|
151
|
+
if not pos:
|
|
152
|
+
raise ValueError('输入中未匹配到任何网络内基因, '
|
|
153
|
+
'请检查基因符号 (建议使用 HGNC 标准符号)')
|
|
154
|
+
if verbose:
|
|
155
|
+
print(f'网络基因: {len(gene_set)}, 匹配 biomarker: {len(pos)}')
|
|
156
|
+
|
|
157
|
+
X_all, meta = _load_features()
|
|
158
|
+
y_all = np.array([1 if g in pos else 0 for g in gene_list], np.int32)
|
|
159
|
+
|
|
160
|
+
# 全局 1:1 欠采样 (正样本全留, 负样本随机抽至等量, SEED=42)
|
|
161
|
+
rng = np.random.default_rng(SEED)
|
|
162
|
+
pos_idx = np.where(y_all == 1)[0]
|
|
163
|
+
neg_idx = np.where(y_all == 0)[0]
|
|
164
|
+
n = min(len(pos_idx), len(neg_idx))
|
|
165
|
+
if len(pos_idx) > len(neg_idx):
|
|
166
|
+
keep = rng.choice(pos_idx, size=n, replace=False)
|
|
167
|
+
sample_idx = np.sort(np.concatenate([keep, neg_idx]))
|
|
168
|
+
else:
|
|
169
|
+
keep = rng.choice(neg_idx, size=n, replace=False)
|
|
170
|
+
sample_idx = np.sort(np.concatenate([pos_idx, keep]))
|
|
171
|
+
y = y_all[sample_idx]
|
|
172
|
+
if verbose:
|
|
173
|
+
print(f'欠采样后: pos={int(y.sum())}, neg={int((y == 0).sum())}, '
|
|
174
|
+
f'特征 {X_all.shape[1]} 维, 训练中 ...')
|
|
175
|
+
|
|
176
|
+
clf = xgb.XGBClassifier(**XGB_PARAMS)
|
|
177
|
+
clf.fit(X_all[sample_idx], y)
|
|
178
|
+
|
|
179
|
+
# SHAP 在全部基因上计算
|
|
180
|
+
contribs = clf.get_booster().predict(DMatrix(X_all), pred_contribs=True)
|
|
181
|
+
sv = contribs[:, :-1]
|
|
182
|
+
|
|
183
|
+
rank = pd.DataFrame(meta, columns=['dataset', 'stat', 'pathway'])
|
|
184
|
+
rank['mean_abs_shap'] = np.abs(sv).mean(axis=0)
|
|
185
|
+
rank['dataset'] = rank['dataset'].map(DB_NAME)
|
|
186
|
+
|
|
187
|
+
# 通路级聚合: 同名通路取最大分数, 仅保留分数 > 0, 按分数降序
|
|
188
|
+
result = (rank.groupby('pathway')['mean_abs_shap'].max()
|
|
189
|
+
.loc[lambda s: s > 0]
|
|
190
|
+
.sort_values(ascending=False).to_dict())
|
|
191
|
+
if verbose:
|
|
192
|
+
top = list(result.items())[:5]
|
|
193
|
+
print('Top 5 通路: ' + ', '.join(
|
|
194
|
+
f'{pw}={v:.4f}' for pw, v in top))
|
|
195
|
+
return result
|