PyOVERCAST 1.0.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.
- pyovercast-1.0.0/LICENSE +0 -0
- pyovercast-1.0.0/PKG-INFO +86 -0
- pyovercast-1.0.0/PyOVERCAST/__init__.py +0 -0
- pyovercast-1.0.0/PyOVERCAST/clinks.py +177 -0
- pyovercast-1.0.0/PyOVERCAST/predict.py +631 -0
- pyovercast-1.0.0/PyOVERCAST.egg-info/PKG-INFO +86 -0
- pyovercast-1.0.0/PyOVERCAST.egg-info/SOURCES.txt +12 -0
- pyovercast-1.0.0/PyOVERCAST.egg-info/dependency_links.txt +1 -0
- pyovercast-1.0.0/PyOVERCAST.egg-info/entry_points.txt +3 -0
- pyovercast-1.0.0/PyOVERCAST.egg-info/requires.txt +27 -0
- pyovercast-1.0.0/PyOVERCAST.egg-info/top_level.txt +1 -0
- pyovercast-1.0.0/README.md +46 -0
- pyovercast-1.0.0/pyproject.toml +62 -0
- pyovercast-1.0.0/setup.cfg +4 -0
pyovercast-1.0.0/LICENSE
ADDED
|
File without changes
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: PyOVERCAST
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A Python package for mining key transcription factors from transcriptome data.
|
|
5
|
+
Author-email: Tinghua Huang <thua45@126.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/thua45/PyOVERCAST
|
|
8
|
+
Project-URL: Documentation, https://github.com/thua45/PyOVERCAST#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/thua45/PyOVERCAST
|
|
10
|
+
Project-URL: Issues, https://github.com/thua45/PyOVERCAST/issues
|
|
11
|
+
Keywords: PyOVERCAST,Transcription Factor,Binding Site,Transcriptome Data
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: numpy==1.26; sys_platform == "darwin"
|
|
21
|
+
Requires-Dist: numpy>=2.0; sys_platform == "win32"
|
|
22
|
+
Requires-Dist: numpy>=2.0; sys_platform == "linux"
|
|
23
|
+
Requires-Dist: pandas==3.0; sys_platform == "darwin"
|
|
24
|
+
Requires-Dist: pandas>=3.0; sys_platform == "win32"
|
|
25
|
+
Requires-Dist: pandas>=3.0; sys_platform == "linux"
|
|
26
|
+
Requires-Dist: statsmodels==0.14; sys_platform == "darwin"
|
|
27
|
+
Requires-Dist: statsmodels>=0.14; sys_platform == "win32"
|
|
28
|
+
Requires-Dist: statsmodels>=0.14; sys_platform == "linux"
|
|
29
|
+
Requires-Dist: seaborn==0.13; sys_platform == "darwin"
|
|
30
|
+
Requires-Dist: seaborn>=0.13; sys_platform == "win32"
|
|
31
|
+
Requires-Dist: seaborn>=0.13; sys_platform == "linux"
|
|
32
|
+
Requires-Dist: matplotlib==3.11; sys_platform == "darwin"
|
|
33
|
+
Requires-Dist: matplotlib>=3.11; sys_platform == "win32"
|
|
34
|
+
Requires-Dist: matplotlib>=3.11; sys_platform == "linux"
|
|
35
|
+
Provides-Extra: dev
|
|
36
|
+
Requires-Dist: matplotlib; extra == "dev"
|
|
37
|
+
Provides-Extra: test
|
|
38
|
+
Requires-Dist: matplotlib; extra == "test"
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
# PyOVERCAST
|
|
42
|
+
|
|
43
|
+
A Python package for mining key transcription factors from transcriptome data.
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install numpy pandas statsmodels seaborn matplotlib PyOVERCAST
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Usage
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from PyOVERCAST import clinks, predict
|
|
55
|
+
|
|
56
|
+
if __name__ == '__main__':
|
|
57
|
+
# download TF-target set, only need to run once!!!
|
|
58
|
+
clinks.download_data()
|
|
59
|
+
|
|
60
|
+
# list available TF-target set
|
|
61
|
+
sets_names = clinks.get_sets(species='Homo sapiens')
|
|
62
|
+
print(sets_names)
|
|
63
|
+
|
|
64
|
+
# list avaiable TFs
|
|
65
|
+
tfs_codes = clinks.get_tfs(set_name='human_hocomoco_CLink_wtcoor_1w_0.8')
|
|
66
|
+
print(tfs_codes)
|
|
67
|
+
|
|
68
|
+
# get targets
|
|
69
|
+
targets = clinks.get_targets(set_name='human_hocomoco_CLink_wtcoor_1w_0.8', tf='NFKB1_HUMAN.H11MO.1.B')
|
|
70
|
+
print(targets)
|
|
71
|
+
|
|
72
|
+
# predict one DEG-list
|
|
73
|
+
result = predict.olcr(set_names=['human_hocomoco_CLink_wtcoor_1w_0.8', 'human_jaspar_CLink_wtcoor_1w_0.8'], list_file='./PyOVERCAST_data/input_deg-list.txt', win=30, thread_n=16)
|
|
74
|
+
|
|
75
|
+
# or predict one DEG-list with bootstrap
|
|
76
|
+
result = predict.olcr_bootstrap(set_names=['human_hocomoco_CLink_wtcoor_1w_0.8', 'human_jaspar_CLink_wtcoor_1w_0.8'], list_file='./PyOVERCAST_data/input_deg-list.txt', win=30, thread_n=16)
|
|
77
|
+
|
|
78
|
+
# save result to text file
|
|
79
|
+
result.to_csv('output.txt', sep='\t', index=False, encoding='utf-8-sig')
|
|
80
|
+
|
|
81
|
+
# plot OLC matrix
|
|
82
|
+
predict.plot_olc(set_names=['human_hocomoco_CLink_wtcoor_1w_0.8', 'human_jaspar_CLink_wtcoor_1w_0.8'], list_file='./PyOVERCAST_data/input_deg-list.txt', tf='MA0844.2_XBP1', win=30)
|
|
83
|
+
|
|
84
|
+
# plot fitted 3D U-surface
|
|
85
|
+
predict.plot_fit3D(set_names=['human_hocomoco_CLink_wtcoor_1w_0.8', 'human_jaspar_CLink_wtcoor_1w_0.8'], list_file='./PyOVERCAST_data/input_deg-list.txt', tf='MA0844.2_XBP1', win=30)
|
|
86
|
+
```
|
|
File without changes
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# from importlib import resources
|
|
2
|
+
import urllib.request
|
|
3
|
+
import zipfile
|
|
4
|
+
import os
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
overcast_data_dir = './OVERCAST_data'
|
|
8
|
+
|
|
9
|
+
def download_with_progress(url, filename=None):
|
|
10
|
+
"""
|
|
11
|
+
带进度显示的下载函数
|
|
12
|
+
"""
|
|
13
|
+
def progress_callback(block_num, block_size, total_size):
|
|
14
|
+
"""
|
|
15
|
+
进度回调函数
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
block_num: 当前已下载的块数量
|
|
19
|
+
block_size: 每个块的大小(字节)
|
|
20
|
+
total_size: 文件总大小(字节)
|
|
21
|
+
"""
|
|
22
|
+
downloaded = block_num * block_size
|
|
23
|
+
percent = downloaded / total_size * 100 if total_size > 0 else 0
|
|
24
|
+
|
|
25
|
+
# 限制显示频率,避免刷新太快
|
|
26
|
+
if block_num % 100 == 0 or downloaded >= total_size:
|
|
27
|
+
print(f"\rProgress: {percent:.1f}% ({downloaded}/{total_size} bytes)", end='', flush=True)
|
|
28
|
+
|
|
29
|
+
# 如果没有指定文件名,从URL中提取
|
|
30
|
+
if filename is None:
|
|
31
|
+
filename = os.path.basename(url)
|
|
32
|
+
|
|
33
|
+
print(f"start download: {url}")
|
|
34
|
+
print(f"save as: {filename}")
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
urllib.request.urlretrieve(url, filename, progress_callback)
|
|
38
|
+
print("donwload sucessful")
|
|
39
|
+
except Exception as e:
|
|
40
|
+
print(f"donwload failed: {e}")
|
|
41
|
+
exit(1)
|
|
42
|
+
|
|
43
|
+
# 使用示例
|
|
44
|
+
# download_with_progress("https://example.com/largefile.zip", "downloaded_file.zip")
|
|
45
|
+
|
|
46
|
+
def extractall_individual_files(zip_path, extract_path):
|
|
47
|
+
"""
|
|
48
|
+
使用 extract() 方法逐个解压文件,确保覆盖
|
|
49
|
+
"""
|
|
50
|
+
os.makedirs(extract_path, exist_ok=True)
|
|
51
|
+
|
|
52
|
+
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
|
|
53
|
+
# 获取所有成员信息
|
|
54
|
+
members = zip_ref.infolist()
|
|
55
|
+
|
|
56
|
+
print(f"extracting {len(members)} file...")
|
|
57
|
+
|
|
58
|
+
for i, member in enumerate(members, 1):
|
|
59
|
+
try:
|
|
60
|
+
# 直接使用 extract() 方法,它会处理覆盖
|
|
61
|
+
zip_ref.extract(member, extract_path)
|
|
62
|
+
|
|
63
|
+
if i % 10 == 0 or i == len(members):
|
|
64
|
+
print(f"progress: {i}/{len(members)}")
|
|
65
|
+
|
|
66
|
+
except Exception as e:
|
|
67
|
+
print(f"extracting failed {member.filename}: {e}")
|
|
68
|
+
exit(1)
|
|
69
|
+
|
|
70
|
+
print("extracting finished")
|
|
71
|
+
|
|
72
|
+
# 使用示例
|
|
73
|
+
# extractall_individual_files("example.zip", "extracted_files")
|
|
74
|
+
|
|
75
|
+
def download_and_extract_zip(url, extract_to):
|
|
76
|
+
"""
|
|
77
|
+
下载ZIP文件并解压到指定目录
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
url: ZIP文件的URL
|
|
81
|
+
extract_to: 解压目标目录
|
|
82
|
+
"""
|
|
83
|
+
# 创建目标目录
|
|
84
|
+
Path(extract_to).mkdir(parents=True, exist_ok=True)
|
|
85
|
+
|
|
86
|
+
# 临时ZIP文件路径
|
|
87
|
+
zip_path = os.path.join(extract_to, "temp.zip")
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
# 下载文件
|
|
91
|
+
# print(f"downloading: {url}")
|
|
92
|
+
# urllib.request.urlretrieve(url, zip_path)
|
|
93
|
+
download_with_progress(url, zip_path)
|
|
94
|
+
# print("donwload finished")
|
|
95
|
+
|
|
96
|
+
# 解压文件
|
|
97
|
+
# print(f"extracting: {extract_to}")
|
|
98
|
+
# with zipfile.ZipFile(zip_path, 'r') as zip_ref:
|
|
99
|
+
# zip_ref.extractall(extract_to, overwrite=True)
|
|
100
|
+
extractall_individual_files(zip_path, extract_to)
|
|
101
|
+
# print("extract finished")
|
|
102
|
+
|
|
103
|
+
except Exception as e:
|
|
104
|
+
print(f"错误: {e}")
|
|
105
|
+
return False
|
|
106
|
+
finally:
|
|
107
|
+
# 清理临时ZIP文件
|
|
108
|
+
if os.path.exists(zip_path):
|
|
109
|
+
os.remove(zip_path)
|
|
110
|
+
print("clearing temp files")
|
|
111
|
+
|
|
112
|
+
return True
|
|
113
|
+
|
|
114
|
+
def download_data(data_dir=None):
|
|
115
|
+
if data_dir == None:
|
|
116
|
+
data_dir = overcast_data_dir
|
|
117
|
+
if not (os.path.exists(data_dir) and os.path.isdir(data_dir)):
|
|
118
|
+
print(data_dir, 'not exist, creating...')
|
|
119
|
+
folder_path = Path(data_dir)
|
|
120
|
+
folder_path.mkdir(exist_ok=True)
|
|
121
|
+
url = "http://www.thua45.cn/overcast/OVERCAST_data.zip"
|
|
122
|
+
extract_dir = data_dir
|
|
123
|
+
# extract_dir = "./downloaded_content"
|
|
124
|
+
success = download_and_extract_zip(url, extract_dir)
|
|
125
|
+
if success:
|
|
126
|
+
print("models installed successful")
|
|
127
|
+
|
|
128
|
+
def get_sets(species=None, data_dir=None):
|
|
129
|
+
if data_dir == None:
|
|
130
|
+
data_dir = overcast_data_dir
|
|
131
|
+
index_file = data_dir + '/sets_index.txt'
|
|
132
|
+
if not os.path.exists(index_file):
|
|
133
|
+
print('sets_index.txt not exist, you may need to run download_data() first')
|
|
134
|
+
exit(1)
|
|
135
|
+
header = ''
|
|
136
|
+
result = []
|
|
137
|
+
for line in open(index_file, 'r'):
|
|
138
|
+
if line[0] == "#":
|
|
139
|
+
header = line.rstrip()
|
|
140
|
+
continue
|
|
141
|
+
lblocks = line.rstrip().split('\t')
|
|
142
|
+
if species != None and lblocks[1] != species:
|
|
143
|
+
continue
|
|
144
|
+
result.append(lblocks[0])
|
|
145
|
+
return result
|
|
146
|
+
|
|
147
|
+
def get_tfs(set_name=None, data_dir=None):
|
|
148
|
+
if data_dir == None:
|
|
149
|
+
data_dir = overcast_data_dir
|
|
150
|
+
sets_file = data_dir + '/TF-target_sets/' + set_name + '.txt'
|
|
151
|
+
if not os.path.exists(sets_file):
|
|
152
|
+
print('sets_file not exist, you may need to run download_data() first')
|
|
153
|
+
exit(1)
|
|
154
|
+
result = set()
|
|
155
|
+
for line in open(sets_file, 'r'):
|
|
156
|
+
if line[0] == "#":
|
|
157
|
+
continue
|
|
158
|
+
lblocks = line.rstrip().split('\t')
|
|
159
|
+
result.add(lblocks[0])
|
|
160
|
+
return list(result)
|
|
161
|
+
|
|
162
|
+
def get_targets(set_name=None, tf=None, data_dir=None):
|
|
163
|
+
if data_dir == None:
|
|
164
|
+
data_dir = overcast_data_dir
|
|
165
|
+
sets_file = data_dir + '/TF-target_sets/' + set_name + '.txt'
|
|
166
|
+
if not os.path.exists(sets_file):
|
|
167
|
+
print('sets_file not exist, you may need to run download_data() first')
|
|
168
|
+
exit(1)
|
|
169
|
+
result = set()
|
|
170
|
+
for line in open(sets_file, 'r'):
|
|
171
|
+
if line[0] == "#":
|
|
172
|
+
continue
|
|
173
|
+
lblocks = line.rstrip().split('\t')
|
|
174
|
+
if tf != None and lblocks[0] != tf:
|
|
175
|
+
continue
|
|
176
|
+
result.add(lblocks[1])
|
|
177
|
+
return list(result)
|
|
@@ -0,0 +1,631 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
import os
|
|
3
|
+
import math
|
|
4
|
+
import time
|
|
5
|
+
from collections import defaultdict
|
|
6
|
+
from operator import itemgetter
|
|
7
|
+
import numpy as np
|
|
8
|
+
import pandas as pd
|
|
9
|
+
import matplotlib.pyplot as plt
|
|
10
|
+
import statsmodels.api as sm
|
|
11
|
+
# 设置中文字体,避免 DejaVu Sans 缺失中文字符
|
|
12
|
+
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'PingFang SC', 'Noto Sans CJK SC', 'Arial Unicode MS'] # 指定默认字体
|
|
13
|
+
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
|
|
14
|
+
import multiprocessing as mp
|
|
15
|
+
import seaborn as sns
|
|
16
|
+
|
|
17
|
+
overcast_data_dir = './OVERCAST_data'
|
|
18
|
+
|
|
19
|
+
def read_tfbs_file(file, tfbs):
|
|
20
|
+
for line in open(file, 'r'):
|
|
21
|
+
if line[0] == '#':
|
|
22
|
+
continue
|
|
23
|
+
lblocks = line.rstrip().split('\t')
|
|
24
|
+
tfbs[lblocks[0]]['gene'].append(lblocks[1])
|
|
25
|
+
tfbs[lblocks[0]]['LogFC'].append(float(lblocks[2]))
|
|
26
|
+
|
|
27
|
+
def read_gene_sets(files):
|
|
28
|
+
tfbs = defaultdict(lambda: defaultdict(list))
|
|
29
|
+
for file in files:
|
|
30
|
+
read_tfbs_file(file, tfbs)
|
|
31
|
+
tfbs_array = []
|
|
32
|
+
tfs = list(tfbs.keys())
|
|
33
|
+
for tf in tfs:
|
|
34
|
+
df = pd.DataFrame({"gene": tfbs[tf]['gene'], "logFC": tfbs[tf]['LogFC']}, index=tfbs[tf]['gene'])
|
|
35
|
+
tfbs_array.append([tf, df])
|
|
36
|
+
return tfbs_array
|
|
37
|
+
|
|
38
|
+
def read_DEG_list(file_in):
|
|
39
|
+
genes = []
|
|
40
|
+
LogFCs = []
|
|
41
|
+
#file_in = 'XBP1_DEG-list.txt'
|
|
42
|
+
for line in open(file_in, 'r'):
|
|
43
|
+
if line[0] == '#':
|
|
44
|
+
continue
|
|
45
|
+
lblocks = line.rstrip().split('\t')
|
|
46
|
+
genes.append(lblocks[0])
|
|
47
|
+
LogFCs.append(float(lblocks[1]))
|
|
48
|
+
df = pd.DataFrame({"gene": genes, "logFC": LogFCs}, index=genes)
|
|
49
|
+
return df
|
|
50
|
+
|
|
51
|
+
def slide_window_inclusive(lst, w, s):
|
|
52
|
+
lst_splits = []
|
|
53
|
+
l_idxs = []
|
|
54
|
+
n = len(lst)
|
|
55
|
+
for start in range(0, n, s):
|
|
56
|
+
lst_splits.append(lst[start:min(start + w, n)])
|
|
57
|
+
l_idxs.append(start + math.floor(min(w, n - start) / 2.0))
|
|
58
|
+
return l_idxs, lst_splits
|
|
59
|
+
|
|
60
|
+
def rrho_matrix_str(list1, list2, window=20, universe=None):
|
|
61
|
+
# unique genes preserving order
|
|
62
|
+
list1 = list(dict.fromkeys([str(x) for x in list1]))
|
|
63
|
+
list2 = list(dict.fromkeys([str(x) for x in list2]))
|
|
64
|
+
|
|
65
|
+
l_idxs1, lst_splits1 = slide_window_inclusive(list1, math.floor(len(list1) / window), math.floor(len(list1) / window / 3.0))
|
|
66
|
+
l_idxs2, lst_splits2 = slide_window_inclusive(list2, math.floor(len(list2) / window), math.floor(len(list2) / window / 3.0))
|
|
67
|
+
step = math.floor(window / 3)
|
|
68
|
+
|
|
69
|
+
mat = np.zeros((len(l_idxs1), len(l_idxs2)), dtype=float)
|
|
70
|
+
# print(mat.shape)
|
|
71
|
+
mi = -1
|
|
72
|
+
for lst_i in lst_splits1:
|
|
73
|
+
mi += 1
|
|
74
|
+
mj = -1
|
|
75
|
+
for lst_j in lst_splits2:
|
|
76
|
+
mj += 1
|
|
77
|
+
mat[mi, mj] = len(set(lst_i) & set(lst_j))
|
|
78
|
+
|
|
79
|
+
# mat = mat - np.mean(mat)
|
|
80
|
+
rrho_df = pd.DataFrame(mat, index=l_idxs1, columns=l_idxs2)
|
|
81
|
+
rrho_df.index.name = "Prefix_list1"
|
|
82
|
+
rrho_df.columns.name = "Prefix_list2"
|
|
83
|
+
return rrho_df
|
|
84
|
+
|
|
85
|
+
def Z_to_frame(Z):
|
|
86
|
+
# ============================
|
|
87
|
+
# 1. 生成模拟数据(网格点云)
|
|
88
|
+
# ============================
|
|
89
|
+
# 假设正方形边长为2,范围[-1, 1],生成20x20网格
|
|
90
|
+
np.random.seed(42) # 固定随机种子,保证可复现
|
|
91
|
+
x = np.linspace(-1, 1, Z.shape[1])
|
|
92
|
+
y = np.linspace(-1, 1, Z.shape[0])
|
|
93
|
+
X, Y = np.meshgrid(x, y)
|
|
94
|
+
|
|
95
|
+
# 展平成一维数组,便于回归
|
|
96
|
+
df = pd.DataFrame({
|
|
97
|
+
'x': X.ravel(),
|
|
98
|
+
'y': Y.ravel(),
|
|
99
|
+
'z': Z.ravel()
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
return df
|
|
103
|
+
|
|
104
|
+
def compute_dtci_core(df, x_col='x', y_col='y', z_col='z',
|
|
105
|
+
c=1.0, alpha=1.0, beta_w=0.5, gamma=1.0):
|
|
106
|
+
"""
|
|
107
|
+
核心DTCI计算 —— 使用 statsmodels.OLS
|
|
108
|
+
|
|
109
|
+
拟合模型: z = β0 + β1·x + β2·y + β3·(x·y) + ε
|
|
110
|
+
|
|
111
|
+
参数:
|
|
112
|
+
df: DataFrame,包含 x, y, z 列
|
|
113
|
+
x_col, y_col, z_col: 列名
|
|
114
|
+
c: 深度调节系数
|
|
115
|
+
alpha, beta_w, gamma: DI, SI, QI 的权重指数
|
|
116
|
+
|
|
117
|
+
返回:
|
|
118
|
+
DTCI, beta_hat, R2_adj, p_beta3, residuals
|
|
119
|
+
"""
|
|
120
|
+
x = df[x_col].values
|
|
121
|
+
y = df[y_col].values
|
|
122
|
+
z = df[z_col].values
|
|
123
|
+
xy = x * y
|
|
124
|
+
|
|
125
|
+
# 使用 statsmodels.OLS(自动处理常数项、标准误和p值)
|
|
126
|
+
X_design = sm.add_constant(pd.DataFrame({'x': x, 'y': y, 'xy': xy}))
|
|
127
|
+
model = sm.OLS(z, X_design).fit()
|
|
128
|
+
|
|
129
|
+
# 提取系数 [const, x, y, xy]
|
|
130
|
+
beta_hat = model.params.values
|
|
131
|
+
beta0, beta1, beta2, beta3 = beta_hat
|
|
132
|
+
|
|
133
|
+
# 模型质量
|
|
134
|
+
R2 = model.rsquared
|
|
135
|
+
R2_adj = model.rsquared_adj
|
|
136
|
+
|
|
137
|
+
# 系数显著性(statsmodels 自动计算,更准确)
|
|
138
|
+
p_values = model.pvalues.values
|
|
139
|
+
|
|
140
|
+
# 数据波动
|
|
141
|
+
z_std = np.std(z, ddof=1)
|
|
142
|
+
|
|
143
|
+
# 子指标
|
|
144
|
+
DI = np.abs(beta3) / (np.abs(beta3) + c * z_std)
|
|
145
|
+
SI = min(1.0, -np.log10(max(p_values[3], 1e-10)) / 25.0)
|
|
146
|
+
QI = max(0.0, R2_adj)
|
|
147
|
+
|
|
148
|
+
total_weight = alpha + beta_w + gamma
|
|
149
|
+
DTCI = (DI**alpha * SI**beta_w * QI**gamma) ** (1.0 / total_weight)
|
|
150
|
+
|
|
151
|
+
return DTCI, beta_hat, R2_adj, p_values[3], model.resid
|
|
152
|
+
|
|
153
|
+
def bootstrap_parametric_h0(df, n_bootstrap=2000, random_state=42):
|
|
154
|
+
"""
|
|
155
|
+
参数化Bootstrap:在H0(无对角线趋势,即beta3=0)下生成数据
|
|
156
|
+
|
|
157
|
+
步骤:
|
|
158
|
+
1. 在H0下拟合模型:z = beta0 + beta1·x + beta2·y + eps
|
|
159
|
+
2. 获取拟合值和残差
|
|
160
|
+
3. 从残差中有放回抽样,构造新数据
|
|
161
|
+
4. 对新数据计算DTCI
|
|
162
|
+
5. 比较观测DTCI与零分布,计算p值
|
|
163
|
+
"""
|
|
164
|
+
np.random.seed(random_state)
|
|
165
|
+
n = len(df)
|
|
166
|
+
x = df['x'].values
|
|
167
|
+
y = df['y'].values
|
|
168
|
+
z = df['z'].values
|
|
169
|
+
|
|
170
|
+
# 步骤1:在H0下拟合(不含交互项)
|
|
171
|
+
X_h0 = sm.add_constant(pd.DataFrame({'x': x, 'y': y}))
|
|
172
|
+
model_h0 = sm.OLS(z, X_h0).fit()
|
|
173
|
+
z_pred_h0 = model_h0.fittedvalues
|
|
174
|
+
resid_h0 = model_h0.resid
|
|
175
|
+
|
|
176
|
+
# 观测DTCI(全模型)
|
|
177
|
+
# dtci_obs = dtci_compute_dtci_core(df)[0]
|
|
178
|
+
dtci_obs, beta_hat, dtci_pval = itemgetter(0, 1, 3)(compute_dtci_core(df))
|
|
179
|
+
|
|
180
|
+
# 步骤3-4:生成零分布
|
|
181
|
+
dtci_null = []
|
|
182
|
+
for i in range(n_bootstrap):
|
|
183
|
+
resid_boot = np.random.choice(resid_h0, size=n, replace=True)
|
|
184
|
+
z_boot = z_pred_h0 + resid_boot
|
|
185
|
+
|
|
186
|
+
df_boot = pd.DataFrame({'x': x, 'y': y, 'z': z_boot})
|
|
187
|
+
try:
|
|
188
|
+
dtci_i, _, _, _, _ = compute_dtci_core(df_boot)
|
|
189
|
+
dtci_null.append(dtci_i)
|
|
190
|
+
except:
|
|
191
|
+
pass
|
|
192
|
+
|
|
193
|
+
dtci_null = np.array(dtci_null)
|
|
194
|
+
|
|
195
|
+
# 步骤5:计算p值 (H1: DTCI > 0)
|
|
196
|
+
p_value = np.mean(dtci_null >= dtci_obs)
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
'method': '参数化Bootstrap (H0: beta3=0)',
|
|
200
|
+
'dtci_obs': dtci_obs,
|
|
201
|
+
'dtci_pval': dtci_pval,
|
|
202
|
+
'beta3': beta_hat[3],
|
|
203
|
+
'dtci_null': dtci_null,
|
|
204
|
+
'p_value': p_value,
|
|
205
|
+
'se_null': np.std(dtci_null, ddof=1),
|
|
206
|
+
'mean_null': np.mean(dtci_null),
|
|
207
|
+
'n_boot': len(dtci_null)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
def rrho_coor(df1, df2, win):
|
|
211
|
+
gene_col1 = df1.columns[0]
|
|
212
|
+
score_col1 = df1.columns[1]
|
|
213
|
+
df1 = df1.sort_values(score_col1, ascending=False)
|
|
214
|
+
genes1 = df1[gene_col1].astype(str).tolist()
|
|
215
|
+
|
|
216
|
+
# remove duplicates while preserving order
|
|
217
|
+
genes1 = list(dict.fromkeys(genes1))
|
|
218
|
+
|
|
219
|
+
gene_col2 = df1.columns[0]
|
|
220
|
+
score_col2 = df2.columns[1]
|
|
221
|
+
df2 = df2.sort_values(score_col2, ascending=False)
|
|
222
|
+
genes2 = df2[gene_col2].astype(str).tolist()
|
|
223
|
+
# remove duplicates while preserving order
|
|
224
|
+
genes2 = list(dict.fromkeys(genes2))
|
|
225
|
+
|
|
226
|
+
window = win
|
|
227
|
+
if (len(genes1) < window*3 or len(genes2) < window*3):
|
|
228
|
+
return 0.0, 1.0, 0.0
|
|
229
|
+
rrho_df = rrho_matrix_str(genes1, genes2, window=window)
|
|
230
|
+
|
|
231
|
+
if rrho_df.shape[0] == 0 or rrho_df.shape[1] == 0 or (rrho_df > 0.0).sum().sum() == 0:
|
|
232
|
+
return 0.0, 1.0, 0.0
|
|
233
|
+
|
|
234
|
+
#beta_xy, pval_xy = td_coor(rrho_df.to_numpy())
|
|
235
|
+
df = Z_to_frame(rrho_df.to_numpy())
|
|
236
|
+
DTCI, betas, pval_xy = itemgetter(0, 1, 3)(compute_dtci_core(df))
|
|
237
|
+
|
|
238
|
+
return betas[3], pval_xy, DTCI
|
|
239
|
+
|
|
240
|
+
def plot_rrho(rrho_df, out_file=None, title="RRLO Matrix", cmap="magma"):
|
|
241
|
+
"""
|
|
242
|
+
Plot RRHO heatmap.
|
|
243
|
+
"""
|
|
244
|
+
plt.figure(figsize=(6, 5), dpi=150)
|
|
245
|
+
ax = sns.heatmap(
|
|
246
|
+
rrho_df,
|
|
247
|
+
cmap=cmap,
|
|
248
|
+
#cbar_kws={"label": r"$-\log_{10}(p)$"},
|
|
249
|
+
cbar_kws={"label": r"Overlaps"},
|
|
250
|
+
xticklabels=max(1, len(rrho_df.columns) // 10),
|
|
251
|
+
yticklabels=max(1, len(rrho_df.index) // 10),
|
|
252
|
+
)
|
|
253
|
+
ax.invert_yaxis() # 添加这一行,让 [0,0] 位于左下角
|
|
254
|
+
ax.set_xlabel("Prefix overlap in TF-target set")
|
|
255
|
+
ax.set_ylabel("Prefix overlap in DEG list")
|
|
256
|
+
ax.set_title(title, fontsize=10)
|
|
257
|
+
plt.tight_layout()
|
|
258
|
+
|
|
259
|
+
if out_file:
|
|
260
|
+
plt.savefig(out_file + '.pdf', dpi=300, bbox_inches="tight")
|
|
261
|
+
plt.savefig(out_file + '.svg', dpi=300, bbox_inches="tight")
|
|
262
|
+
plt.show()
|
|
263
|
+
|
|
264
|
+
def plot_olc_plot(df1, df2, win):
|
|
265
|
+
gene_col1 = df1.columns[0]
|
|
266
|
+
score_col1 = df1.columns[1]
|
|
267
|
+
df1 = df1.sort_values(score_col1, ascending=False)
|
|
268
|
+
genes1 = df1[gene_col1].astype(str).tolist()
|
|
269
|
+
|
|
270
|
+
# remove duplicates while preserving order
|
|
271
|
+
genes1 = list(dict.fromkeys(genes1))
|
|
272
|
+
|
|
273
|
+
gene_col2 = df1.columns[0]
|
|
274
|
+
score_col2 = df2.columns[1]
|
|
275
|
+
df2 = df2.sort_values(score_col2, ascending=False)
|
|
276
|
+
genes2 = df2[gene_col2].astype(str).tolist()
|
|
277
|
+
# remove duplicates while preserving order
|
|
278
|
+
genes2 = list(dict.fromkeys(genes2))
|
|
279
|
+
|
|
280
|
+
window = win
|
|
281
|
+
if (len(genes1) < window*3 or len(genes2) < window*3):
|
|
282
|
+
return 0.0, 1.0, 0.0
|
|
283
|
+
rrho_df = rrho_matrix_str(genes1, genes2, window=window)
|
|
284
|
+
plot_rrho(rrho_df)
|
|
285
|
+
|
|
286
|
+
def plot_3D_surface(heatmap_a, out_file=None):
|
|
287
|
+
row_n, col_n = heatmap_a.shape
|
|
288
|
+
x = np.linspace(-1, 1, col_n)
|
|
289
|
+
y = np.linspace(-1, 1, row_n)
|
|
290
|
+
X, Y = np.meshgrid(x, y)
|
|
291
|
+
Z_data = heatmap_a
|
|
292
|
+
|
|
293
|
+
def fit_surface(Z, X, Y):
|
|
294
|
+
df = pd.DataFrame({
|
|
295
|
+
'x': X.ravel(),
|
|
296
|
+
'y': Y.ravel(),
|
|
297
|
+
'z': Z.ravel()
|
|
298
|
+
})
|
|
299
|
+
df['xy'] = df['x'] * df['y'] # 交互项
|
|
300
|
+
X_design = sm.add_constant(df[['x', 'y', 'xy']])
|
|
301
|
+
y_dep = df['z']
|
|
302
|
+
model = sm.OLS(y_dep, X_design).fit()
|
|
303
|
+
beta = model.params
|
|
304
|
+
# print(beta)
|
|
305
|
+
|
|
306
|
+
Z_fit = beta['const'] + beta['x']*X + beta['y']*Y + beta['xy']*X*Y
|
|
307
|
+
return beta, Z_fit
|
|
308
|
+
|
|
309
|
+
beta, Z_fit = fit_surface(Z_data.T, X, Y)
|
|
310
|
+
|
|
311
|
+
fig = plt.figure(figsize=(6, 4), dpi=150)
|
|
312
|
+
ax = fig.add_subplot(111, projection='3d')
|
|
313
|
+
|
|
314
|
+
color = '#e94560' #['#e94560', '#0f3460', '#0f3460']
|
|
315
|
+
|
|
316
|
+
ax.plot_surface(X, Y, Z_fit, cmap='plasma', alpha=0.8)
|
|
317
|
+
|
|
318
|
+
ax.tick_params(axis='x', pad=-5) # x轴标签向外偏移5个点
|
|
319
|
+
ax.tick_params(axis='y', pad=-5) # y轴标签向外偏移5个点
|
|
320
|
+
ax.tick_params(axis='z', pad=0) # z轴标签向外偏移5个点
|
|
321
|
+
ax.set_xlabel('x (Rank A)', fontsize=12, labelpad=-5)
|
|
322
|
+
ax.set_ylabel('y (Rank B)', fontsize=12, labelpad=-5)
|
|
323
|
+
ax.set_zlabel('z (Overlaps)', fontsize=12, labelpad=0)
|
|
324
|
+
|
|
325
|
+
#title = '$z=\\beta_0+\\beta_1x+\\beta_2y+\\beta_3(xy)+\\epsilon$'
|
|
326
|
+
#ax.set_title(title, fontsize=5, fontweight='bold', color='#16213e', pad=1)
|
|
327
|
+
idx = 0
|
|
328
|
+
ax.view_init(elev=20, azim=-55 + idx*12)
|
|
329
|
+
|
|
330
|
+
#beta_text = f'$\\beta_0$={beta['const']:.2f}\n$\\beta_1$={beta['x']:.2f}\n$\\beta_2$={beta['y']:.2f}\n$\\beta_3$={beta['xy']:.2f}'
|
|
331
|
+
#ax.text2D(-0.13, 0.98, beta_text, transform=ax.transAxes, fontsize=4,
|
|
332
|
+
# verticalalignment='top', bbox=dict(boxstyle='round', facecolor='white', alpha=0.8, edgecolor=color, linewidth=0.4))
|
|
333
|
+
|
|
334
|
+
#ax.set_xlim(-1, 1)
|
|
335
|
+
#ax.set_ylim(-1, 1)
|
|
336
|
+
#ax.set_zlim(-3, 8)
|
|
337
|
+
#ax.tick_params(labelsize=3, pad=0)
|
|
338
|
+
# 反转 x 轴
|
|
339
|
+
#ax.invert_yaxis()
|
|
340
|
+
ax.view_init(elev=20, azim=-145)
|
|
341
|
+
plt.tight_layout()
|
|
342
|
+
|
|
343
|
+
if out_file:
|
|
344
|
+
plt.savefig(out_file + '.pdf', dpi=300, bbox_inches="tight")
|
|
345
|
+
plt.savefig(out_file + '.svg', dpi=300, bbox_inches="tight")
|
|
346
|
+
plt.show()
|
|
347
|
+
|
|
348
|
+
def plot_fit3D_plot(df1, df2, win):
|
|
349
|
+
gene_col1 = df1.columns[0]
|
|
350
|
+
score_col1 = df1.columns[1]
|
|
351
|
+
df1 = df1.sort_values(score_col1, ascending=False)
|
|
352
|
+
genes1 = df1[gene_col1].astype(str).tolist()
|
|
353
|
+
|
|
354
|
+
# remove duplicates while preserving order
|
|
355
|
+
genes1 = list(dict.fromkeys(genes1))
|
|
356
|
+
|
|
357
|
+
gene_col2 = df1.columns[0]
|
|
358
|
+
score_col2 = df2.columns[1]
|
|
359
|
+
df2 = df2.sort_values(score_col2, ascending=False)
|
|
360
|
+
genes2 = df2[gene_col2].astype(str).tolist()
|
|
361
|
+
# remove duplicates while preserving order
|
|
362
|
+
genes2 = list(dict.fromkeys(genes2))
|
|
363
|
+
|
|
364
|
+
window = win
|
|
365
|
+
if (len(genes1) < window*3 or len(genes2) < window*3):
|
|
366
|
+
return 0.0, 1.0, 0.0
|
|
367
|
+
rrho_df = rrho_matrix_str(genes1, genes2, window=window)
|
|
368
|
+
|
|
369
|
+
if rrho_df.shape[0] == 0 or rrho_df.shape[1] == 0 or (rrho_df > 0.0).sum().sum() == 0:
|
|
370
|
+
print('no overlaps found')
|
|
371
|
+
exit(1)
|
|
372
|
+
|
|
373
|
+
#beta_xy, pval_xy = td_coor(rrho_df.to_numpy())
|
|
374
|
+
# df = Z_to_frame(rrho_df.to_numpy())
|
|
375
|
+
plot_3D_surface(rrho_df.to_numpy())
|
|
376
|
+
|
|
377
|
+
def rrho_coor_bs(df1, df2, win):
|
|
378
|
+
gene_col1 = df1.columns[0]
|
|
379
|
+
score_col1 = df1.columns[1]
|
|
380
|
+
df1 = df1.sort_values(score_col1, ascending=False)
|
|
381
|
+
genes1 = df1[gene_col1].astype(str).tolist()
|
|
382
|
+
|
|
383
|
+
# remove duplicates while preserving order
|
|
384
|
+
genes1 = list(dict.fromkeys(genes1))
|
|
385
|
+
|
|
386
|
+
gene_col2 = df1.columns[0]
|
|
387
|
+
score_col2 = df2.columns[1]
|
|
388
|
+
df2 = df2.sort_values(score_col2, ascending=False)
|
|
389
|
+
genes2 = df2[gene_col2].astype(str).tolist()
|
|
390
|
+
# remove duplicates while preserving order
|
|
391
|
+
genes2 = list(dict.fromkeys(genes2))
|
|
392
|
+
|
|
393
|
+
window = win
|
|
394
|
+
if (len(genes1) < window*3 or len(genes2) < window*3):
|
|
395
|
+
return 0.0, 1.0, 0.0, 1.0
|
|
396
|
+
rrho_df = rrho_matrix_str(genes1, genes2, window=window)
|
|
397
|
+
|
|
398
|
+
if rrho_df.shape[0] == 0 or rrho_df.shape[1] == 0 or (rrho_df > 0.0).sum().sum() == 0:
|
|
399
|
+
return 0.0, 1.0, 0.0, 1.0
|
|
400
|
+
|
|
401
|
+
#beta_xy, pval_xy = td_coor(rrho_df.to_numpy())
|
|
402
|
+
df = Z_to_frame(rrho_df.to_numpy())
|
|
403
|
+
dtci_res = bootstrap_parametric_h0(df)
|
|
404
|
+
beta_xy = dtci_res['beta3']
|
|
405
|
+
pval_xy = dtci_res['dtci_pval']
|
|
406
|
+
DTCI = dtci_res['dtci_obs']
|
|
407
|
+
bs_pval = dtci_res['p_value']
|
|
408
|
+
|
|
409
|
+
return beta_xy, pval_xy, DTCI, bs_pval
|
|
410
|
+
|
|
411
|
+
def thread_one(tfbs_array, deg_list, win, shared_list, nn, lock):
|
|
412
|
+
tf_n = len(tfbs_array)
|
|
413
|
+
epsilon = 1e-15 # 常用值
|
|
414
|
+
for i in range(tf_n):
|
|
415
|
+
# fin_n += 1
|
|
416
|
+
beta_xy, pval_xy, DTCI = rrho_coor(deg_list, tfbs_array[i][1], win)
|
|
417
|
+
#score = math.pow(abs(beta_xy), 1.0) * math.log10(pval_xy + epsilon) * -1.0
|
|
418
|
+
#if math.isnan(score):
|
|
419
|
+
# score = 0
|
|
420
|
+
with lock:
|
|
421
|
+
shared_list.append([tfbs_array[i][0], beta_xy, pval_xy, DTCI])
|
|
422
|
+
nn[1] += 1
|
|
423
|
+
print(str(nn[1]) + ' / ' + str(nn[0]))
|
|
424
|
+
|
|
425
|
+
def thread_one_bs(tfbs_array, deg_list, win, shared_list, nn, lock):
|
|
426
|
+
tf_n = len(tfbs_array)
|
|
427
|
+
epsilon = 1e-15 # 常用值
|
|
428
|
+
for i in range(tf_n):
|
|
429
|
+
# fin_n += 1
|
|
430
|
+
beta_xy, pval_xy, DTCI, bs_pval = rrho_coor_bs(deg_list, tfbs_array[i][1], win)
|
|
431
|
+
#score = math.pow(abs(beta_xy), 1.0) * math.log10(pval_xy + epsilon) * -1.0
|
|
432
|
+
#if math.isnan(score):
|
|
433
|
+
# score = 0
|
|
434
|
+
with lock:
|
|
435
|
+
shared_list.append([tfbs_array[i][0], beta_xy, pval_xy, DTCI, bs_pval])
|
|
436
|
+
nn[1] += 1
|
|
437
|
+
print(str(nn[1]) + ' / ' + str(nn[0]))
|
|
438
|
+
|
|
439
|
+
def split_into_n_even(lst, n):
|
|
440
|
+
nn = len(lst)
|
|
441
|
+
base = nn // n # 每份基础大小
|
|
442
|
+
remainder = nn % n # 余数,前remainder份各多1个
|
|
443
|
+
|
|
444
|
+
result = []
|
|
445
|
+
start = 0
|
|
446
|
+
for i in range(n):
|
|
447
|
+
size = base + (1 if i < remainder else 0)
|
|
448
|
+
result.append(lst[start:start+size])
|
|
449
|
+
start += size
|
|
450
|
+
return result
|
|
451
|
+
|
|
452
|
+
def olcr(set_names=None, list_file=None, win=30, thread_n=16, data_dir=None):
|
|
453
|
+
if data_dir == None:
|
|
454
|
+
data_dir = overcast_data_dir
|
|
455
|
+
if not (os.path.exists(data_dir) and os.path.isdir(data_dir)):
|
|
456
|
+
print("OVERCAST_data folder can not found!")
|
|
457
|
+
exit(1)
|
|
458
|
+
sets_files = []
|
|
459
|
+
for set1 in set_names:
|
|
460
|
+
sets_file = data_dir + '/TF-target_sets/' + set1 + '.txt'
|
|
461
|
+
if not os.path.exists(sets_file):
|
|
462
|
+
print(sets_file, 'not exist!')
|
|
463
|
+
exit(1)
|
|
464
|
+
else:
|
|
465
|
+
sets_files.append(sets_file)
|
|
466
|
+
degl_file = list_file
|
|
467
|
+
if not os.path.exists(degl_file):
|
|
468
|
+
print(degl_file, 'not exist!')
|
|
469
|
+
exit(1)
|
|
470
|
+
|
|
471
|
+
tfbs_array = read_gene_sets(sets_files)
|
|
472
|
+
deg_list = read_DEG_list(degl_file)
|
|
473
|
+
|
|
474
|
+
manager = mp.Manager()
|
|
475
|
+
shared_list = manager.list([]) # 支持任意类型
|
|
476
|
+
nn = manager.list([len(tfbs_array), 0]) # 支持任意类型
|
|
477
|
+
lock = manager.Lock()
|
|
478
|
+
|
|
479
|
+
tfbs_array_splits = split_into_n_even(tfbs_array, thread_n)
|
|
480
|
+
task_args = []
|
|
481
|
+
for ti in range(thread_n):
|
|
482
|
+
task_args.append((tfbs_array_splits[ti], deg_list, win, shared_list, nn, lock))
|
|
483
|
+
|
|
484
|
+
# 创建进程池
|
|
485
|
+
with mp.Pool(processes=thread_n) as pool:
|
|
486
|
+
print(f"thrend_n:", thread_n)
|
|
487
|
+
print(f"total {len(task_args)} tasks")
|
|
488
|
+
# starmap 会自动分配任务到进程池
|
|
489
|
+
start_time = time.time()
|
|
490
|
+
results = pool.starmap(thread_one, task_args)
|
|
491
|
+
elapsed = time.time() - start_time
|
|
492
|
+
# 打印结果
|
|
493
|
+
print("time consumed:", elapsed)
|
|
494
|
+
|
|
495
|
+
sorted_results = sorted(shared_list, key=itemgetter(-1), reverse=True)
|
|
496
|
+
results = []
|
|
497
|
+
ln = len(sorted_results)
|
|
498
|
+
for li in range(len(sorted_results)):
|
|
499
|
+
ratio = (li + 1) / ln
|
|
500
|
+
rline = sorted_results[li] + [li + 1, ratio]
|
|
501
|
+
results.append(rline)
|
|
502
|
+
col_names = ['TF_motif', 'Beta3', 'Beta3_Pvalue', 'DTCI', 'Rank', 'Rank / N']
|
|
503
|
+
df = pd.DataFrame(results, columns=col_names)
|
|
504
|
+
|
|
505
|
+
return df
|
|
506
|
+
|
|
507
|
+
def olcr_bootstrap(set_names=None, list_file=None, win=30, thread_n=16, data_dir=None):
|
|
508
|
+
if data_dir == None:
|
|
509
|
+
data_dir = overcast_data_dir
|
|
510
|
+
if not (os.path.exists(data_dir) and os.path.isdir(data_dir)):
|
|
511
|
+
print("OVERCAST_data folder can not found!")
|
|
512
|
+
exit(1)
|
|
513
|
+
sets_files = []
|
|
514
|
+
for set1 in set_names:
|
|
515
|
+
sets_file = data_dir + '/TF-target_sets/' + set1 + '.txt'
|
|
516
|
+
if not os.path.exists(sets_file):
|
|
517
|
+
print(sets_file, 'not exist!')
|
|
518
|
+
exit(1)
|
|
519
|
+
else:
|
|
520
|
+
sets_files.append(sets_file)
|
|
521
|
+
degl_file = list_file
|
|
522
|
+
if not os.path.exists(degl_file):
|
|
523
|
+
print(degl_file, 'not exist!')
|
|
524
|
+
exit(1)
|
|
525
|
+
|
|
526
|
+
tfbs_array = read_gene_sets(sets_files)
|
|
527
|
+
deg_list = read_DEG_list(degl_file)
|
|
528
|
+
|
|
529
|
+
manager = mp.Manager()
|
|
530
|
+
shared_list = manager.list([]) # 支持任意类型
|
|
531
|
+
nn = manager.list([len(tfbs_array), 0]) # 支持任意类型
|
|
532
|
+
lock = manager.Lock()
|
|
533
|
+
|
|
534
|
+
tfbs_array_splits = split_into_n_even(tfbs_array, thread_n)
|
|
535
|
+
task_args = []
|
|
536
|
+
for ti in range(thread_n):
|
|
537
|
+
task_args.append((tfbs_array_splits[ti], deg_list, win, shared_list, nn, lock))
|
|
538
|
+
|
|
539
|
+
# 创建进程池
|
|
540
|
+
with mp.Pool(processes=thread_n) as pool:
|
|
541
|
+
print(f"thrend_n:", thread_n)
|
|
542
|
+
print(f"total {len(task_args)} tasks")
|
|
543
|
+
# starmap 会自动分配任务到进程池
|
|
544
|
+
start_time = time.time()
|
|
545
|
+
results = pool.starmap(thread_one_bs, task_args)
|
|
546
|
+
elapsed = time.time() - start_time
|
|
547
|
+
# 打印结果
|
|
548
|
+
print("time consumed:", elapsed)
|
|
549
|
+
|
|
550
|
+
sorted_results = sorted(shared_list, key=lambda x: (x[-1], -x[-2]), reverse=False) # lambda x: (x[4], -x[1]), reverse=False
|
|
551
|
+
results = []
|
|
552
|
+
ln = len(sorted_results)
|
|
553
|
+
for li in range(len(sorted_results)):
|
|
554
|
+
ratio = (li + 1) / ln
|
|
555
|
+
rline = sorted_results[li] + [li + 1, ratio]
|
|
556
|
+
results.append(rline)
|
|
557
|
+
col_names = ['TF_motif', 'Beta3', 'Beta3_Pvalue', 'DTCI', 'DTCI_Pvalue', 'Rank', 'Rank / N']
|
|
558
|
+
df = pd.DataFrame(results, columns=col_names)
|
|
559
|
+
|
|
560
|
+
return df
|
|
561
|
+
|
|
562
|
+
def plot_olc(set_names=['human_hocomoco_CLink_wtcoor_1w_0.8'], list_file='input_deg-list.txt', tf='NFKB1_HUMAN.H11MO.1.B', win=30, data_dir=None):
|
|
563
|
+
if data_dir == None:
|
|
564
|
+
data_dir = overcast_data_dir
|
|
565
|
+
if not (os.path.exists(data_dir) and os.path.isdir(data_dir)):
|
|
566
|
+
print("OVERCAST_data folder can not found!")
|
|
567
|
+
exit(1)
|
|
568
|
+
sets_files = []
|
|
569
|
+
for set1 in set_names:
|
|
570
|
+
sets_file = data_dir + '/TF-target_sets/' + set1 + '.txt'
|
|
571
|
+
if not os.path.exists(sets_file):
|
|
572
|
+
print(sets_file, 'not exist!')
|
|
573
|
+
exit(1)
|
|
574
|
+
else:
|
|
575
|
+
sets_files.append(sets_file)
|
|
576
|
+
degl_file = list_file
|
|
577
|
+
if not os.path.exists(degl_file):
|
|
578
|
+
print(degl_file, 'not exist!')
|
|
579
|
+
exit(1)
|
|
580
|
+
|
|
581
|
+
tfbs_array = read_gene_sets(sets_files)
|
|
582
|
+
deg_list = read_DEG_list(degl_file)
|
|
583
|
+
|
|
584
|
+
tf_n = len(tfbs_array)
|
|
585
|
+
epsilon = 1e-15 # 常用值
|
|
586
|
+
for i in range(tf_n):
|
|
587
|
+
if tfbs_array[i][0] == tf:
|
|
588
|
+
plot_olc_plot(deg_list, tfbs_array[i][1], win)
|
|
589
|
+
break
|
|
590
|
+
|
|
591
|
+
def plot_fit3D(set_names=['human_hocomoco_CLink_wtcoor_1w_0.8'], list_file='input_deg-list.txt', tf='NFKB1_HUMAN.H11MO.1.B', win=30, data_dir=None):
|
|
592
|
+
if data_dir == None:
|
|
593
|
+
data_dir = overcast_data_dir
|
|
594
|
+
if not (os.path.exists(data_dir) and os.path.isdir(data_dir)):
|
|
595
|
+
print("OVERCAST_data folder can not found!")
|
|
596
|
+
exit(1)
|
|
597
|
+
sets_files = []
|
|
598
|
+
for set1 in set_names:
|
|
599
|
+
sets_file = data_dir + '/TF-target_sets/' + set1 + '.txt'
|
|
600
|
+
if not os.path.exists(sets_file):
|
|
601
|
+
print(sets_file, 'not exist!')
|
|
602
|
+
exit(1)
|
|
603
|
+
else:
|
|
604
|
+
sets_files.append(sets_file)
|
|
605
|
+
degl_file = list_file
|
|
606
|
+
if not os.path.exists(degl_file):
|
|
607
|
+
print(degl_file, 'not exist!')
|
|
608
|
+
exit(1)
|
|
609
|
+
|
|
610
|
+
tfbs_array = read_gene_sets(sets_files)
|
|
611
|
+
deg_list = read_DEG_list(degl_file)
|
|
612
|
+
|
|
613
|
+
tf_n = len(tfbs_array)
|
|
614
|
+
epsilon = 1e-15 # 常用值
|
|
615
|
+
for i in range(tf_n):
|
|
616
|
+
if tfbs_array[i][0] == tf:
|
|
617
|
+
plot_fit3D_plot(deg_list, tfbs_array[i][1], win)
|
|
618
|
+
break
|
|
619
|
+
|
|
620
|
+
if __name__ == '__main__':
|
|
621
|
+
'''
|
|
622
|
+
result = olcr(set_names=['human_hocomoco_CLink_wtcoor_1w_0.8', 'human_jaspar_CLink_wtcoor_1w_0.8'], list_file='OVERCAST_data/input_deg-list.txt', win=30, thread_n=16)
|
|
623
|
+
result.to_csv('output.txt', sep='\t', index=False, encoding='utf-8')
|
|
624
|
+
|
|
625
|
+
result = olcr_bootstrap(set_names=['human_hocomoco_CLink_wtcoor_1w_0.8', 'human_jaspar_CLink_wtcoor_1w_0.8'], list_file='OVERCAST_data/input_deg-list.txt', win=30, thread_n=16)
|
|
626
|
+
result.to_csv('output_bootstrap.txt', sep='\t', index=False, encoding='utf-8')
|
|
627
|
+
'''
|
|
628
|
+
|
|
629
|
+
#plot_olc(set_names=['human_hocomoco_CLink_wtcoor_1w_0.8', 'human_jaspar_CLink_wtcoor_1w_0.8'], list_file='OVERCAST_data/input_deg-list.txt', tf='MA0844.2_XBP1', win=30)
|
|
630
|
+
|
|
631
|
+
#plot_fit3D(set_names=['human_hocomoco_CLink_wtcoor_1w_0.8', 'human_jaspar_CLink_wtcoor_1w_0.8'], list_file='OVERCAST_data/input_deg-list.txt', tf='MA0844.2_XBP1', win=30)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: PyOVERCAST
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A Python package for mining key transcription factors from transcriptome data.
|
|
5
|
+
Author-email: Tinghua Huang <thua45@126.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/thua45/PyOVERCAST
|
|
8
|
+
Project-URL: Documentation, https://github.com/thua45/PyOVERCAST#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/thua45/PyOVERCAST
|
|
10
|
+
Project-URL: Issues, https://github.com/thua45/PyOVERCAST/issues
|
|
11
|
+
Keywords: PyOVERCAST,Transcription Factor,Binding Site,Transcriptome Data
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: numpy==1.26; sys_platform == "darwin"
|
|
21
|
+
Requires-Dist: numpy>=2.0; sys_platform == "win32"
|
|
22
|
+
Requires-Dist: numpy>=2.0; sys_platform == "linux"
|
|
23
|
+
Requires-Dist: pandas==3.0; sys_platform == "darwin"
|
|
24
|
+
Requires-Dist: pandas>=3.0; sys_platform == "win32"
|
|
25
|
+
Requires-Dist: pandas>=3.0; sys_platform == "linux"
|
|
26
|
+
Requires-Dist: statsmodels==0.14; sys_platform == "darwin"
|
|
27
|
+
Requires-Dist: statsmodels>=0.14; sys_platform == "win32"
|
|
28
|
+
Requires-Dist: statsmodels>=0.14; sys_platform == "linux"
|
|
29
|
+
Requires-Dist: seaborn==0.13; sys_platform == "darwin"
|
|
30
|
+
Requires-Dist: seaborn>=0.13; sys_platform == "win32"
|
|
31
|
+
Requires-Dist: seaborn>=0.13; sys_platform == "linux"
|
|
32
|
+
Requires-Dist: matplotlib==3.11; sys_platform == "darwin"
|
|
33
|
+
Requires-Dist: matplotlib>=3.11; sys_platform == "win32"
|
|
34
|
+
Requires-Dist: matplotlib>=3.11; sys_platform == "linux"
|
|
35
|
+
Provides-Extra: dev
|
|
36
|
+
Requires-Dist: matplotlib; extra == "dev"
|
|
37
|
+
Provides-Extra: test
|
|
38
|
+
Requires-Dist: matplotlib; extra == "test"
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
# PyOVERCAST
|
|
42
|
+
|
|
43
|
+
A Python package for mining key transcription factors from transcriptome data.
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install numpy pandas statsmodels seaborn matplotlib PyOVERCAST
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Usage
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from PyOVERCAST import clinks, predict
|
|
55
|
+
|
|
56
|
+
if __name__ == '__main__':
|
|
57
|
+
# download TF-target set, only need to run once!!!
|
|
58
|
+
clinks.download_data()
|
|
59
|
+
|
|
60
|
+
# list available TF-target set
|
|
61
|
+
sets_names = clinks.get_sets(species='Homo sapiens')
|
|
62
|
+
print(sets_names)
|
|
63
|
+
|
|
64
|
+
# list avaiable TFs
|
|
65
|
+
tfs_codes = clinks.get_tfs(set_name='human_hocomoco_CLink_wtcoor_1w_0.8')
|
|
66
|
+
print(tfs_codes)
|
|
67
|
+
|
|
68
|
+
# get targets
|
|
69
|
+
targets = clinks.get_targets(set_name='human_hocomoco_CLink_wtcoor_1w_0.8', tf='NFKB1_HUMAN.H11MO.1.B')
|
|
70
|
+
print(targets)
|
|
71
|
+
|
|
72
|
+
# predict one DEG-list
|
|
73
|
+
result = predict.olcr(set_names=['human_hocomoco_CLink_wtcoor_1w_0.8', 'human_jaspar_CLink_wtcoor_1w_0.8'], list_file='./PyOVERCAST_data/input_deg-list.txt', win=30, thread_n=16)
|
|
74
|
+
|
|
75
|
+
# or predict one DEG-list with bootstrap
|
|
76
|
+
result = predict.olcr_bootstrap(set_names=['human_hocomoco_CLink_wtcoor_1w_0.8', 'human_jaspar_CLink_wtcoor_1w_0.8'], list_file='./PyOVERCAST_data/input_deg-list.txt', win=30, thread_n=16)
|
|
77
|
+
|
|
78
|
+
# save result to text file
|
|
79
|
+
result.to_csv('output.txt', sep='\t', index=False, encoding='utf-8-sig')
|
|
80
|
+
|
|
81
|
+
# plot OLC matrix
|
|
82
|
+
predict.plot_olc(set_names=['human_hocomoco_CLink_wtcoor_1w_0.8', 'human_jaspar_CLink_wtcoor_1w_0.8'], list_file='./PyOVERCAST_data/input_deg-list.txt', tf='MA0844.2_XBP1', win=30)
|
|
83
|
+
|
|
84
|
+
# plot fitted 3D U-surface
|
|
85
|
+
predict.plot_fit3D(set_names=['human_hocomoco_CLink_wtcoor_1w_0.8', 'human_jaspar_CLink_wtcoor_1w_0.8'], list_file='./PyOVERCAST_data/input_deg-list.txt', tf='MA0844.2_XBP1', win=30)
|
|
86
|
+
```
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
PyOVERCAST/__init__.py
|
|
5
|
+
PyOVERCAST/clinks.py
|
|
6
|
+
PyOVERCAST/predict.py
|
|
7
|
+
PyOVERCAST.egg-info/PKG-INFO
|
|
8
|
+
PyOVERCAST.egg-info/SOURCES.txt
|
|
9
|
+
PyOVERCAST.egg-info/dependency_links.txt
|
|
10
|
+
PyOVERCAST.egg-info/entry_points.txt
|
|
11
|
+
PyOVERCAST.egg-info/requires.txt
|
|
12
|
+
PyOVERCAST.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
|
|
2
|
+
[:sys_platform == "darwin"]
|
|
3
|
+
numpy==1.26
|
|
4
|
+
pandas==3.0
|
|
5
|
+
statsmodels==0.14
|
|
6
|
+
seaborn==0.13
|
|
7
|
+
matplotlib==3.11
|
|
8
|
+
|
|
9
|
+
[:sys_platform == "linux"]
|
|
10
|
+
numpy>=2.0
|
|
11
|
+
pandas>=3.0
|
|
12
|
+
statsmodels>=0.14
|
|
13
|
+
seaborn>=0.13
|
|
14
|
+
matplotlib>=3.11
|
|
15
|
+
|
|
16
|
+
[:sys_platform == "win32"]
|
|
17
|
+
numpy>=2.0
|
|
18
|
+
pandas>=3.0
|
|
19
|
+
statsmodels>=0.14
|
|
20
|
+
seaborn>=0.13
|
|
21
|
+
matplotlib>=3.11
|
|
22
|
+
|
|
23
|
+
[dev]
|
|
24
|
+
matplotlib
|
|
25
|
+
|
|
26
|
+
[test]
|
|
27
|
+
matplotlib
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
PyOVERCAST
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# PyOVERCAST
|
|
2
|
+
|
|
3
|
+
A Python package for mining key transcription factors from transcriptome data.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install numpy pandas statsmodels seaborn matplotlib PyOVERCAST
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from PyOVERCAST import clinks, predict
|
|
15
|
+
|
|
16
|
+
if __name__ == '__main__':
|
|
17
|
+
# download TF-target set, only need to run once!!!
|
|
18
|
+
clinks.download_data()
|
|
19
|
+
|
|
20
|
+
# list available TF-target set
|
|
21
|
+
sets_names = clinks.get_sets(species='Homo sapiens')
|
|
22
|
+
print(sets_names)
|
|
23
|
+
|
|
24
|
+
# list avaiable TFs
|
|
25
|
+
tfs_codes = clinks.get_tfs(set_name='human_hocomoco_CLink_wtcoor_1w_0.8')
|
|
26
|
+
print(tfs_codes)
|
|
27
|
+
|
|
28
|
+
# get targets
|
|
29
|
+
targets = clinks.get_targets(set_name='human_hocomoco_CLink_wtcoor_1w_0.8', tf='NFKB1_HUMAN.H11MO.1.B')
|
|
30
|
+
print(targets)
|
|
31
|
+
|
|
32
|
+
# predict one DEG-list
|
|
33
|
+
result = predict.olcr(set_names=['human_hocomoco_CLink_wtcoor_1w_0.8', 'human_jaspar_CLink_wtcoor_1w_0.8'], list_file='./PyOVERCAST_data/input_deg-list.txt', win=30, thread_n=16)
|
|
34
|
+
|
|
35
|
+
# or predict one DEG-list with bootstrap
|
|
36
|
+
result = predict.olcr_bootstrap(set_names=['human_hocomoco_CLink_wtcoor_1w_0.8', 'human_jaspar_CLink_wtcoor_1w_0.8'], list_file='./PyOVERCAST_data/input_deg-list.txt', win=30, thread_n=16)
|
|
37
|
+
|
|
38
|
+
# save result to text file
|
|
39
|
+
result.to_csv('output.txt', sep='\t', index=False, encoding='utf-8-sig')
|
|
40
|
+
|
|
41
|
+
# plot OLC matrix
|
|
42
|
+
predict.plot_olc(set_names=['human_hocomoco_CLink_wtcoor_1w_0.8', 'human_jaspar_CLink_wtcoor_1w_0.8'], list_file='./PyOVERCAST_data/input_deg-list.txt', tf='MA0844.2_XBP1', win=30)
|
|
43
|
+
|
|
44
|
+
# plot fitted 3D U-surface
|
|
45
|
+
predict.plot_fit3D(set_names=['human_hocomoco_CLink_wtcoor_1w_0.8', 'human_jaspar_CLink_wtcoor_1w_0.8'], list_file='./PyOVERCAST_data/input_deg-list.txt', tf='MA0844.2_XBP1', win=30)
|
|
46
|
+
```
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "PyOVERCAST"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
license = "MIT" # SPDX expression
|
|
9
|
+
description = "A Python package for mining key transcription factors from transcriptome data."
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
requires-python = ">=3.11"
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Tinghua Huang", email = "thua45@126.com"}
|
|
14
|
+
]
|
|
15
|
+
keywords = ["PyOVERCAST", "Transcription Factor", "Binding Site", 'Transcriptome Data']
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Programming Language :: Python :: 3.13",
|
|
22
|
+
]
|
|
23
|
+
dependencies = [
|
|
24
|
+
"numpy==1.26; sys_platform == 'darwin'",
|
|
25
|
+
"numpy>=2.0; sys_platform == 'win32'",
|
|
26
|
+
"numpy>=2.0; sys_platform == 'linux'",
|
|
27
|
+
"pandas==3.0; sys_platform == 'darwin'",
|
|
28
|
+
"pandas>=3.0; sys_platform == 'win32'",
|
|
29
|
+
"pandas>=3.0; sys_platform == 'linux'",
|
|
30
|
+
"statsmodels==0.14; sys_platform == 'darwin'",
|
|
31
|
+
"statsmodels>=0.14; sys_platform == 'win32'",
|
|
32
|
+
"statsmodels>=0.14; sys_platform == 'linux'",
|
|
33
|
+
"seaborn==0.13; sys_platform == 'darwin'",
|
|
34
|
+
"seaborn>=0.13; sys_platform == 'win32'",
|
|
35
|
+
"seaborn>=0.13; sys_platform == 'linux'",
|
|
36
|
+
"matplotlib==3.11; sys_platform == 'darwin'",
|
|
37
|
+
"matplotlib>=3.11; sys_platform == 'win32'",
|
|
38
|
+
"matplotlib>=3.11; sys_platform == 'linux'",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[project.urls]
|
|
42
|
+
Homepage = "https://github.com/thua45/PyOVERCAST"
|
|
43
|
+
Documentation = "https://github.com/thua45/PyOVERCAST#readme"
|
|
44
|
+
Repository = "https://github.com/thua45/PyOVERCAST"
|
|
45
|
+
Issues = "https://github.com/thua45/PyOVERCAST/issues"
|
|
46
|
+
|
|
47
|
+
[project.optional-dependencies]
|
|
48
|
+
dev = [
|
|
49
|
+
"matplotlib",
|
|
50
|
+
]
|
|
51
|
+
test = [
|
|
52
|
+
"matplotlib",
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
[project.scripts]
|
|
56
|
+
PyOVERCAST = "PyOVERCAST.cli:main"
|
|
57
|
+
PyOVERCAST-cleanup = "PyOVERCAST.uninstall:cleanup"
|
|
58
|
+
|
|
59
|
+
[tool.setuptools]
|
|
60
|
+
packages = ["PyOVERCAST"]
|
|
61
|
+
package-data = {"*" = ["*.txt", "*.md", "*.rst", "data/*"]}
|
|
62
|
+
|