PyTFBS 1.0.0__tar.gz → 1.0.2__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.
pytfbs-1.0.2/PKG-INFO ADDED
@@ -0,0 +1,69 @@
1
+ Metadata-Version: 2.4
2
+ Name: PyTFBS
3
+ Version: 1.0.2
4
+ Summary: PyTFBS: A Python Package for Transcription Factor Binding Site Prediction
5
+ Author-email: Tinghua Huang <thua45@126.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/thua45/pytfbs
8
+ Project-URL: Documentation, https://github.com/thua45/pytfbs#readme
9
+ Project-URL: Repository, https://github.com/thua45/pytfbs
10
+ Project-URL: Issues, https://github.com/thua45/pytfbs/issues
11
+ Keywords: PyTFBS,Transcription Factor,Binding Site
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: torch
21
+ Requires-Dist: numpy==1.26; sys_platform == "darwin"
22
+ Requires-Dist: numpy>=2.0; sys_platform == "win32"
23
+ Requires-Dist: numpy>=2.0; sys_platform == "linux"
24
+ Provides-Extra: dev
25
+ Requires-Dist: matplotlib; extra == "dev"
26
+ Provides-Extra: test
27
+ Requires-Dist: matplotlib; extra == "test"
28
+ Dynamic: license-file
29
+
30
+ # PyTFBS
31
+
32
+ A Python package for dpredicting transcription factor binding sites.
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ pip install torch numpy PyTFBS
38
+ ```
39
+
40
+ ## Usage
41
+
42
+ ```python
43
+ from PyTFBS import motif, predict
44
+
45
+ # download PyTFBS data, only need to run once!!!
46
+ motif.download_data()
47
+
48
+ # list available models
49
+ motif.list_models(species='Homo sapiens', accuracy=0.9, sensitivity=0.9)
50
+
51
+ # get avaiable motifs
52
+ motifs = motif.get_motifs(species='Homo sapiens', accuracy=0.9, sensitivity=0.9)
53
+ print(motifs)
54
+
55
+ # get models based on motif name
56
+ models = motif.get_models('RFX2_HUMAN.H11MO.0.A')
57
+ print(models)
58
+
59
+ # predict one model
60
+ predict.script('CEBPB_HUMAN.H11MO.0.A', 'CEBPB_HUMAN.H11MO.0.A_1231', 'input_seq_file.fasta', 'out_file.txt')
61
+
62
+ # speed up using mutil-threading (for Windows OS only)
63
+ predict.win_bin('CEBPB_HUMAN.H11MO.0.A', 'CEBPB_HUMAN.H11MO.0.A_1231', 'input_seq_file.fasta', 64, 'out_file.txt')
64
+
65
+ # run prediction with user motif data
66
+ # the my_motif_dir should be organized as [[motif], [trace], [par]]
67
+ predict.script('CEBPB_HUMAN.H11MO.0.A', 'CEBPB_HUMAN.H11MO.0.A_1231', 'input_seq_file.fasta', data_dir='my_motif_dir')
68
+ predict.win_bin('CEBPB_HUMAN.H11MO.0.A', 'CEBPB_HUMAN.H11MO.0.A_1231', 'input_seq_file.fasta', 64, data_dir='my_motif_dir')
69
+ ```
@@ -0,0 +1,196 @@
1
+ # from importlib import resources
2
+ import urllib.request
3
+ import zipfile
4
+ import os
5
+ from pathlib import Path
6
+
7
+ PyTFBS_data_dir = './PyTFBS_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 = PyTFBS_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/PyTFBS/PyTFBS_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 list_models(motif_name=None, species=None, accuracy=None, sensitivity=None, data_dir=None):
129
+ if data_dir == None:
130
+ data_dir = PyTFBS_data_dir
131
+ index_file = data_dir + '/motif_index.txt'
132
+ if not os.path.exists(index_file):
133
+ print('motif_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 motif_name != None and lblocks[0] != motif_name:
143
+ continue
144
+ if species != None and lblocks[1] != species:
145
+ continue
146
+ if accuracy != None and float(lblocks[3]) < accuracy:
147
+ continue
148
+ if sensitivity != None and float(lblocks[4]) < sensitivity:
149
+ continue
150
+ result.append(lblocks)
151
+ print(header)
152
+ for rline in result:
153
+ print('\t'.join(rline))
154
+
155
+ def get_motifs(motif_name=None, species=None, accuracy=None, sensitivity=None, data_dir=None):
156
+ if data_dir == None:
157
+ data_dir = PyTFBS_data_dir
158
+ index_file = data_dir + '/motif_index.txt'
159
+ if not os.path.exists(index_file):
160
+ print('motif_index.txt not exist, you may need to run download_data() first')
161
+ exit(1)
162
+ header = ''
163
+ result = []
164
+ for line in open(index_file, 'r'):
165
+ if line[0] == "#":
166
+ header = line.rstrip()
167
+ continue
168
+ lblocks = line.rstrip().split('\t')
169
+ if motif_name != None and lblocks[0] != motif_name:
170
+ continue
171
+ if species != None and lblocks[1] != species:
172
+ continue
173
+ if accuracy != None and float(lblocks[3]) < accuracy:
174
+ continue
175
+ if sensitivity != None and float(lblocks[4]) < sensitivity:
176
+ continue
177
+ result.append(lblocks[0])
178
+ return result
179
+
180
+ def get_models(motif_name, data_dir=None):
181
+ if data_dir == None:
182
+ data_dir = PyTFBS_data_dir
183
+ index_file = data_dir + '/motif_index.txt'
184
+ if not os.path.exists(index_file):
185
+ print('motif_index.txt not exist, you may need to run download_data() first')
186
+ exit(1)
187
+ models = []
188
+ for line in open(index_file, 'r'):
189
+ if line[0] == "#":
190
+ header = line.rstrip()
191
+ continue
192
+ lblocks = line.rstrip().split('\t')
193
+ if lblocks[0] != motif_name:
194
+ continue
195
+ models.append(lblocks[2])
196
+ return {'motif_name': motif_name, 'models': models}
@@ -0,0 +1,413 @@
1
+ #!/usr/bin/env python
2
+ import os
3
+ from importlib import resources
4
+ from collections import defaultdict
5
+ from importlib import resources
6
+ import math
7
+ import torch
8
+ import torch.nn as nn
9
+ from torch.nn import functional as F
10
+
11
+ PyTFBS_data_dir = './PyTFBS_data'
12
+
13
+ def has_non_acgtn_upper(sequence):
14
+ """只检查大写ACGT字符"""
15
+ valid_chars = {'A', 'C', 'G', 'T', 'N'}
16
+ return any(char.upper() not in valid_chars for char in sequence)
17
+
18
+ def sequence_to_numbers(sequence, mapping=None):
19
+ # 将ACGT序列转换为数字列表
20
+ if mapping is None:
21
+ mapping = {'A': 0, 'C': 1, 'G': 2, 'T': 3, 'N': 4}
22
+ return [mapping.get(base.upper(), -1) for base in sequence]
23
+
24
+ def to_base(line_str):
25
+ seq_base = 'ACGT'
26
+ line_blocks = line_str.split('\t')
27
+ line_data = [float(bi) for bi in line_blocks]
28
+ max_index = line_data.index(max(line_data))
29
+ # print(max_index)
30
+ base = seq_base[max_index]
31
+ return base
32
+
33
+ def read_motif(fasta_file):
34
+ motifs_all = {}
35
+ #fasta = {}
36
+ #motifs = []
37
+ #motif_names = []
38
+ fp = open(fasta_file, 'r')
39
+ header = ''
40
+ seq = ''
41
+ motif = []
42
+ for line in fp:
43
+ line_str = line.rstrip()
44
+ if line_str[0] == '>':
45
+ if header != '':
46
+ #fasta[header] = seq
47
+ #motifs.append(motif)
48
+ #motif_names.append(header)
49
+ motifs_all[header] = (motif, seq)
50
+ header = line_str[1:len(line_str)]
51
+ seq = ''
52
+ motif = []
53
+ else:
54
+ seq += to_base(line_str)
55
+ motif.append(to_mdata_n_frq(line_str))
56
+ else:
57
+ if header != '':
58
+ # fasta[header] = seq
59
+ # motifs.append(motif)
60
+ # motif_names.append(header)
61
+ motifs_all[header] = (motif, seq)
62
+ fp.close()
63
+ return motifs_all
64
+
65
+ def cal_base_freq(fasta_seqs):
66
+ base_sum = [0.25, 0.25, 0.25, 0.25, 0.25]
67
+ for seq_name in fasta_seqs.keys():
68
+ for base in fasta_seqs[seq_name]:
69
+ if base == 'A' or base == 'a':
70
+ base_sum[0] += 1
71
+ elif base == 'C' or base == 'c':
72
+ base_sum[1] += 1
73
+ elif base == 'G' or base == 'g':
74
+ base_sum[2] += 1
75
+ elif base == 'T' or base == 't':
76
+ base_sum[3] += 1
77
+ elif base == 'N' or base == 'n':
78
+ base_sum[4] += 1
79
+ bsum = sum(base_sum)
80
+ base_freq1 = [bs / bsum for bs in base_sum]
81
+ return base_freq1
82
+
83
+ def read_motif_single(fasta_file):
84
+ motif_name = ''
85
+ motif = []
86
+ for line in open(fasta_file, 'r'):
87
+ line_str = line.rstrip()
88
+ if line_str == '':
89
+ continue
90
+ if line_str[0] == '>':
91
+ motif_name = line_str[1:len(line_str)]
92
+ else:
93
+ motif.append(to_mdata_n_frq(line_str))
94
+ return motif_name, motif
95
+
96
+ def cal_base_freq(fasta_seqs):
97
+ base_sum = [0.25, 0.25, 0.25, 0.25, 0.25]
98
+ for seq_name in fasta_seqs.keys():
99
+ for base in fasta_seqs[seq_name]:
100
+ if base == 'A' or base == 'a':
101
+ base_sum[0] += 1
102
+ elif base == 'C' or base == 'c':
103
+ base_sum[1] += 1
104
+ elif base == 'G' or base == 'g':
105
+ base_sum[2] += 1
106
+ elif base == 'T' or base == 't':
107
+ base_sum[3] += 1
108
+ elif base == 'N' or base == 'n':
109
+ base_sum[4] += 1
110
+ bsum = sum(base_sum)
111
+ base_freq1 = [bs / bsum for bs in base_sum]
112
+ return base_freq1
113
+
114
+ def read_fasta(file):
115
+ fasta = {}
116
+ header = ''
117
+ seq = ''
118
+ seq_id = 0
119
+ for line in open(file, 'r'):
120
+ line_str = line.rstrip()
121
+ if line_str[0] == '>':
122
+ if header != '':
123
+ seq_id += 1
124
+ fasta[header] = seq
125
+ header = line_str[1:len(line_str)]
126
+ seq = ''
127
+ else:
128
+ seq += line_str
129
+ else:
130
+ if header != '':
131
+ seq_id += 1
132
+ fasta[header] = seq
133
+ seq_freq = cal_base_freq(fasta)
134
+ return fasta, seq_freq
135
+
136
+ def to_mdata_n(line_str):
137
+ seq_base = 'ACGT'
138
+ line_blocks = line_str.split('\t')
139
+ line_data = [float(bi) + 0.25 for bi in line_blocks]
140
+ line_data.append(0.25)
141
+ return line_data
142
+
143
+ def to_mdata_n_frq(line_str):
144
+ seq_base = 'ACGT'
145
+ line_blocks = line_str.split('\t')
146
+ line_data = [float(bi) + 0.25 for bi in line_blocks]
147
+ line_data.append(0.25)
148
+ sum_base = sum(line_data)
149
+ line_data_frq = [da / sum_base for da in line_data]
150
+ return line_data_frq
151
+
152
+ '''
153
+ def forward(x, params):
154
+ # global parameters
155
+ w1, b1, w2, b2, w3, b3 = params
156
+ x = x @ w1.t() + b1
157
+ x = torch.tanh(x)
158
+ x = x @ w2.t() + b2
159
+ x = torch.tanh(x)
160
+ x = x @ w3.t() + b3
161
+ x = torch.tanh(x)
162
+ return x
163
+ '''
164
+
165
+ def forward(x, params):
166
+ # global parameters
167
+ w1, b1, w2, b2, w3, b3 = params
168
+ x = x @ w1.t() + b1
169
+ x = F.relu(x)
170
+ x = x @ w2.t() + b2
171
+ x = F.relu(x)
172
+ x = x @ w3.t() + b3
173
+ x = F.relu(x)
174
+ return x
175
+
176
+ def predict_llrs(seq, motif, header, motif_name):
177
+ global parameters
178
+ base_freq, j_mean = parameters['jpar']
179
+ # print('base_freq, j_mean: ', base_freq, j_mean)
180
+ model_pars = parameters['model']
181
+ dseq = sequence_to_numbers(seq)
182
+ seq_len = len(seq)
183
+ motif_len = len(motif)
184
+ j_best = -10.0
185
+ best_pos = 0
186
+ best_llrs = []
187
+ llr_matrix = []
188
+ j_tmps = []
189
+ for si in range(seq_len - motif_len + 1):
190
+ llr_sum = 0.0
191
+ llrs = []
192
+ for mi in range(motif_len):
193
+ base = dseq[si + mi]
194
+ if base == 4:
195
+ llr = 0.0
196
+ else:
197
+ llr = math.log(motif[mi][base] / base_freq[base]);
198
+ llr_sum += llr
199
+ # print(motif[mi][base], base_freq[base])
200
+ llrs.append(llr)
201
+ # llrs_norm = [llr - j_mean for llr in llrs]
202
+ llrs_norm = llrs
203
+ # print('llrs_norm: ', llrs_norm)
204
+ j_tmp = llr_sum / motif_len
205
+ if j_tmp > 0.0:
206
+ llr_matrix.append(llrs_norm)
207
+ j_tmps.append(j_tmp)
208
+ data = torch.tensor(llr_matrix)
209
+ data = data.view(-1, motif_len)
210
+ logits = forward(data, model_pars)
211
+ predict = torch.sigmoid(logits * 4.0)
212
+ # print('predict.shape:', predict.shape)
213
+ col_index = 1
214
+ #indices = torch.nonzero(predict[:, col_index] >= 0.5, as_tuple=False)
215
+ indices = torch.nonzero((predict[:, 1] > 0.75) & (predict[:, 0] < 0.25), as_tuple=False)
216
+ for value in indices:
217
+ match_seq = seq[value.item(): value.item() + motif_len]
218
+ print(header, motif_name, value.item(), match_seq, j_tmps[value.item()], predict[value, 0].item(), predict[value, 1].item())
219
+
220
+ def predict_seq():
221
+ motifs_all = read_motif('motif_data.txt')
222
+ # fasta, seq_freq = read_fasta('GCF_000001405.40_GRCh38.p14_promoter_1.1k.txt')
223
+ fasta, seq_freq = read_fasta('GCF_000001405.40_GRCh38.p14_promoter_1.1k_rdm1000.txt')
224
+ # print(len(fasta))
225
+ for seq_name in fasta.keys():
226
+ seq = fasta[seq_name].upper()
227
+ if has_non_acgtn_upper(seq):
228
+ continue
229
+ for motif_name in motifs_all.keys():
230
+ if motif_name != 'ANDR_HUMAN.H11MO.0.A':
231
+ continue
232
+ motif = motifs_all[motif_name][0]
233
+ predict_llrs(seq, motif, seq_name, motif_name)
234
+
235
+ def seq2onehot(seq_in, motif_len, motif, base_freq):
236
+ datas = []
237
+ poss = []
238
+ j_tmps = []
239
+ seq_len = len(seq_in)
240
+ for si in range(seq_len - motif_len + 1):
241
+ seq = seq_in[si: si + motif_len]
242
+ j_tmp = jindex(seq, motif, base_freq)
243
+ # print(j_tmp)
244
+ if j_tmp <= 0.0:
245
+ continue
246
+ poss.append(si)
247
+ j_tmps.append(j_tmp)
248
+ seq_onehot = []
249
+ dseq = sequence_to_numbers(seq.upper())
250
+ for si in dseq:
251
+ sonehot = [0.0, 0.0, 0.0, 0.0, 0.0]
252
+ sonehot[si] = 1.0
253
+ seq_onehot += sonehot
254
+ datas.append(seq_onehot)
255
+ return datas, poss, j_tmps
256
+
257
+ def jindex(seq, motif, base_freq):
258
+ dseq = sequence_to_numbers(seq)
259
+ motif_len = len(motif)
260
+ llr_sum = 0.0
261
+ for mi in range(motif_len):
262
+ base = dseq[mi]
263
+ if base == 4:
264
+ llr = 0.0
265
+ else:
266
+ llr = math.log(motif[mi][base] / base_freq[base]);
267
+ llr_sum += llr
268
+ j_tmp = llr_sum / motif_len
269
+ return j_tmp
270
+
271
+ def predict_tfbs(file, motif_name, motif, data_dim, model, base_freq, out_file):
272
+ fasta, seq_freq = read_fasta(file)
273
+ # print(len(fasta))
274
+ motif_len = int(data_dim[0] / 5)
275
+ fp = open(out_file, 'w')
276
+ for seq_name in fasta.keys():
277
+ seq = fasta[seq_name].upper()
278
+ if has_non_acgtn_upper(seq):
279
+ continue
280
+ data_onehot, poss, j_tmps = seq2onehot(seq, motif_len, motif, base_freq)
281
+ data = torch.tensor(data_onehot)
282
+ data = data.view(-1, data_dim[0])
283
+ logits = model(data).flatten().tolist()
284
+ #indices = torch.nonzero(logits > 0.5, as_tuple=False).squeeze().tolist()
285
+ #indices = torch.nonzero(logits[:, 1] > logits[:, 0], as_tuple=False)
286
+ #indices = [i for i, value in enumerate(logits) if value > 0.5]
287
+ for vi in range(len(logits)):
288
+ if logits[vi] > 0.5:
289
+ value = vi
290
+ match_seq = seq[poss[value]: poss[value] + motif_len]
291
+ fp.write('\t'.join([seq_name, str(poss[value]), match_seq, str(j_tmps[value]), str(logits[value])]) + '\n')
292
+ fp.close()
293
+
294
+ def load_module(file):
295
+ saved_data = torch.load(file)
296
+ base_freq = saved_data['base_freq']
297
+ data_dim = saved_data['data_dim']
298
+ w1 = saved_data['w1']
299
+ b1 = saved_data['b1']
300
+ w2 = saved_data['w2']
301
+ b2 = saved_data['b2']
302
+ w3 = saved_data['w3']
303
+ b3 = saved_data['b3']
304
+ parameters = {'jpar': [base_freq, data_dim], 'model': [w1, b1, w2, b2, w3, b3]}
305
+ return parameters
306
+
307
+ def read_motif_cmp(file):
308
+ motif_cmp = {}
309
+ for line in open(file, 'r'):
310
+ if line[0: 3] != 'Cmp':
311
+ continue
312
+ lblocks = line.rstrip().split(' ')
313
+ motif_cmp[lblocks[1]] = lblocks[2]
314
+ return motif_cmp
315
+
316
+ def loade_model_pars(model_file, data_dim):
317
+ # 加载模型参数
318
+ # 首先需要创建相同结构的模型
319
+ global x_dimention
320
+ global optimizer
321
+ model_pars = torch.load(model_file)
322
+ #seq_freq = model_pars['base_freq']
323
+ #data_dim = model_pars['data_dim']
324
+ #del model_pars['base_freq']
325
+ #del model_pars['data_dim']
326
+ x_dimention = data_dim[0]
327
+ y_dimention = data_dim[1]
328
+ # 1. 定义模型
329
+ model = nn.Sequential(
330
+ nn.Linear(x_dimention, 64),
331
+ nn.ReLU(),
332
+ nn.Linear(64, 32),
333
+ nn.ReLU(),
334
+ nn.Linear(32, 1),
335
+ nn.Sigmoid()
336
+ )
337
+ model.load_state_dict(model_pars)
338
+ #new_model.eval()
339
+ return model
340
+
341
+ def script(motif_id, model_id, seq_file, out_file, data_dir=None):
342
+ if data_dir == None:
343
+ data_dir = PyTFBS_data_dir
344
+ if not (os.path.exists(data_dir) and os.path.isdir(data_dir)):
345
+ print("PyTFBS_data folder can not found!")
346
+ exit(1)
347
+ motif_file = data_dir + '/motif/' + motif_id + '.pwm'
348
+ model_file = data_dir + '/par/' + model_id + '_par.pth'
349
+ if not os.path.exists(motif_file):
350
+ print(motif_file, 'not exist!')
351
+ exit(1)
352
+ if not os.path.exists(model_file):
353
+ print(model_file, 'not exist!')
354
+ exit(1)
355
+ if not os.path.exists(seq_file):
356
+ print(seq_file, 'not exist!')
357
+ exit(1)
358
+ motif_name, motif = read_motif_single(motif_file)
359
+ data_dim = [len(motif) * 5, 1]
360
+ model = loade_model_pars(model_file, data_dim)
361
+ # run predict
362
+ fasta, seq_freq = read_fasta(seq_file)
363
+ # seq_freq = [0.22698189046475983, 0.2685381453335145, 0.2744548727333858, 0.22957776564437835, 0.00044732582396156444]
364
+ motif_len = len(motif)
365
+ fp = open(out_file, 'w')
366
+ seq_n = len(fasta)
367
+ finished_n = 0
368
+ for seq_name in fasta.keys():
369
+ seq = fasta[seq_name].upper()
370
+ if has_non_acgtn_upper(seq):
371
+ continue
372
+ data_onehot, poss, j_tmps = seq2onehot(seq, motif_len, motif, seq_freq)
373
+ data = torch.tensor(data_onehot)
374
+ data = data.view(-1, data_dim[0])
375
+ logits = model(data).flatten().tolist()
376
+ #indices = torch.nonzero(logits > 0.5, as_tuple=False).squeeze().tolist()
377
+ #indices = torch.nonzero(logits[:, 1] > logits[:, 0], as_tuple=False)
378
+ #indices = [i for i, value in enumerate(logits) if value > 0.5]
379
+ for vi in range(len(logits)):
380
+ if logits[vi] > 0.5:
381
+ value = vi
382
+ match_seq = seq[poss[value]: poss[value] + motif_len]
383
+ fp.write('\t'.join([seq_name, str(poss[value]), match_seq, str(j_tmps[value]), str(logits[value])]) + '\n')
384
+ finished_n += 1
385
+ if finished_n % 100 == 0 or finished_n == seq_n:
386
+ print(str(finished_n) + '/' + str(seq_n))
387
+ fp.close()
388
+
389
+ def win_bin(motif_id, model_id, seq_file, thread_n, out_file, data_dir=None):
390
+ if data_dir == None:
391
+ data_dir = PyTFBS_data_dir
392
+ if not (os.path.exists(data_dir) and os.path.isdir(data_dir)):
393
+ print("PyTFBS_data folder can not found!")
394
+ exit(1)
395
+ motif_file = data_dir + '/motif/' + motif_id + '.pwm'
396
+ model_file = data_dir + '/trace/' + model_id + '_trace.pth'
397
+ PyTFBS_exe = data_dir + '/PyTFBS_bin/PyTFBS.exe'
398
+ if not os.path.exists(motif_file):
399
+ print(motif_file, 'not exist!')
400
+ exit(1)
401
+ if not os.path.exists(model_file):
402
+ print(model_file, 'not exist!')
403
+ exit(1)
404
+ if not os.path.exists(seq_file):
405
+ print(seq_file, 'not exist!')
406
+ exit(1)
407
+ cmd = '\"' + PyTFBS_exe + '\" -m ' + motif_file + ' -z ' + model_file + ' -i ' + seq_file + ' -t ' + str(thread_n) + ' -o ' + out_file + ''
408
+ cmd = cmd.replace('/', '\\')
409
+ print(cmd)
410
+ os.system(cmd)
411
+
412
+ if __name__ == '__main__':
413
+ script('CEBPB_HUMAN.H11MO.0.A', 'CEBPB_HUMAN.H11MO.0.A_1231', 'GCF_000001405.40_GRCh38.p14_promoter_1.1k.txt', 'out_file.txt')
@@ -0,0 +1,69 @@
1
+ Metadata-Version: 2.4
2
+ Name: PyTFBS
3
+ Version: 1.0.2
4
+ Summary: PyTFBS: A Python Package for Transcription Factor Binding Site Prediction
5
+ Author-email: Tinghua Huang <thua45@126.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/thua45/pytfbs
8
+ Project-URL: Documentation, https://github.com/thua45/pytfbs#readme
9
+ Project-URL: Repository, https://github.com/thua45/pytfbs
10
+ Project-URL: Issues, https://github.com/thua45/pytfbs/issues
11
+ Keywords: PyTFBS,Transcription Factor,Binding Site
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: torch
21
+ Requires-Dist: numpy==1.26; sys_platform == "darwin"
22
+ Requires-Dist: numpy>=2.0; sys_platform == "win32"
23
+ Requires-Dist: numpy>=2.0; sys_platform == "linux"
24
+ Provides-Extra: dev
25
+ Requires-Dist: matplotlib; extra == "dev"
26
+ Provides-Extra: test
27
+ Requires-Dist: matplotlib; extra == "test"
28
+ Dynamic: license-file
29
+
30
+ # PyTFBS
31
+
32
+ A Python package for dpredicting transcription factor binding sites.
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ pip install torch numpy PyTFBS
38
+ ```
39
+
40
+ ## Usage
41
+
42
+ ```python
43
+ from PyTFBS import motif, predict
44
+
45
+ # download PyTFBS data, only need to run once!!!
46
+ motif.download_data()
47
+
48
+ # list available models
49
+ motif.list_models(species='Homo sapiens', accuracy=0.9, sensitivity=0.9)
50
+
51
+ # get avaiable motifs
52
+ motifs = motif.get_motifs(species='Homo sapiens', accuracy=0.9, sensitivity=0.9)
53
+ print(motifs)
54
+
55
+ # get models based on motif name
56
+ models = motif.get_models('RFX2_HUMAN.H11MO.0.A')
57
+ print(models)
58
+
59
+ # predict one model
60
+ predict.script('CEBPB_HUMAN.H11MO.0.A', 'CEBPB_HUMAN.H11MO.0.A_1231', 'input_seq_file.fasta', 'out_file.txt')
61
+
62
+ # speed up using mutil-threading (for Windows OS only)
63
+ predict.win_bin('CEBPB_HUMAN.H11MO.0.A', 'CEBPB_HUMAN.H11MO.0.A_1231', 'input_seq_file.fasta', 64, 'out_file.txt')
64
+
65
+ # run prediction with user motif data
66
+ # the my_motif_dir should be organized as [[motif], [trace], [par]]
67
+ predict.script('CEBPB_HUMAN.H11MO.0.A', 'CEBPB_HUMAN.H11MO.0.A_1231', 'input_seq_file.fasta', data_dir='my_motif_dir')
68
+ predict.win_bin('CEBPB_HUMAN.H11MO.0.A', 'CEBPB_HUMAN.H11MO.0.A_1231', 'input_seq_file.fasta', 64, data_dir='my_motif_dir')
69
+ ```
@@ -0,0 +1,12 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ PyTFBS/__init__.py
5
+ PyTFBS/motif.py
6
+ PyTFBS/predict.py
7
+ PyTFBS.egg-info/PKG-INFO
8
+ PyTFBS.egg-info/SOURCES.txt
9
+ PyTFBS.egg-info/dependency_links.txt
10
+ PyTFBS.egg-info/entry_points.txt
11
+ PyTFBS.egg-info/requires.txt
12
+ PyTFBS.egg-info/top_level.txt
@@ -1,2 +1,3 @@
1
1
  [console_scripts]
2
2
  PyTFBS = PyTFBS.cli:main
3
+ PyTFBS-cleanup = PyTFBS.uninstall:cleanup
@@ -0,0 +1,16 @@
1
+ torch
2
+
3
+ [:sys_platform == "darwin"]
4
+ numpy==1.26
5
+
6
+ [:sys_platform == "linux"]
7
+ numpy>=2.0
8
+
9
+ [:sys_platform == "win32"]
10
+ numpy>=2.0
11
+
12
+ [dev]
13
+ matplotlib
14
+
15
+ [test]
16
+ matplotlib
pytfbs-1.0.2/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # PyTFBS
2
+
3
+ A Python package for dpredicting transcription factor binding sites.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install torch numpy PyTFBS
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```python
14
+ from PyTFBS import motif, predict
15
+
16
+ # download PyTFBS data, only need to run once!!!
17
+ motif.download_data()
18
+
19
+ # list available models
20
+ motif.list_models(species='Homo sapiens', accuracy=0.9, sensitivity=0.9)
21
+
22
+ # get avaiable motifs
23
+ motifs = motif.get_motifs(species='Homo sapiens', accuracy=0.9, sensitivity=0.9)
24
+ print(motifs)
25
+
26
+ # get models based on motif name
27
+ models = motif.get_models('RFX2_HUMAN.H11MO.0.A')
28
+ print(models)
29
+
30
+ # predict one model
31
+ predict.script('CEBPB_HUMAN.H11MO.0.A', 'CEBPB_HUMAN.H11MO.0.A_1231', 'input_seq_file.fasta', 'out_file.txt')
32
+
33
+ # speed up using mutil-threading (for Windows OS only)
34
+ predict.win_bin('CEBPB_HUMAN.H11MO.0.A', 'CEBPB_HUMAN.H11MO.0.A_1231', 'input_seq_file.fasta', 64, 'out_file.txt')
35
+
36
+ # run prediction with user motif data
37
+ # the my_motif_dir should be organized as [[motif], [trace], [par]]
38
+ predict.script('CEBPB_HUMAN.H11MO.0.A', 'CEBPB_HUMAN.H11MO.0.A_1231', 'input_seq_file.fasta', data_dir='my_motif_dir')
39
+ predict.win_bin('CEBPB_HUMAN.H11MO.0.A', 'CEBPB_HUMAN.H11MO.0.A_1231', 'input_seq_file.fasta', 64, data_dir='my_motif_dir')
40
+ ```
@@ -4,28 +4,27 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "PyTFBS"
7
- version = "1.0.0"
7
+ version = "1.0.2"
8
+ license = "MIT" # SPDX expression
8
9
  description = "PyTFBS: A Python Package for Transcription Factor Binding Site Prediction"
9
10
  readme = "README.md"
10
- requires-python = ">=3.8"
11
+ requires-python = ">=3.11"
11
12
  authors = [
12
13
  {name = "Tinghua Huang", email = "thua45@126.com"}
13
14
  ]
14
- license = {text = "Acdemic"}
15
15
  keywords = ["PyTFBS", "Transcription Factor", "Binding Site"]
16
16
  classifiers = [
17
17
  "Development Status :: 4 - Beta",
18
18
  "Intended Audience :: Developers",
19
- "License :: OSI Approved :: MIT License",
20
- "Programming Language :: Python :: 3",
21
- "Programming Language :: Python :: 3.8",
22
- "Programming Language :: Python :: 3.9",
23
- "Programming Language :: Python :: 3.10",
24
19
  "Programming Language :: Python :: 3.11",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
25
22
  ]
26
23
  dependencies = [
27
- "requests>=2.25.0",
28
- "numpy>=1.21.0",
24
+ "torch",
25
+ "numpy==1.26; sys_platform == 'darwin'",
26
+ "numpy>=2.0; sys_platform == 'win32'",
27
+ "numpy>=2.0; sys_platform == 'linux'",
29
28
  ]
30
29
 
31
30
  [project.urls]
@@ -36,18 +35,17 @@ Issues = "https://github.com/thua45/pytfbs/issues"
36
35
 
37
36
  [project.optional-dependencies]
38
37
  dev = [
39
- "pytest>=6.0",
38
+ "matplotlib",
40
39
  ]
41
40
  test = [
42
- "pytest>=6.0",
41
+ "matplotlib",
43
42
  ]
44
43
 
45
44
  [project.scripts]
46
45
  PyTFBS = "PyTFBS.cli:main"
46
+ PyTFBS-cleanup = "PyTFBS.uninstall:cleanup"
47
47
 
48
48
  [tool.setuptools]
49
- packages = {find = {where = ["src"]}}
50
- package-dir = {"" = "src"}
49
+ packages = ["PyTFBS"]
50
+ package-data = {"*" = ["*.txt", "*.md", "*.rst", "data/*"]}
51
51
 
52
- [tool.setuptools.package-data]
53
- mypackage = ["*.txt", "*.json", "data/*", "templates/*"]
pytfbs-1.0.0/PKG-INFO DELETED
@@ -1,48 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: PyTFBS
3
- Version: 1.0.0
4
- Summary: PyTFBS: A Python Package for Transcription Factor Binding Site Prediction
5
- Author-email: Tinghua Huang <thua45@126.com>
6
- License: Acdemic
7
- Project-URL: Homepage, https://github.com/thua45/pytfbs
8
- Project-URL: Documentation, https://github.com/thua45/pytfbs#readme
9
- Project-URL: Repository, https://github.com/thua45/pytfbs
10
- Project-URL: Issues, https://github.com/thua45/pytfbs/issues
11
- Keywords: PyTFBS,Transcription Factor,Binding Site
12
- Classifier: Development Status :: 4 - Beta
13
- Classifier: Intended Audience :: Developers
14
- Classifier: License :: OSI Approved :: MIT License
15
- Classifier: Programming Language :: Python :: 3
16
- Classifier: Programming Language :: Python :: 3.8
17
- Classifier: Programming Language :: Python :: 3.9
18
- Classifier: Programming Language :: Python :: 3.10
19
- Classifier: Programming Language :: Python :: 3.11
20
- Requires-Python: >=3.8
21
- Description-Content-Type: text/markdown
22
- License-File: LICENSE
23
- Requires-Dist: requests>=2.25.0
24
- Requires-Dist: numpy>=1.21.0
25
- Provides-Extra: dev
26
- Requires-Dist: pytest>=6.0; extra == "dev"
27
- Provides-Extra: test
28
- Requires-Dist: pytest>=6.0; extra == "test"
29
- Dynamic: license-file
30
-
31
- # My Package
32
-
33
- A Python package for dpredicting transcription factor binding sites.
34
-
35
- ## Installation
36
-
37
- ```bash
38
- pip install PyTFBS
39
- ```
40
-
41
- ## Usage
42
-
43
- ```python
44
- import PyTFBS
45
-
46
- result = PyTFBS.predict()
47
- print(result)
48
- ```
pytfbs-1.0.0/README.md DELETED
@@ -1,18 +0,0 @@
1
- # My Package
2
-
3
- A Python package for dpredicting transcription factor binding sites.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- pip install PyTFBS
9
- ```
10
-
11
- ## Usage
12
-
13
- ```python
14
- import PyTFBS
15
-
16
- result = PyTFBS.predict()
17
- print(result)
18
- ```
File without changes
@@ -1,48 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: PyTFBS
3
- Version: 1.0.0
4
- Summary: PyTFBS: A Python Package for Transcription Factor Binding Site Prediction
5
- Author-email: Tinghua Huang <thua45@126.com>
6
- License: Acdemic
7
- Project-URL: Homepage, https://github.com/thua45/pytfbs
8
- Project-URL: Documentation, https://github.com/thua45/pytfbs#readme
9
- Project-URL: Repository, https://github.com/thua45/pytfbs
10
- Project-URL: Issues, https://github.com/thua45/pytfbs/issues
11
- Keywords: PyTFBS,Transcription Factor,Binding Site
12
- Classifier: Development Status :: 4 - Beta
13
- Classifier: Intended Audience :: Developers
14
- Classifier: License :: OSI Approved :: MIT License
15
- Classifier: Programming Language :: Python :: 3
16
- Classifier: Programming Language :: Python :: 3.8
17
- Classifier: Programming Language :: Python :: 3.9
18
- Classifier: Programming Language :: Python :: 3.10
19
- Classifier: Programming Language :: Python :: 3.11
20
- Requires-Python: >=3.8
21
- Description-Content-Type: text/markdown
22
- License-File: LICENSE
23
- Requires-Dist: requests>=2.25.0
24
- Requires-Dist: numpy>=1.21.0
25
- Provides-Extra: dev
26
- Requires-Dist: pytest>=6.0; extra == "dev"
27
- Provides-Extra: test
28
- Requires-Dist: pytest>=6.0; extra == "test"
29
- Dynamic: license-file
30
-
31
- # My Package
32
-
33
- A Python package for dpredicting transcription factor binding sites.
34
-
35
- ## Installation
36
-
37
- ```bash
38
- pip install PyTFBS
39
- ```
40
-
41
- ## Usage
42
-
43
- ```python
44
- import PyTFBS
45
-
46
- result = PyTFBS.predict()
47
- print(result)
48
- ```
@@ -1,11 +0,0 @@
1
- LICENSE
2
- README.md
3
- pyproject.toml
4
- src/PyTFBS/__init__.py
5
- src/PyTFBS/tango.py
6
- src/PyTFBS.egg-info/PKG-INFO
7
- src/PyTFBS.egg-info/SOURCES.txt
8
- src/PyTFBS.egg-info/dependency_links.txt
9
- src/PyTFBS.egg-info/entry_points.txt
10
- src/PyTFBS.egg-info/requires.txt
11
- src/PyTFBS.egg-info/top_level.txt
@@ -1,8 +0,0 @@
1
- requests>=2.25.0
2
- numpy>=1.21.0
3
-
4
- [dev]
5
- pytest>=6.0
6
-
7
- [test]
8
- pytest>=6.0
File without changes
File without changes
File without changes