FunVIP 0.3.20__py3-none-any.whl
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.
- FunVIP-0.3.20.dist-info/LICENSE +674 -0
- FunVIP-0.3.20.dist-info/METADATA +32 -0
- FunVIP-0.3.20.dist-info/RECORD +36 -0
- FunVIP-0.3.20.dist-info/WHEEL +5 -0
- FunVIP-0.3.20.dist-info/entry_points.txt +3 -0
- FunVIP-0.3.20.dist-info/top_level.txt +3 -0
- data/__init__.py +0 -0
- external/BLAST_Windows/bin/cleanup-blastdb-volumes.py +162 -0
- external/__init__.py +0 -0
- src/__init__.py +0 -0
- src/align.py +124 -0
- src/cluster.py +510 -0
- src/command.py +360 -0
- src/concatenate.py +356 -0
- src/dataset.py +716 -0
- src/ext.py +448 -0
- src/hasher.py +98 -0
- src/initialize.py +335 -0
- src/logger.py +69 -0
- src/logics.py +104 -0
- src/modeltest.py +443 -0
- src/ncbi.py +160 -0
- src/opt_generator.py +72 -0
- src/patch.py +261 -0
- src/reporter.py +875 -0
- src/save.py +181 -0
- src/search.py +440 -0
- src/tool.py +309 -0
- src/tree.py +222 -0
- src/tree_interpretation.py +1379 -0
- src/tree_interpretation_pipe.py +679 -0
- src/trim.py +118 -0
- src/validate_input.py +846 -0
- src/validate_option.py +1609 -0
- src/validation.py +38 -0
- src/version.py +337 -0
src/modeltest.py
ADDED
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
from funvip.src import ext
|
|
2
|
+
import os, shutil, copy
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
cmd_translator_modeltestng_fasttree = {
|
|
6
|
+
"JC": "",
|
|
7
|
+
"GTR": "-gtr",
|
|
8
|
+
"JC+G4": "-gamma",
|
|
9
|
+
"GTR+G4": "-gtr -gamma",
|
|
10
|
+
}
|
|
11
|
+
cmd_translator_modelfinder_fasttree = {
|
|
12
|
+
"JC": "",
|
|
13
|
+
"GTR": "-gtr",
|
|
14
|
+
"JC+G4": "-gamma",
|
|
15
|
+
"GTR+G4": "-gtr -gamma",
|
|
16
|
+
}
|
|
17
|
+
cmd_translator_modelfinder_raxml = {
|
|
18
|
+
"JC": "-m JC",
|
|
19
|
+
"JC+G4": "-m JC",
|
|
20
|
+
"JC+I": "-m JC",
|
|
21
|
+
"JC+G4+I": "-m JC",
|
|
22
|
+
"HKY": "-m HKY85",
|
|
23
|
+
"HKY+G4": "-m HKY85",
|
|
24
|
+
"HKY+I": "-m HKY85",
|
|
25
|
+
"HKY+G4+I": "-m HKY85",
|
|
26
|
+
"K80": "-m K80",
|
|
27
|
+
"K80+G4": "-m K80",
|
|
28
|
+
"TrNef": "-m GTRCAT",
|
|
29
|
+
"TrNef+G4": "-m GTRGAMMA",
|
|
30
|
+
"TrNef+I": "-m GTRCATI",
|
|
31
|
+
"TrNef+G4+I": "-m GTRGAMMAI",
|
|
32
|
+
"TPM1": "-m GTRCAT",
|
|
33
|
+
"TPM1+G4": "-m GTRGAMMA",
|
|
34
|
+
"TPM1+I": "-m GTRCATI",
|
|
35
|
+
"TPM1+G4+I": "-m GTRGAMMAI",
|
|
36
|
+
"TPM2": "-m GTRCAT",
|
|
37
|
+
"TPM2+G4": "-m GTRGAMMA",
|
|
38
|
+
"TPM2+I": "-m GTRCATI",
|
|
39
|
+
"TPM2+G4+I": "-m GTRGAMMAI",
|
|
40
|
+
"TPM3": "-m GTRCAT",
|
|
41
|
+
"TPM3+G4": "-m GTRGAMMA",
|
|
42
|
+
"TPM3+I": "-m GTRCATI",
|
|
43
|
+
"TPM3+G4+I": "-m GTRGAMMAI",
|
|
44
|
+
"TIM1": "-m GTRCAT",
|
|
45
|
+
"TIM1+G4": "-m GTRGAMMA",
|
|
46
|
+
"TIM1+I": "-m GTRCATI",
|
|
47
|
+
"TIM1+G4+I": "-m GTRGAMMAI",
|
|
48
|
+
"TIM2": "-m GTRCAT",
|
|
49
|
+
"TIM2+G4": "-m GTRGAMMA",
|
|
50
|
+
"TIM2+I": "-m GTRCATI",
|
|
51
|
+
"TIM2+G4+I": "-m GTRGAMMAI",
|
|
52
|
+
"TIM3": "-m GTRCAT",
|
|
53
|
+
"TIM3+G4": "-m GTRGAMMA",
|
|
54
|
+
"TIM3+I": "-m GTRCATI",
|
|
55
|
+
"TIM3+G4+I": "-m GTRGAMMAI",
|
|
56
|
+
"TVMef": "-m GTRCAT",
|
|
57
|
+
"TVMef+G4": "-m GTRGAMMA",
|
|
58
|
+
"TVMef+I": "-m GTRCATI",
|
|
59
|
+
"TVMef+G4+I": "-m GTRGAMMAI",
|
|
60
|
+
"SYM": "-m GTRCAT",
|
|
61
|
+
"SYM+G4": "-m GTRGAMMA",
|
|
62
|
+
"SYM+I": "-m GTRCATI",
|
|
63
|
+
"SYM+G4+I": "-m GTRGAMMAI",
|
|
64
|
+
"F81+FO": "-m GTRCATX",
|
|
65
|
+
"F81+FO+G4": "-m GTRGAMMAX",
|
|
66
|
+
"F81+FO+I": "-m GTRCATIX",
|
|
67
|
+
"F81+FO+G4+I": "-m GTRGAMMAIX",
|
|
68
|
+
"TrN+FO": "-m GTRCATX",
|
|
69
|
+
"TrN+FO+G4": "-m GTRGAMMAX",
|
|
70
|
+
"TrN+FO+I": "-m GTRCATIX",
|
|
71
|
+
"TrN+FO+G4+I": "-m GTRGAMMAIX",
|
|
72
|
+
"TPM1uf+FO": "-m GTRCATX",
|
|
73
|
+
"TPM1uf+FO+G4": "-m GTRGAMMAX",
|
|
74
|
+
"TPM1uf+FO+I": "-m GTRCATIX",
|
|
75
|
+
"TPM1uf+FO+G4+I": "-m GTRGAMMAIX",
|
|
76
|
+
"TPM2uf+FO": "-m GTRCATX",
|
|
77
|
+
"TPM2uf+FO+G4": "-m GTRGAMMAX",
|
|
78
|
+
"TPM2uf+FO+I": "-m GTRCATIX",
|
|
79
|
+
"TPM2uf+FO+G4+I": "-m GTRGAMMAIX",
|
|
80
|
+
"TPM3uf+FO": "-m GTRCATX",
|
|
81
|
+
"TPM3uf+FO+G4": "-m GTRGAMMAX",
|
|
82
|
+
"TPM3uf+FO+I": "-m GTRCATIX",
|
|
83
|
+
"TPM3uf+FO+G4+I": "-m GTRGAMMAIX",
|
|
84
|
+
"TIM1uf+FO": "-m GTRCATX",
|
|
85
|
+
"TIM1uf+FO+G4": "-m GTRGAMMAX",
|
|
86
|
+
"TIM1uf+FO+I": "-m GTRCATIX",
|
|
87
|
+
"TIM1uf+FO+G4+I": "-m GTRGAMMAIX",
|
|
88
|
+
"TIM2uf+FO": "-m GTRCATX",
|
|
89
|
+
"TIM2uf+FO+G4": "-m GTRGAMMAX",
|
|
90
|
+
"TIM2uf+FO+I": "-m GTRCATIX",
|
|
91
|
+
"TIM2uf+FO+G4+I": "-m GTRGAMMAIX",
|
|
92
|
+
"TIM3uf+FO": "-m GTRCATX",
|
|
93
|
+
"TIM3uf+FO+G4": "-m GTRGAMMAX",
|
|
94
|
+
"TIM3uf+FO+I": "-m GTRCATIX",
|
|
95
|
+
"TIM3uf+FO+G4+I": "-m GTRGAMMAIX",
|
|
96
|
+
"TVM+FO": "-m GTRCATX",
|
|
97
|
+
"TVM+FO+G4": "-m GTRGAMMAX",
|
|
98
|
+
"TVM+FO+I": "-m GTRCATIX",
|
|
99
|
+
"TVM+FO+G4+I": "-m GTRGAMMAIX",
|
|
100
|
+
"GTR+FO": "-m GTRCATX",
|
|
101
|
+
"GTR+FO+G4": "-m GTRGAMMAX",
|
|
102
|
+
"GTR+FO+I": "-m GTRCATIX",
|
|
103
|
+
"GTR+FO+G4+I": "-m GTRGAMMAIX",
|
|
104
|
+
"F81": "-m GTRCATX",
|
|
105
|
+
"F81+G4": "-m GTRGAMMAX",
|
|
106
|
+
"F81+I": "-m GTRCATIX",
|
|
107
|
+
"F81+G4+I": "-m GTRGAMMAIX",
|
|
108
|
+
"TrN": "-m GTRCAT",
|
|
109
|
+
"TrN+G4": "-m GTRGAMMA",
|
|
110
|
+
"TrN+I": "-m GTRCATI",
|
|
111
|
+
"TrN+G4+I": "-m GTRGAMMAI",
|
|
112
|
+
"TPM1uf": "-m GTRCAT",
|
|
113
|
+
"TPM1uf+G4": "-m GTRGAMMA",
|
|
114
|
+
"TPM1uf+I": "-m GTRCATI",
|
|
115
|
+
"TPM1uf+G4+I": "-m GTRGAMMAI",
|
|
116
|
+
"TPM2uf": "-m GTRCAT",
|
|
117
|
+
"TPM2uf+G4": "-m GTRGAMMA",
|
|
118
|
+
"TPM2uf+I": "-m GTRCATI",
|
|
119
|
+
"TPM2uf+G4+I": "-m GTRGAMMAI",
|
|
120
|
+
"TPM3uf": "-m GTRCAT",
|
|
121
|
+
"TPM3uf+G4": "-m GTRGAMMA",
|
|
122
|
+
"TPM3uf+I": "-m GTRCATI",
|
|
123
|
+
"TPM3uf+G4+I": "-m GTRGAMMAI",
|
|
124
|
+
"TIM1uf": "-m GTRCAT",
|
|
125
|
+
"TIM1uf+G4": "-m GTRGAMMA",
|
|
126
|
+
"TIM1uf+I": "-m GTRCATI",
|
|
127
|
+
"TIM1uf+G4+I": "-m GTRGAMMAI",
|
|
128
|
+
"TIM2uf": "-m GTRCAT",
|
|
129
|
+
"TIM2uf+G4": "-m GTRGAMMA",
|
|
130
|
+
"TIM2uf+I": "-m GTRCATI",
|
|
131
|
+
"TIM2uf+G4+I": "-m GTRGAMMAI",
|
|
132
|
+
"TIM3uf": "-m GTRCAT",
|
|
133
|
+
"TIM3uf+G4": "-m GTRGAMMA",
|
|
134
|
+
"TIM3uf+I": "-m GTRCATI",
|
|
135
|
+
"TIM3uf+G4+I": "-m GTRGAMMAI",
|
|
136
|
+
"TVM": "-m GTRCAT",
|
|
137
|
+
"TVM+G4": "-m GTRGAMMA",
|
|
138
|
+
"TVM+I": "-m GTRCATI",
|
|
139
|
+
"TVM+G4+I": "-m GTRGAMMAI",
|
|
140
|
+
"GTR": "-m GTRCAT",
|
|
141
|
+
"GTR+G4": "-m GTRGAMMA",
|
|
142
|
+
"GTR+I": "-m GTRCATI",
|
|
143
|
+
"GTR+G4+I": "-m GTRGAMMAI",
|
|
144
|
+
"F81+F": "-m GTRCAT",
|
|
145
|
+
"F81+F+G4": "-m GTRGAMMA",
|
|
146
|
+
"F81+F+I": "-m GTRCATI",
|
|
147
|
+
"F81+F+G4+I": "-m GTRGAMMAI",
|
|
148
|
+
"TrN+F": "-m GTRCAT",
|
|
149
|
+
"TrN+F+G4": "-m GTRGAMMA",
|
|
150
|
+
"TrN+F+I": "-m GTRCATI",
|
|
151
|
+
"TrN+F+G4+I": "-m GTRGAMMAI",
|
|
152
|
+
"TPM1uf+F": "-m GTRCAT",
|
|
153
|
+
"TPM1uf+F+G4": "-m GTRGAMMA",
|
|
154
|
+
"TPM1uf+F+I": "-m GTRCATI",
|
|
155
|
+
"TPM1uf+F+G4+I": "-m GTRGAMMAI",
|
|
156
|
+
"TPM2uf+F": "-m GTRCAT",
|
|
157
|
+
"TPM2uf+F+G4": "-m GTRGAMMA",
|
|
158
|
+
"TPM2uf+F+I": "-m GTRCATI",
|
|
159
|
+
"TPM2uf+F+G4+I": "-m GTRGAMMAI",
|
|
160
|
+
"TPM3uf+F": "-m GTRCAT",
|
|
161
|
+
"TPM3uf+F+G4": "-m GTRGAMMA",
|
|
162
|
+
"TPM3uf+F+I": "-m GTRCATI",
|
|
163
|
+
"TPM3uf+F+G4+I": "-m GTRGAMMAI",
|
|
164
|
+
"TIM1uf+F": "-m GTRCAT",
|
|
165
|
+
"TIM1uf+F+G4": "-m GTRGAMMA",
|
|
166
|
+
"TIM1uf+F+I": "-m GTRCATI",
|
|
167
|
+
"TIM1uf+F+G4+I": "-m GTRGAMMAI",
|
|
168
|
+
"TIM2uf+F": "-m GTRCAT",
|
|
169
|
+
"TIM2uf+F+G4": "-m GTRGAMMA",
|
|
170
|
+
"TIM2uf+F+I": "-m GTRCATI",
|
|
171
|
+
"TIM2uf+F+G4+I": "-m GTRGAMMAI",
|
|
172
|
+
"TIM3uf+F": "-m GTRCAT",
|
|
173
|
+
"TIM3uf+F+G4": "-m GTRGAMMA",
|
|
174
|
+
"TIM3uf+F+I": "-m GTRCATI",
|
|
175
|
+
"TIM3uf+F+G4+I": "-m GTRGAMMAI",
|
|
176
|
+
"TVM+F": "-m GTRCAT",
|
|
177
|
+
"TVM+F+G4": "-m GTRGAMMA",
|
|
178
|
+
"TVM+F+I": "-m GTRCATI",
|
|
179
|
+
"TVM+F+G4+I": "-m GTRGAMMAI",
|
|
180
|
+
"GTR+F": "-m GTRCAT",
|
|
181
|
+
"GTR+F+G4": "-m GTRGAMMA",
|
|
182
|
+
"GTR+F+I": "-m GTRCATI",
|
|
183
|
+
"GTR+F+G4+I": "-m GTRGAMMAI",
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def parse_model(modeltest_file, opt, path):
|
|
188
|
+
model_list = []
|
|
189
|
+
model_cmd = "no model found"
|
|
190
|
+
|
|
191
|
+
if opt.method.modeltest == "iqtree":
|
|
192
|
+
# Check iqtree - iqtree pair has misentered here
|
|
193
|
+
if opt.method.tree == "iqtree":
|
|
194
|
+
logging.error(
|
|
195
|
+
f"DEVELOPMENTAL ERROR. MODELTEST {opt.method.modeltest} entered while {opt.method.tree} selected"
|
|
196
|
+
)
|
|
197
|
+
raise Exception
|
|
198
|
+
|
|
199
|
+
# read modeltest result file
|
|
200
|
+
print(modeltest_file)
|
|
201
|
+
try:
|
|
202
|
+
with open(modeltest_file, "r", encoding="UTF-8") as f:
|
|
203
|
+
lines = f.readlines()
|
|
204
|
+
logging.info(f"Successfully parsed {modeltest_file}")
|
|
205
|
+
except:
|
|
206
|
+
logging.error(f"Cannot parse modeltest file for {modeltest_file}")
|
|
207
|
+
raise Exception
|
|
208
|
+
|
|
209
|
+
# Parse modeltest file
|
|
210
|
+
flag = 0
|
|
211
|
+
for line in lines:
|
|
212
|
+
# finish parsing from new line break
|
|
213
|
+
if line == "\n" and flag == 1:
|
|
214
|
+
break
|
|
215
|
+
# parse when modeltest result existing region
|
|
216
|
+
if flag == 1:
|
|
217
|
+
model_list.append(line.split(" ")[0])
|
|
218
|
+
|
|
219
|
+
# indicing modeltest result start
|
|
220
|
+
if (
|
|
221
|
+
"Model LogL AIC w-AIC AICc w-AICc BIC w-BIC"
|
|
222
|
+
in line
|
|
223
|
+
):
|
|
224
|
+
# parsing flag
|
|
225
|
+
flag = 1
|
|
226
|
+
|
|
227
|
+
# Designate modeltest method by tree method
|
|
228
|
+
if opt.method.tree == "fasttree":
|
|
229
|
+
for model in model_list:
|
|
230
|
+
# if model is available model for tree method take it
|
|
231
|
+
if model in cmd_translator_modelfinder_fasttree:
|
|
232
|
+
# convert to commands
|
|
233
|
+
model_cmd = cmd_translator_modelfinder_fasttree[model]
|
|
234
|
+
break
|
|
235
|
+
|
|
236
|
+
if model_cmd == "no model found":
|
|
237
|
+
logging.error(f"DEVELOPMENTAL ERROR. FAILED PARSING APPROPRIATE MODEL")
|
|
238
|
+
logging.error(f"{model_list}")
|
|
239
|
+
raise Exception
|
|
240
|
+
|
|
241
|
+
elif opt.method.tree == "raxml":
|
|
242
|
+
for model in model_list:
|
|
243
|
+
# if model is available model for tree method take it
|
|
244
|
+
if model in cmd_translator_modelfinder_raxml:
|
|
245
|
+
# convert to commands
|
|
246
|
+
model_cmd = cmd_translator_modelfinder_raxml[model]
|
|
247
|
+
break
|
|
248
|
+
|
|
249
|
+
if model_cmd == "no model found":
|
|
250
|
+
logging.error(f"DEVELOPMENTAL ERROR. FAILED PARSING APPROPRIATE MODEL")
|
|
251
|
+
logging.error(f"{model_list}")
|
|
252
|
+
raise Exception
|
|
253
|
+
|
|
254
|
+
else:
|
|
255
|
+
logging.error(
|
|
256
|
+
f"DEVELOPMENTAL ERROR. INAPPROPRIATE TREE METHOD {opt.method.tree} selected"
|
|
257
|
+
)
|
|
258
|
+
raise Exception
|
|
259
|
+
|
|
260
|
+
elif opt.method.modeltest == "modeltest-ng":
|
|
261
|
+
### Testing block ###
|
|
262
|
+
# Parse files
|
|
263
|
+
|
|
264
|
+
try:
|
|
265
|
+
with open(modeltest_file, "r", encoding="UTF-8") as f:
|
|
266
|
+
lines = f.readlines()
|
|
267
|
+
status = None
|
|
268
|
+
|
|
269
|
+
# Fasttree needs converter
|
|
270
|
+
if opt.method.tree == "fasttree":
|
|
271
|
+
flag = 0 # 1 : Criterion found, 2: --- line found 1st time(while parsing), 0 : parsing ended
|
|
272
|
+
cnt = 1 # line count
|
|
273
|
+
for line in lines:
|
|
274
|
+
print(f"flag {flag} / [line]: {line}")
|
|
275
|
+
|
|
276
|
+
if (
|
|
277
|
+
"model K lnL score delta weight"
|
|
278
|
+
in line
|
|
279
|
+
):
|
|
280
|
+
# Check if the right chunk selected
|
|
281
|
+
if "AICc" in line and opt.criterion == "AICc":
|
|
282
|
+
flag = 1
|
|
283
|
+
elif "AIC" in line and opt.criterion == "AIC":
|
|
284
|
+
flag = 1
|
|
285
|
+
elif "BIC" in line and opt.criterion == "BIC":
|
|
286
|
+
flag = 1
|
|
287
|
+
|
|
288
|
+
elif flag == 1 and "--------" in line:
|
|
289
|
+
# Skipping one line
|
|
290
|
+
flag = 2
|
|
291
|
+
elif flag == 2 and not ("------" in line):
|
|
292
|
+
logging.info(f"[line]")
|
|
293
|
+
logging.info(line)
|
|
294
|
+
# Parsing objects
|
|
295
|
+
model_list.append(line.split(f"{cnt} ")[1].split(" ")[0])
|
|
296
|
+
if cnt < 10:
|
|
297
|
+
cnt += 1
|
|
298
|
+
else:
|
|
299
|
+
cnt = 1
|
|
300
|
+
# Finish all 10 parsed
|
|
301
|
+
break
|
|
302
|
+
elif flag == 2 and "------" in line:
|
|
303
|
+
# reset block
|
|
304
|
+
flag = 0
|
|
305
|
+
|
|
306
|
+
# get commands with available model
|
|
307
|
+
for model in model_list:
|
|
308
|
+
if model in cmd_translator_modeltestng_fasttree:
|
|
309
|
+
model_cmd = cmd_translator_modeltestng_fasttree[model]
|
|
310
|
+
break
|
|
311
|
+
|
|
312
|
+
# For IQTREE and RAxML, just easily parse commands
|
|
313
|
+
else:
|
|
314
|
+
for line in lines:
|
|
315
|
+
# Check current criterion
|
|
316
|
+
if "AICc" in line:
|
|
317
|
+
status = "AICc"
|
|
318
|
+
elif "AIC" in line and not "AICc" in line:
|
|
319
|
+
status = "AIC"
|
|
320
|
+
elif "BIC" in line:
|
|
321
|
+
status = "BIC"
|
|
322
|
+
|
|
323
|
+
if status == opt.criterion:
|
|
324
|
+
if line.strip().startswith(">"):
|
|
325
|
+
if opt.method.tree == "fasttree":
|
|
326
|
+
logging.error(
|
|
327
|
+
f"DEVELOPMENTAL ERROR. MODELTEST-NG COMMAND PARSING STEP entered while {opt.method.tree} selected"
|
|
328
|
+
)
|
|
329
|
+
elif opt.method.tree == "iqtree":
|
|
330
|
+
if "iqtree" in line:
|
|
331
|
+
model_cmd = f'-m {line.split("-m ")[1]}'
|
|
332
|
+
break
|
|
333
|
+
elif opt.method.tree == "raxml":
|
|
334
|
+
if "raxmlHPC-SSE3" in line:
|
|
335
|
+
model_cmd = (
|
|
336
|
+
f'-m {line.split("-m ")[1].split(" -n ")[0]}'
|
|
337
|
+
)
|
|
338
|
+
break
|
|
339
|
+
|
|
340
|
+
# Currently using BIC model
|
|
341
|
+
model_cmd = model_dict["BIC"]
|
|
342
|
+
|
|
343
|
+
except:
|
|
344
|
+
logging.warning(f"Cannot parse modeltest file for {modeltest_file}")
|
|
345
|
+
|
|
346
|
+
else:
|
|
347
|
+
logging.error(
|
|
348
|
+
f"DEVELOPMENTAL ERROR. INAPPROPRIATE MODELTEST METHOD {opt.method.modeltest} selected"
|
|
349
|
+
)
|
|
350
|
+
raise Exception
|
|
351
|
+
|
|
352
|
+
return model_cmd
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def cleanup(modeltest_file):
|
|
356
|
+
pass
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
# main function
|
|
360
|
+
def modeltest(V, path, opt) -> dict:
|
|
361
|
+
group_dict = V.dict_dataset
|
|
362
|
+
model_dict = copy.deepcopy(group_dict)
|
|
363
|
+
|
|
364
|
+
# By group and by gene
|
|
365
|
+
for group in group_dict:
|
|
366
|
+
for gene in group_dict[group]:
|
|
367
|
+
if opt.method.modeltest.lower() == "modeltest-ng":
|
|
368
|
+
# As modeltest-ng shows only top 10 results,
|
|
369
|
+
# we should reduce number of models to prevent none of the model fits
|
|
370
|
+
if opt.method.tree == "fasttree":
|
|
371
|
+
models = "-m JC,GTR -h ug"
|
|
372
|
+
else:
|
|
373
|
+
models = ""
|
|
374
|
+
|
|
375
|
+
# run modeltest-ng
|
|
376
|
+
ext.Modeltest_ng(
|
|
377
|
+
fasta=f"{path.out_alignment}/{opt.runname}_trimmed_{group}_{gene}.fasta",
|
|
378
|
+
models=models,
|
|
379
|
+
out=f"{path.out_modeltest}/{opt.runname}_{group}_{gene}",
|
|
380
|
+
thread=opt.thread,
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
# Change result name
|
|
384
|
+
try:
|
|
385
|
+
shutil.move(
|
|
386
|
+
f"{path.out_modeltest}/{opt.runname}_{group}_{gene}.out",
|
|
387
|
+
f"{path.out_modeltest}/{opt.runname}_{group}_{gene}.modeltest",
|
|
388
|
+
)
|
|
389
|
+
except:
|
|
390
|
+
logging.warning(
|
|
391
|
+
f"Cannot parse modeltest file for {path.out_modeltest}/{opt.runname}_{group}_{gene}.out. Running with default option"
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
# parse model from modeltest-ng result
|
|
395
|
+
model_dict[group][gene] = parse_model(
|
|
396
|
+
modeltest_file=f"{path.out_modeltest}/{opt.runname}_{group}_{gene}.modeltest",
|
|
397
|
+
path=path,
|
|
398
|
+
opt=opt,
|
|
399
|
+
)
|
|
400
|
+
elif opt.method.modeltest.lower() == "iqtree":
|
|
401
|
+
if not (opt.method.tree == "iqtree"):
|
|
402
|
+
# Run IQTREE ModelFinder
|
|
403
|
+
ext.ModelFinder(
|
|
404
|
+
fasta=f"{path.out_alignment}/{opt.runname}_trimmed_{group}_{gene}.fasta",
|
|
405
|
+
opt=opt,
|
|
406
|
+
path=path,
|
|
407
|
+
thread=opt.thread,
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
# Move modeltest result to appropriate location
|
|
411
|
+
try:
|
|
412
|
+
shutil.move(
|
|
413
|
+
f"{path.out_alignment}/{opt.runname}_trimmed_{group}_{gene}.fasta.iqtree",
|
|
414
|
+
f"{path.out_modeltest}/{opt.runname}_{group}_{gene}.modeltest",
|
|
415
|
+
)
|
|
416
|
+
except:
|
|
417
|
+
logging.warning(
|
|
418
|
+
f"Cannot parse modeltest file for {path.out_alignment}/{opt.runname}_trimmed_{group}_{gene}.fasta.iqtree. Running with default option"
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
# parse model from IQTREE ModelFinder result
|
|
422
|
+
model_dict[group][gene] = parse_model(
|
|
423
|
+
modeltest_file=f"{path.out_modeltest}/{opt.runname}_{group}_{gene}.modeltest",
|
|
424
|
+
path=path,
|
|
425
|
+
opt=opt,
|
|
426
|
+
)
|
|
427
|
+
else:
|
|
428
|
+
logging.info(
|
|
429
|
+
"IQTREE will perform ModelFinder internally in tree construction step, skipping in modeltest step"
|
|
430
|
+
)
|
|
431
|
+
model_dict[group][gene] = "skip"
|
|
432
|
+
|
|
433
|
+
else: # including opt.model_method.lower() == "none":
|
|
434
|
+
if opt.method.tree == "raxml":
|
|
435
|
+
logging.info("Skipping modeltest. Using GTRGAMMA as default")
|
|
436
|
+
model_dict[group][gene] = "-m GTRGAMMA"
|
|
437
|
+
else:
|
|
438
|
+
logging.info("Skipping modeltest.")
|
|
439
|
+
model_dict[group][gene] = "skip"
|
|
440
|
+
|
|
441
|
+
logging.info(model_dict)
|
|
442
|
+
|
|
443
|
+
return model_dict
|
src/ncbi.py
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
from Bio import Entrez
|
|
2
|
+
from Bio import SeqIO
|
|
3
|
+
from Bio.Blast.Applications import NcbiblastnCommandline
|
|
4
|
+
from Bio.Blast import NCBIXML
|
|
5
|
+
from Bio.Seq import Seq
|
|
6
|
+
import time
|
|
7
|
+
from time import sleep
|
|
8
|
+
import pickle
|
|
9
|
+
import json
|
|
10
|
+
import re
|
|
11
|
+
import logging
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
from itertools import repeat
|
|
14
|
+
import xmltodict
|
|
15
|
+
import os, sys, subprocess
|
|
16
|
+
import random
|
|
17
|
+
import pickle
|
|
18
|
+
import pandas as pd
|
|
19
|
+
import shutil
|
|
20
|
+
|
|
21
|
+
# from .logger import Mes
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def Downloadbyacclist(Email, list_ID, out):
|
|
25
|
+
if len(list_ID) == 0:
|
|
26
|
+
return []
|
|
27
|
+
|
|
28
|
+
path_tmp = "tmp_download"
|
|
29
|
+
|
|
30
|
+
# print(list_ID)
|
|
31
|
+
|
|
32
|
+
def xml2dict(record):
|
|
33
|
+
dict_type = xmltodict.parse(record)
|
|
34
|
+
json_type = json.dumps(dict_type, indent=4)
|
|
35
|
+
dict2_type = json.loads(json_type)
|
|
36
|
+
return dict2_type
|
|
37
|
+
|
|
38
|
+
Entrez.email = Email
|
|
39
|
+
|
|
40
|
+
logging.info(f"Number of IDs: {len(list_ID)}")
|
|
41
|
+
|
|
42
|
+
# Parse
|
|
43
|
+
cnt = 0
|
|
44
|
+
cut = 50 # parse by 50 sequences
|
|
45
|
+
cnt_all = len(list_ID)
|
|
46
|
+
|
|
47
|
+
start_time = time.time()
|
|
48
|
+
|
|
49
|
+
record_list = []
|
|
50
|
+
|
|
51
|
+
for i in range(int((len(list_ID) - 1) / cut) + 1):
|
|
52
|
+
# last chunk
|
|
53
|
+
if i * cut + cut > len(list_ID):
|
|
54
|
+
# Mes(i)
|
|
55
|
+
ID_string = ",".join(list_ID[i * cut :])
|
|
56
|
+
sleep(0.3)
|
|
57
|
+
cnt += len(list_ID[i * cut :])
|
|
58
|
+
logging.info(
|
|
59
|
+
f"{cnt}/{cnt_all} {100*cnt/cnt_all}% {time.time()-start_time}s"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
print(ID_string)
|
|
64
|
+
handle = Entrez.efetch(
|
|
65
|
+
db="nucleotide", id=ID_string, rettype="gb", retmode="xml"
|
|
66
|
+
)
|
|
67
|
+
except: # Retry
|
|
68
|
+
logging.info("Requesting again...")
|
|
69
|
+
try:
|
|
70
|
+
sleep(10)
|
|
71
|
+
handle = Entrez.efetch(
|
|
72
|
+
db="nucleotide", id=ID_string, rettype="gb", retmode="xml"
|
|
73
|
+
)
|
|
74
|
+
except:
|
|
75
|
+
sleep(100)
|
|
76
|
+
handle = Entrez.efetch(
|
|
77
|
+
db="nucleotide", id=ID_string, rettype="gb", retmode="xml"
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
pre_record = handle.read()
|
|
81
|
+
json_record = xml2dict(pre_record)
|
|
82
|
+
tmp_record_list = []
|
|
83
|
+
|
|
84
|
+
# If only one result available
|
|
85
|
+
if type(json_record["GBSet"]["GBSeq"]) is dict:
|
|
86
|
+
json_record["GBSet"]["GBSeq"] = [json_record["GBSet"]["GBSeq"]]
|
|
87
|
+
|
|
88
|
+
if len(list_ID[i * cut :]) != 1:
|
|
89
|
+
for record in json_record["GBSet"]["GBSeq"]:
|
|
90
|
+
print(record)
|
|
91
|
+
record_list.append(record)
|
|
92
|
+
tmp_record_list.append(record)
|
|
93
|
+
else:
|
|
94
|
+
record = json_record["GBSet"]["GBSeq"]
|
|
95
|
+
record_list.append(record)
|
|
96
|
+
tmp_record_list.append(record)
|
|
97
|
+
|
|
98
|
+
try:
|
|
99
|
+
with open(f"./{path_tmp}/{i}", "wb") as f:
|
|
100
|
+
pickle.dump(tmp_record_list, f)
|
|
101
|
+
except:
|
|
102
|
+
logging.error("Saving Error")
|
|
103
|
+
raise Exception
|
|
104
|
+
|
|
105
|
+
# non last chunk
|
|
106
|
+
else:
|
|
107
|
+
# Mes(i)
|
|
108
|
+
ID_string = ",".join(list_ID[i * cut : i * cut + cut])
|
|
109
|
+
sleep(0.3)
|
|
110
|
+
cnt += cut
|
|
111
|
+
logging.info(
|
|
112
|
+
f"{cnt}/{cnt_all} {100*cnt/cnt_all}% {time.time()-start_time}s"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
try:
|
|
116
|
+
handle = Entrez.efetch(
|
|
117
|
+
db="nucleotide", id=ID_string, rettype="gb", retmode="xml"
|
|
118
|
+
)
|
|
119
|
+
except: # Retry
|
|
120
|
+
logging.info("Requesting again...")
|
|
121
|
+
try:
|
|
122
|
+
sleep(10)
|
|
123
|
+
handle = Entrez.efetch(
|
|
124
|
+
db="nucleotide", id=ID_string, rettype="gb", retmode="xml"
|
|
125
|
+
)
|
|
126
|
+
except:
|
|
127
|
+
sleep(100)
|
|
128
|
+
handle = Entrez.efetch(
|
|
129
|
+
db="nucleotide", id=ID_string, rettype="gb", retmode="xml"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
pre_record = handle.read()
|
|
133
|
+
json_record = xml2dict(pre_record)
|
|
134
|
+
tmp_record_list = []
|
|
135
|
+
|
|
136
|
+
if len(list_ID[i * cut :]) != 1:
|
|
137
|
+
for record in json_record["GBSet"]["GBSeq"]:
|
|
138
|
+
record_list.append(record)
|
|
139
|
+
tmp_record_list.append(record)
|
|
140
|
+
else:
|
|
141
|
+
record = json_record["GBSet"]["GBSeq"]
|
|
142
|
+
record_list.append(record)
|
|
143
|
+
tmp_record_list.append(record)
|
|
144
|
+
|
|
145
|
+
try:
|
|
146
|
+
with open(f"./{path_tmp}/{i}", "wb") as f:
|
|
147
|
+
pickle.dump(tmp_record_list, f)
|
|
148
|
+
except:
|
|
149
|
+
logging.error("Saving Error")
|
|
150
|
+
raise Exception
|
|
151
|
+
|
|
152
|
+
with open(out, "w") as fp:
|
|
153
|
+
json_term = json.dump(record_list, fp, indent=4)
|
|
154
|
+
|
|
155
|
+
# If success, remove tmp files
|
|
156
|
+
tmp_file_list = [file for file in os.listdir(f"./{path_tmp}/")]
|
|
157
|
+
for file in tmp_file_list:
|
|
158
|
+
os.remove(f"./{path_tmp}/{file}")
|
|
159
|
+
|
|
160
|
+
return record_list
|
src/opt_generator.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Option generator for multiprocessing starmap
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def opt_generator(V, opt, path, step, thread=None):
|
|
7
|
+
list_opt = []
|
|
8
|
+
|
|
9
|
+
if thread is None:
|
|
10
|
+
thread = opt.thread
|
|
11
|
+
|
|
12
|
+
## alignment options generator
|
|
13
|
+
if step == "alignment":
|
|
14
|
+
for group in V.dict_dataset:
|
|
15
|
+
for gene in V.dict_dataset[group]:
|
|
16
|
+
# For concatenated gene matrix, alignment should be done by each gene matrix and then concatenated
|
|
17
|
+
if gene != "concatenated":
|
|
18
|
+
# double checking path
|
|
19
|
+
if os.path.isfile(
|
|
20
|
+
f"{path.out_adjusted}/{opt.runname}_Adjusted_{group}_{gene}.fasta"
|
|
21
|
+
):
|
|
22
|
+
list_opt.append(
|
|
23
|
+
(
|
|
24
|
+
f"{path.out_adjusted}/{opt.runname}_Adjusted_{group}_{gene}.fasta",
|
|
25
|
+
f"{path.out_alignment}/{opt.runname}_MAFFT_{group}_{gene}.fasta",
|
|
26
|
+
path,
|
|
27
|
+
thread,
|
|
28
|
+
opt.mafft.algorithm,
|
|
29
|
+
"adjustdirection",
|
|
30
|
+
opt.mafft.op,
|
|
31
|
+
opt.mafft.ep,
|
|
32
|
+
)
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
elif step == "trimming":
|
|
36
|
+
# Generate trimming opts for multiprocessing
|
|
37
|
+
for group in V.dict_dataset:
|
|
38
|
+
for gene in V.dict_dataset[group]:
|
|
39
|
+
if not (gene == "concatenated"):
|
|
40
|
+
if opt.method.trim.lower() == "gblocks":
|
|
41
|
+
list_opt.append(
|
|
42
|
+
(
|
|
43
|
+
f"{path.out_alignment}/{opt.runname}_MAFFT_{group}_{gene}.fasta",
|
|
44
|
+
f"{path.out_alignment}/{opt.runname}_trimmed_{group}_{gene}.fasta",
|
|
45
|
+
path,
|
|
46
|
+
opt,
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
elif opt.method.trim.lower() == "trimal":
|
|
50
|
+
list_opt.append(
|
|
51
|
+
(
|
|
52
|
+
f"{path.out_alignment}/{opt.runname}_MAFFT_{group}_{gene}.fasta",
|
|
53
|
+
f"{path.out_alignment}/{opt.runname}_trimmed_{group}_{gene}.fasta",
|
|
54
|
+
path,
|
|
55
|
+
opt,
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
else: # for just copy
|
|
59
|
+
list_opt.append(
|
|
60
|
+
(
|
|
61
|
+
f"{path.out_alignment}/{opt.runname}_MAFFT_{group}_{gene}.fasta",
|
|
62
|
+
f"{path.out_alignment}/{opt.runname}_trimmed_{group}_{gene}.fasta",
|
|
63
|
+
path,
|
|
64
|
+
opt,
|
|
65
|
+
)
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
else:
|
|
69
|
+
logging.error(f"[Error] Unexpected step {step} given for opt_generator")
|
|
70
|
+
raise Exception
|
|
71
|
+
|
|
72
|
+
return list_opt
|