seqCompare 1.0.0__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.
@@ -0,0 +1,1596 @@
1
+ #!/usr/bin/env python
2
+
3
+ import os
4
+ import sys
5
+ from Bio import SeqIO
6
+ import multiprocessing
7
+ import argparse
8
+ import itertools
9
+ from progress.bar import ChargingBar
10
+ import re
11
+ import subprocess
12
+ from pathlib import Path
13
+ import numpy as np
14
+ import shutil
15
+ import pandas as pd
16
+ import networkx as nx
17
+ import math
18
+ import datetime
19
+ import statistics
20
+ import glob
21
+ from .__init__ import __version__
22
+
23
+ def pairwiseSimilarity(firstRecord,secondRecord):
24
+ recordMatch = 0
25
+ recordMisMatch = 0
26
+
27
+ firstRecordName = list(firstRecord.keys())[0]
28
+ secondRecordName = list(secondRecord.keys())[0]
29
+
30
+ for r,s in zip(firstRecord[firstRecordName],secondRecord[secondRecordName]):
31
+ if not (r == 0 and s == 0):
32
+ if r == s:
33
+ if (r in [0,1] and s in [0,1]):
34
+ recordMatch = recordMatch + 1
35
+ else:
36
+ print(f"--->>>Found characters other than 0 and 1 when comparing items {firstRecordName} and {secondRecordName}... exiting...")
37
+ sys.exit()
38
+ else:
39
+ if (r in [0,1] and s in [0,1]):
40
+ recordMisMatch = recordMisMatch + 1
41
+ else:
42
+ print(f"--->>>Found characters other than 0 and 1 when comparing items {firstRecordName} and {secondRecordName}... exiting...")
43
+ sys.exit()
44
+ else:
45
+ if (r in [0,1] and s in [0,1]):
46
+ pass
47
+ else:
48
+ print(f"--->>>Found characters other than 0 and 1 when comparing items {firstRecordName} and {secondRecordName}... exiting...")
49
+ sys.exit()
50
+
51
+ percentSimilarity = recordMatch/(recordMatch + recordMisMatch)*100
52
+
53
+ similarityData = [firstRecordName,secondRecordName,percentSimilarity]
54
+
55
+ #print('.', end='', flush=True)
56
+
57
+ return(similarityData)
58
+
59
+
60
+ def alignmentSimilarity(i,j):
61
+ seqMatch=0
62
+ seqMisMatch=0
63
+
64
+ for r,s in zip(str(i.seq).replace("-","N").replace("X","N").upper(),str(j.seq).replace("-","N").replace("X","N").upper()):
65
+ if r==s and not (r=="N" or s=="N"):
66
+ seqMatch=seqMatch+1
67
+ else:
68
+ if r=="N" or s=="N":
69
+ continue
70
+ else:
71
+ seqMisMatch=seqMisMatch+1
72
+
73
+ seqPID=round(seqMatch/(seqMatch+seqMisMatch)*100,4) if (seqMatch+seqMisMatch)!=0 else 0
74
+
75
+ seqLen=round(len(i.seq),4)
76
+
77
+ seqCov=round((seqMatch+seqMisMatch)/seqLen*100,4) if seqLen!=0 else 0
78
+
79
+ retValue = f"{i.id}\t{j.id}\t{seqPID}"
80
+
81
+ return(retValue)
82
+
83
+
84
+ def mashSimilarity(cmdValues):
85
+ if not shutil.which("mash"):
86
+ print("Install MASH and try again... exiting...")
87
+ sys.exit()
88
+
89
+ else:
90
+ pass
91
+
92
+ if int(cmdValues["length"])>=16:
93
+ print("Note: use shorter k-mers ≤16 when working diverse sequences")
94
+ else:
95
+ pass
96
+
97
+ tmpOut = f"mash.{str(datetime.datetime.now()).replace(" ","").replace("-",".").replace(":",".")}"
98
+ tmpOutR = f"mash.{str(datetime.datetime.now()).replace(" ","").replace("-",".").replace(":",".")}"
99
+
100
+ print("Calculating percent similarity... may take longer... be patient...")
101
+
102
+ mashSketchCmd = f"mash sketch -p {cmdValues["threads"]} -k {cmdValues["length"]} -o {tmpOut} -l {cmdValues["data"]} -s {cmdValues["size"]}"
103
+ mashDistCmd = f"mash dist -p {cmdValues["threads"]} -s {cmdValues["size"]} {f"{tmpOut}.msh"} {f"{tmpOut}.msh"} > {tmpOutR}"
104
+
105
+ subprocess.call(mashSketchCmd,shell=True,stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
106
+ subprocess.call(mashDistCmd,shell=True,stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
107
+
108
+ mashResults = []
109
+
110
+ with open(tmpOutR,"r") as mashFile:
111
+ for mashOutput in mashFile:
112
+ item = mashOutput.strip().replace("/","\t").split("\t")
113
+ mashResults.append(f"{Path(item[0]).stem}\t{Path(item[1]).stem}\t{float(item[4])/float(item[5])*100}")
114
+
115
+ if os.path.exists(tmpOut):
116
+ os.remove(tmpOut)
117
+ else:
118
+ pass
119
+
120
+ if os.path.exists(tmpOutR):
121
+ os.remove(tmpOutR)
122
+ else:
123
+ pass
124
+
125
+ if os.path.exists(f"{tmpOut}.msh"):
126
+ os.remove(f"{tmpOut}.msh")
127
+ else:
128
+ pass
129
+
130
+ return(mashResults)
131
+
132
+
133
+ def blastANISimilarity(cmdValues):
134
+ if not shutil.which("blastn"):
135
+ print("Install BLAST and try again... exiting...")
136
+ sys.exit()
137
+
138
+ else:
139
+ pass
140
+
141
+ tmpFileNames = []
142
+ fileNames = []
143
+
144
+ tmpCmd="wc -l "+str(cmdValues["data"])+" | awk '{print $1}'"
145
+ dataFileLines=subprocess.Popen(tmpCmd,shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
146
+ dataLines = int(dataFileLines.communicate()[0])
147
+ dataFileLines.kill()
148
+
149
+ bar = ChargingBar("Loading data", max=int(dataLines))
150
+ for fileName in open(cmdValues["data"],"r"):
151
+ fileNames.append(str(fileName).strip())
152
+
153
+ bar.next()
154
+ bar.finish()
155
+
156
+ for i in fileNames:
157
+ tmpFileNames.append(f"{Path(i).stem}.tmpfna")
158
+
159
+ with open(f"{Path(i).stem}.tmpfna","w") as fh:
160
+ for j in SeqIO.parse(i,"fasta"):
161
+ fh.write(f">{Path(os.path.basename(i)).stem}\n{j.seq}\n")
162
+
163
+ tmpOut = f"blast.{str(datetime.datetime.now()).replace(" ","").replace("-",".").replace(":",".")}"
164
+
165
+ bar = ChargingBar("Creating database", max=len(tmpFileNames))
166
+ with open(tmpOut,"w") as tmpOutFile:
167
+ for fileName in tmpFileNames:
168
+ for seq in SeqIO.parse(fileName,"fasta"):
169
+ tmpOutFile.write(f">{seq.id}\n{seq.seq}\n")
170
+ bar.next()
171
+ bar.finish()
172
+
173
+ Cmd2 = f"makeblastdb -in {tmpOut} -dbtype nucl"
174
+
175
+ subprocess.call(Cmd2,shell=True,stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
176
+
177
+ print("Calculating percent similarity... may take longer... be patient...")
178
+
179
+ tmpOut1 = f"blast.{str(datetime.datetime.now()).replace(" ","").replace("-",".").replace(":",".")}"
180
+ tmpOut2 = f"blast.{str(datetime.datetime.now()).replace(" ","").replace("-",".").replace(":",".")}"
181
+
182
+ blastCmd1 = f"echo \'qseqid sseqid length pident qcovs\' | tr \' \' \'\\t\' > {tmpOut2}"
183
+ blastCmd2 = f"blastn -db {tmpOut} -query {tmpOut} -word_size {cmdValues["length"]} -num_threads {cmdValues["threads"]} -outfmt \'6 qseqid sseqid length pident qcovs\' -evalue {cmdValues["evalue"]} >> {tmpOut2}"
184
+
185
+ subprocess.call(blastCmd1,shell=True,stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
186
+ subprocess.call(blastCmd2,shell=True,stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
187
+
188
+ blastData = pd.read_csv(tmpOut2,delimiter="\t",dtype={'pident': float,'qcovs': float, 'length': int})
189
+ blastData = blastData[blastData["qcovs"] >= (cmdValues["fraction"]*100)]
190
+
191
+ blastData["tmp"] = blastData.apply(lambda x: (x["length"] * x["pident"]),axis=1)
192
+
193
+ blastData = blastData[["qseqid","sseqid","tmp","length","qcovs"]].groupby(['qseqid','sseqid']).agg({'tmp':'sum','length':'sum','qcovs':'mean'}).reset_index()
194
+ blastData["tmp1"] = blastData.apply(lambda x: ((x["tmp"] / x["length"]) if x["length"]>0 else 0 ),axis=1)
195
+
196
+ blastData["qseqidR"] = blastData.apply(lambda x: x["qseqid"] if x["qseqid"] > x["sseqid"] else x["sseqid"],axis=1)
197
+ blastData["sseqidR"] = blastData.apply(lambda x: x["sseqid"] if x["qseqid"] > x["sseqid"] else x["qseqid"],axis=1)
198
+
199
+ blastData["qseqid"] = blastData["qseqidR"]
200
+ blastData["sseqid"] = blastData["sseqidR"]
201
+
202
+ blastDataR = blastData[["qseqid","sseqid","tmp1"]].groupby(['qseqid','sseqid']).agg({'tmp1':'min'}).reset_index()
203
+
204
+ blastDataFinal = blastDataR[["sseqid","qseqid","tmp1"]].rename(columns={"sseqid":"seq1","qseqid":"seq2","tmp1":"pid"}).values.tolist()
205
+
206
+ bar = ChargingBar("Cleaning temporary files", max=len(fileNames))
207
+ for fileName in tmpFileNames:
208
+ if os.path.exists(fileName):
209
+ os.remove(fileName)
210
+ else:
211
+ pass
212
+
213
+ bar.next()
214
+ bar.finish()
215
+
216
+ if os.path.exists(tmpOut1):
217
+ os.remove(tmpOut1)
218
+ else:
219
+ pass
220
+
221
+ if os.path.exists(tmpOut2):
222
+ os.remove(tmpOut2)
223
+ else:
224
+ pass
225
+
226
+ for file in glob.glob(f"{tmpOut}*"):
227
+ if os.path.exists(file):
228
+ os.remove(file)
229
+ else:
230
+ pass
231
+
232
+ return(blastDataFinal)
233
+
234
+
235
+ def blastSimilarity(cmdValues):
236
+ if not shutil.which("blastn"):
237
+ print("Install BLAST and try again... exiting...")
238
+ sys.exit()
239
+
240
+ else:
241
+ pass
242
+
243
+ tmpFileNames = []
244
+ fileNames = []
245
+
246
+ tmpCmd="wc -l "+str(cmdValues["data"])+" | awk '{print $1}'"
247
+ dataFileLines=subprocess.Popen(tmpCmd,shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
248
+ dataLines = int(dataFileLines.communicate()[0])
249
+ dataFileLines.kill()
250
+
251
+ bar = ChargingBar("Loading data", max=int(dataLines))
252
+ for fileName in open(cmdValues["data"],"r"):
253
+ fileNames.append(str(fileName).strip())
254
+
255
+ bar.next()
256
+ bar.finish()
257
+
258
+ for i in fileNames:
259
+ tmpFileNames.append(f"{Path(i).stem}.tmpfna")
260
+
261
+ with open(f"{Path(i).stem}.tmpfna","w") as fh:
262
+ for j in SeqIO.parse(i,"fasta"):
263
+ fh.write(f">{Path(os.path.basename(i)).stem}\n{j.seq}\n")
264
+
265
+ tmpOut = f"blast.{str(datetime.datetime.now()).replace(" ","").replace("-",".").replace(":",".")}"
266
+
267
+ bar = ChargingBar("Creating database", max=len(tmpFileNames))
268
+ with open(tmpOut,"w") as tmpOutFile:
269
+ for fileName in tmpFileNames:
270
+ for seq in SeqIO.parse(fileName,"fasta"):
271
+ tmpOutFile.write(f">{seq.id}\n{seq.seq}\n")
272
+ bar.next()
273
+ bar.finish()
274
+
275
+ Cmd2 = f"makeblastdb -in {tmpOut} -dbtype nucl"
276
+
277
+ subprocess.call(Cmd2,shell=True,stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
278
+
279
+ print("Calculating percent similarity... may take longer... be patient...")
280
+
281
+ tmpOut1 = f"blast.{str(datetime.datetime.now()).replace(" ","").replace("-",".").replace(":",".")}"
282
+ tmpOut2 = f"blast.{str(datetime.datetime.now()).replace(" ","").replace("-",".").replace(":",".")}"
283
+
284
+ blastCmd1 = f"echo \'qseqid sseqid length pident qcovs\' | tr \' \' \'\\t\' > {tmpOut2}"
285
+ blastCmd2 = f"blastn -db {tmpOut} -query {tmpOut} -word_size {cmdValues["length"]} -num_threads {cmdValues["threads"]} -outfmt \'6 qseqid sseqid length pident qcovs\' -evalue {cmdValues["evalue"]} >> {tmpOut2}"
286
+
287
+ subprocess.call(blastCmd1,shell=True,stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
288
+ subprocess.call(blastCmd2,shell=True,stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
289
+
290
+ blastData = pd.read_csv(tmpOut2,delimiter="\t",dtype={'pident': float,'qcovs': float, 'length': int})
291
+ blastData["tmp"] = blastData.apply(lambda x: (x["length"] * x["pident"]),axis=1)
292
+
293
+ blastData = blastData[["qseqid","sseqid","tmp","length","qcovs"]].groupby(['qseqid','sseqid']).agg({'tmp':'sum','length':'sum','qcovs':'mean'}).reset_index()
294
+
295
+ blastData["tmp1"] = blastData.apply(lambda x: ((x["tmp"] / x["length"]) * (x["qcovs"] / 100)),axis=1)
296
+
297
+ blastData["qseqidR"] = blastData.apply(lambda x: x["qseqid"] if x["qseqid"] > x["sseqid"] else x["sseqid"],axis=1)
298
+ blastData["sseqidR"] = blastData.apply(lambda x: x["sseqid"] if x["qseqid"] > x["sseqid"] else x["qseqid"],axis=1)
299
+
300
+ blastData["qseqid"] = blastData["qseqidR"]
301
+ blastData["sseqid"] = blastData["sseqidR"]
302
+
303
+ blastDataR = blastData[["qseqid","sseqid","tmp1"]].groupby(['qseqid','sseqid']).agg({'tmp1':'min'}).reset_index()
304
+
305
+ blastDataFinal = blastDataR[["sseqid","qseqid","tmp1"]].rename(columns={"sseqid":"seq1","qseqid":"seq2","tmp1":"pid"}).values.tolist()
306
+
307
+ bar = ChargingBar("Cleaning temporary files", max=len(fileNames))
308
+ for fileName in tmpFileNames:
309
+ if os.path.exists(fileName):
310
+ os.remove(fileName)
311
+ else:
312
+ pass
313
+
314
+ bar.next()
315
+ bar.finish()
316
+
317
+ if os.path.exists(tmpOut1):
318
+ os.remove(tmpOut1)
319
+ else:
320
+ pass
321
+
322
+ if os.path.exists(tmpOut2):
323
+ os.remove(tmpOut2)
324
+ else:
325
+ pass
326
+
327
+ for file in glob.glob(f"{tmpOut}*"):
328
+ if os.path.exists(file):
329
+ os.remove(file)
330
+ else:
331
+ pass
332
+
333
+ return(blastDataFinal)
334
+
335
+
336
+ def fastaniSimilarity(cmdValues):
337
+ if not shutil.which("fastANI"):
338
+ print("Install fastANI and try again... exiting...")
339
+ sys.exit()
340
+
341
+ else:
342
+ pass
343
+
344
+ if int(cmdValues["length"])>=16:
345
+ print("Note: use shorter k-mers ≤16 when working diverse sequences")
346
+ else:
347
+ pass
348
+
349
+ tmpOut = f"mash.{str(datetime.datetime.now()).replace(" ","").replace("-",".").replace(":",".")}"
350
+
351
+ fastaniCmd = f"fastANI --rl {cmdValues["data"]} --ql {cmdValues["data"]} --kmer {cmdValues["length"]} --matrix --fragLen {cmdValues["fragment"]} --minFraction {cmdValues["fraction"]} --output {tmpOut} --threads {cmdValues["threads"]}"
352
+
353
+ print("Calculating percent similarity... may take longer... be patient...")
354
+
355
+ subprocess.call(fastaniCmd,shell=True,stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
356
+
357
+ fastaniResults = []
358
+
359
+ with open(tmpOut,"r") as fastaniFile:
360
+ for itemPos,fastaniOutput in enumerate(fastaniFile):
361
+ item = fastaniOutput.strip().split("\t")
362
+ fastaniResults.append([Path(item[0]).stem,Path(item[1]).stem,float(item[2])])
363
+
364
+ fastaniData = pd.DataFrame(fastaniResults, columns=["seq1R","seq2R","pidR"])
365
+ fastaniData['pidR'] = fastaniData['pidR'].astype(float)
366
+
367
+ fastaniData["seq1RR"] = fastaniData.apply(lambda x: x["seq1R"] if x["seq1R"] > x["seq2R"] else x["seq2R"],axis=1)
368
+ fastaniData["seq2RR"] = fastaniData.apply(lambda x: x["seq2R"] if x["seq1R"] > x["seq2R"] else x["seq1R"],axis=1)
369
+
370
+ fastaniData = fastaniData[["seq1RR","seq2RR","pidR"]].groupby(['seq1RR','seq2RR']).agg({'pidR':min})
371
+ fastaniDataR = fastaniData.reset_index()
372
+ fastaniDataFinal = fastaniDataR[["seq1RR","seq2RR","pidR"]].rename(columns={"seq1RR":"seq1","seq2RR":"seq2","pid":"pidR"}).values.tolist()
373
+
374
+ if os.path.exists(tmpOut):
375
+ os.remove(tmpOut)
376
+ else:
377
+ pass
378
+
379
+ if os.path.exists(f"{tmpOut}.matrix"):
380
+ os.remove(f"{tmpOut}.matrix")
381
+ else:
382
+ pass
383
+
384
+ return(fastaniDataFinal)
385
+
386
+
387
+ def similarity(cmdValues):
388
+
389
+ if sum([1 if cmdValues["blastani"] else 0, 1 if cmdValues["blast"] else 0, 1 if cmdValues["aln"] else 0, 1 if cmdValues["mash"] else 0, 1 if cmdValues["fastani"] else 0]) > 1:
390
+ print("Specify none or only one of these options --aln/-a, --mash/-m, and --fastani/-n... exiting...")
391
+ sys.exit()
392
+ else:
393
+ pass
394
+
395
+ if (not cmdValues["blastani"]) and (not cmdValues["blast"]) and (not cmdValues["aln"]) and (not cmdValues["mash"]) and (not cmdValues["fastani"]):
396
+ dataMatrixDF = loadDataTable(cmdValues)
397
+
398
+ itemPairs = itertools.permutations([item for pos,item in enumerate(dataMatrixDF.keys()) if pos>0], r=2)
399
+ tmpPairs = itertools.permutations([item for pos,item in enumerate(dataMatrixDF.keys()) if pos>0], r=2)
400
+
401
+ print("Calculating pairwise similarity... may take longer... be patient...")
402
+
403
+ with multiprocessing.Pool(processes=cmdValues["threads"]) as pool:
404
+ args = [({firstItem: dataMatrixDF[firstItem]}, {secondItem: dataMatrixDF[secondItem]}) for firstItem,secondItem in itemPairs]
405
+ similarityResults = pool.starmap(pairwiseSimilarity, args)
406
+
407
+ with open(cmdValues["output"],"w") as simData:
408
+ simData.write("seq1\tseq2\tpid\n")
409
+
410
+ bar = ChargingBar('Writing similarity values', max=len(similarityResults))
411
+ for itemPair in similarityResults:
412
+ simData.write(f"{itemPair[0]}\t{itemPair[1]}\t{itemPair[2]}\n")
413
+
414
+ bar.next()
415
+ bar.finish()
416
+
417
+ elif (not cmdValues["blastani"]) and (not cmdValues["blast"]) and (not cmdValues["aln"]) and (cmdValues["mash"]) and (not cmdValues["fastani"]):
418
+ similarityResults = mashSimilarity(cmdValues)
419
+
420
+ with open(cmdValues["output"],"w") as simData:
421
+ simData.write("seq1\tseq2\tpid\n")
422
+
423
+ bar = ChargingBar('Writing similarity values', max=len(similarityResults))
424
+ for itemPair in similarityResults:
425
+ simData.write(f"{itemPair}\n")
426
+
427
+ bar.next()
428
+ bar.finish()
429
+
430
+ elif (not cmdValues["blastani"]) and (not cmdValues["blast"]) and (not cmdValues["aln"]) and (not cmdValues["mash"]) and (cmdValues["fastani"]):
431
+ similarityResults = fastaniSimilarity(cmdValues)
432
+
433
+ with open(cmdValues["output"],"w") as simData:
434
+ simData.write("seq1\tseq2\tpid\n")
435
+
436
+ bar = ChargingBar('Writing similarity values', max=len(similarityResults))
437
+ for itemPair in similarityResults:
438
+ simData.write(f"{"\t".join([str(r).strip() for r in itemPair])}\n")
439
+
440
+ bar.next()
441
+ bar.finish()
442
+
443
+ elif (not cmdValues["blastani"]) and (not cmdValues["blast"]) and (cmdValues["aln"]) and (not cmdValues["mash"]) and (not cmdValues["fastani"]):
444
+ alignment={}
445
+
446
+ for i in SeqIO.parse(cmdValues['data'],"fasta"):
447
+ alignment[i.id]=i
448
+
449
+ fhandle=open(str(cmdValues['outputFile']),"w")
450
+ fhandle.write("seq1\tseq2\tpid\n")
451
+
452
+ tmpList = sorted(map(sorted, combinations(set([i for i in alignment.keys()]), 2)))
453
+
454
+ with multiprocessing.Pool(processes=cmdValues['threads']) as pool:
455
+ args=[(alignment[m],alignment[n]) for m,n in tmpList]
456
+ results=pool.starmap(alignmentSimilarity, args)
457
+
458
+ elif (not cmdValues["blastani"]) and (cmdValues["blast"]) and (not cmdValues["aln"]) and (not cmdValues["mash"]) and (not cmdValues["fastani"]):
459
+ similarityResults = blastSimilarity(cmdValues)
460
+
461
+ with open(cmdValues["output"],"w") as simData:
462
+ simData.write("seq1\tseq2\tpid\n")
463
+
464
+ bar = ChargingBar('Writing similarity values', max=len(similarityResults))
465
+ for itemPair in similarityResults:
466
+ simData.write(f"{"\t".join([str(r) for r in itemPair])}\n")
467
+
468
+ bar.next()
469
+ bar.finish()
470
+
471
+ elif (cmdValues["blastani"]) and (not cmdValues["blast"]) and (not cmdValues["aln"]) and (not cmdValues["mash"]) and (not cmdValues["fastani"]):
472
+ similarityResults = blastANISimilarity(cmdValues)
473
+
474
+ with open(cmdValues["output"],"w") as simData:
475
+ simData.write("seq1\tseq2\tpid\n")
476
+
477
+ bar = ChargingBar('Writing similarity values', max=len(similarityResults))
478
+ for itemPair in similarityResults:
479
+ simData.write(f"{"\t".join([str(r) for r in itemPair])}\n")
480
+
481
+ bar.next()
482
+ bar.finish()
483
+
484
+ else:
485
+ pass
486
+
487
+
488
+ def transpose(cmdValues):
489
+ if not shutil.which("datamash"):
490
+ print("Install 'datamash' software and try again... exiting...")
491
+ sys.exit()
492
+
493
+ else:
494
+ pass
495
+
496
+ bar = ChargingBar("Transposing table", max=1)
497
+ datamashCmd = f"datamash transpose < {cmdValues["data"]} > {f"{Path(cmdValues["output"]).stem}.transposed.tsv"}"
498
+
499
+ subprocess.call(datamashCmd,shell=True,stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
500
+
501
+ bar.next()
502
+ bar.finish()
503
+
504
+
505
+ def lineages1Levels(cmdValues):
506
+ np.random.rand(1)
507
+
508
+ print(cmdValues["thresholds"][0])
509
+ snpDistDF = pd.read_csv(cmdValues["data"],delimiter="\t",header=0)
510
+
511
+ Graph = nx.Graph()
512
+ Graph.add_nodes_from(snpDistDF.seq2.to_list())
513
+ Graph.add_nodes_from(snpDistDF.seq1.to_list())
514
+
515
+ snpDistDF1 = snpDistDF[snpDistDF.pid >= cmdValues["thresholds"][0]]
516
+ seqPair = [(snpDistDF1.seq1[i],snpDistDF1.seq2[i]) for i in snpDistDF1.index]
517
+
518
+ Graph.add_edges_from(seqPair)
519
+ networkClusters = tuple(nx.connected_components(Graph))
520
+
521
+ fhandle = open(f"{cmdValues['output']}","w")
522
+
523
+ for i,j in enumerate(networkClusters):
524
+ for s in j:
525
+ FirstLevelLin = str(i+1)
526
+ seqLabel = s
527
+ clusterPrefix = cmdValues["prefix"]
528
+ separator = cmdValues["separator"]
529
+
530
+ fhandle.write(f"{seqLabel}\t{clusterPrefix}{separator}{FirstLevelLin}\n")
531
+
532
+ fhandle.close();
533
+
534
+
535
+ def lineages2Levels(cmdValues):
536
+ np.random.rand(1)
537
+
538
+ snpDistDF = pd.read_csv(cmdValues["data"],delimiter="\t",header=0)
539
+
540
+ Graph = nx.Graph()
541
+ Graph.add_nodes_from(snpDistDF.seq2.to_list())
542
+ Graph.add_nodes_from(snpDistDF.seq1.to_list())
543
+
544
+ snpDistDF1 = snpDistDF[snpDistDF.pid >= cmdValues["thresholds"][0]]
545
+ seqPair = [(snpDistDF1.seq1[i],snpDistDF1.seq2[i]) for i in snpDistDF1.index]
546
+
547
+ Graph.add_edges_from(seqPair)
548
+ networkClusters = tuple(nx.connected_components(Graph))
549
+
550
+ fhandle = open(f"{cmdValues['output']}","w")
551
+
552
+ for i,j in enumerate(networkClusters):
553
+ snpDistDF2 = snpDistDF[snpDistDF.seq1.isin(j) & snpDistDF.seq2.isin(j)]
554
+ snpDistDF3 = snpDistDF2[snpDistDF2.pid >= cmdValues["thresholds"][1]]
555
+
556
+ Graph1 = nx.Graph()
557
+ Graph1.add_nodes_from(j)
558
+
559
+ seqPair1 = [(snpDistDF3.seq1[z],snpDistDF3.seq2[z]) for z in snpDistDF3.index]
560
+
561
+ Graph1.add_edges_from(seqPair1)
562
+ networkClusters1 = tuple(nx.connected_components(Graph1))
563
+
564
+ for r,k in enumerate(networkClusters1):
565
+ for s in k:
566
+ FirstLevelLin = str(i+1)
567
+ seqLabel = s
568
+ SecondLevelLin = str(r+1)
569
+ clusterPrefix = cmdValues["prefix"]
570
+ separator = cmdValues["separator"]
571
+
572
+ if len(networkClusters1)==1:
573
+ fhandle.write(f"{seqLabel}\t{clusterPrefix}{separator}{FirstLevelLin}\n")
574
+ else:
575
+ fhandle.write(f"{seqLabel}\t{clusterPrefix}{separator}{FirstLevelLin}.{SecondLevelLin}\n")
576
+
577
+ fhandle.close();
578
+
579
+
580
+ def lineages3Levels(cmdValues):
581
+ np.random.rand(1)
582
+
583
+ snpDistDF = pd.read_csv(cmdValues["data"],delimiter="\t",header=0)
584
+
585
+ Graph = nx.Graph()
586
+ Graph.add_nodes_from(snpDistDF.seq2.to_list())
587
+ Graph.add_nodes_from(snpDistDF.seq1.to_list())
588
+
589
+ snpDistDF1 = snpDistDF[snpDistDF.pid >= cmdValues["thresholds"][0]]
590
+ seqPair = [(snpDistDF1.seq1[i],snpDistDF1.seq2[i]) for i in snpDistDF1.index]
591
+
592
+ Graph.add_edges_from(seqPair)
593
+ networkClusters = tuple(nx.connected_components(Graph))
594
+
595
+ fhandle = open(f"{cmdValues['output']}","w")
596
+
597
+ for i,j in enumerate(networkClusters):
598
+ snpDistDF2 = snpDistDF[snpDistDF.seq1.isin(j) & snpDistDF.seq2.isin(j)]
599
+ snpDistDF3 = snpDistDF2[snpDistDF2.pid >= cmdValues["thresholds"][1]]
600
+
601
+ Graph1 = nx.Graph()
602
+ Graph1.add_nodes_from(j)
603
+
604
+ seqPair1 = [(snpDistDF3.seq1[z],snpDistDF3.seq2[z]) for z in snpDistDF3.index]
605
+
606
+ Graph1.add_edges_from(seqPair1)
607
+ networkClusters1 = tuple(nx.connected_components(Graph1))
608
+
609
+ for p,q in enumerate(networkClusters1):
610
+ snpDistDF2R = snpDistDF[snpDistDF.seq1.isin(q) & snpDistDF.seq2.isin(q)]
611
+ snpDistDF3R = snpDistDF2R[snpDistDF2R.pid >= cmdValues["thresholds"][2]]
612
+
613
+ Graph2 = nx.Graph()
614
+ Graph2.add_nodes_from(q)
615
+
616
+ seqPair1R = [(snpDistDF3R.seq1[z],snpDistDF3R.seq2[z]) for z in snpDistDF3R.index]
617
+
618
+ Graph2.add_edges_from(seqPair1R)
619
+ networkClusters2 = tuple(nx.connected_components(Graph2))
620
+
621
+ for r,k in enumerate(networkClusters2):
622
+ for s in k:
623
+ FirstLevelLin = str(i+1)
624
+ SecondLevelLin = str(p+1)
625
+ ThirdLevelLin = str(r+1)
626
+ clusterPrefix = cmdValues["prefix"]
627
+ seqLabel = s
628
+ separator = cmdValues["separator"]
629
+
630
+ if len(networkClusters1)==1:
631
+ fhandle.write(f"{seqLabel}\t{clusterPrefix}{separator}{FirstLevelLin}\n")
632
+ else:
633
+ if len(networkClusters2)==1:
634
+ fhandle.write(f"{seqLabel}\t{clusterPrefix}{separator}{FirstLevelLin}.{SecondLevelLin}\n")
635
+ else:
636
+ fhandle.write(f"{seqLabel}\t{clusterPrefix}{separator}{FirstLevelLin}.{SecondLevelLin}.{ThirdLevelLin}\n")
637
+
638
+ fhandle.close();
639
+
640
+
641
+ def lineages4Levels(cmdValues):
642
+ np.random.rand(1)
643
+
644
+ snpDistDF = pd.read_csv(cmdValues["data"],delimiter="\t",header=0)
645
+
646
+ Graph = nx.Graph()
647
+ Graph.add_nodes_from(snpDistDF.seq2.to_list())
648
+ Graph.add_nodes_from(snpDistDF.seq1.to_list())
649
+
650
+ snpDistDF1 = snpDistDF[snpDistDF.pid >= cmdValues["thresholds"][0]]
651
+ seqPair = [(snpDistDF1.seq1[i],snpDistDF1.seq2[i]) for i in snpDistDF1.index]
652
+
653
+ Graph.add_edges_from(seqPair)
654
+ networkClusters = tuple(nx.connected_components(Graph))
655
+
656
+ fhandle = open(f"{cmdValues['output']}","w")
657
+
658
+ for i,j in enumerate(networkClusters):
659
+ snpDistDF2 = snpDistDF[snpDistDF.seq1.isin(j) & snpDistDF.seq2.isin(j)]
660
+ snpDistDF3 = snpDistDF2[snpDistDF2.pid >= cmdValues["thresholds"][1]]
661
+
662
+ Graph1 = nx.Graph()
663
+ Graph1.add_nodes_from(j)
664
+
665
+ seqPair1 = [(snpDistDF3.seq1[z],snpDistDF3.seq2[z]) for z in snpDistDF3.index]
666
+
667
+ Graph1.add_edges_from(seqPair1)
668
+ networkClusters1 = tuple(nx.connected_components(Graph1))
669
+
670
+ for p,q in enumerate(networkClusters1):
671
+ snpDistDF2R = snpDistDF[snpDistDF.seq1.isin(q) & snpDistDF.seq2.isin(q)]
672
+ snpDistDF3R = snpDistDF2R[snpDistDF2R.pid >= cmdValues["thresholds"][2]]
673
+
674
+ Graph2 = nx.Graph()
675
+ Graph2.add_nodes_from(q)
676
+
677
+ seqPair1R = [(snpDistDF3R.seq1[z],snpDistDF3R.seq2[z]) for z in snpDistDF3R.index]
678
+
679
+ Graph2.add_edges_from(seqPair1R)
680
+ networkClusters2 = tuple(nx.connected_components(Graph2))
681
+
682
+ for x,y in enumerate(networkClusters2):
683
+
684
+ snpDistDF2RR = snpDistDF[snpDistDF.seq1.isin(y) & snpDistDF.seq2.isin(y)]
685
+ snpDistDF3RR = snpDistDF2RR[snpDistDF2RR.pid >= cmdValues["thresholds"][3]]
686
+
687
+ Graph3 = nx.Graph()
688
+ Graph3.add_nodes_from(y)
689
+
690
+ seqPair1RR = [(snpDistDF3RR.seq1[z],snpDistDF3RR.seq2[z]) for z in snpDistDF3RR.index]
691
+
692
+ Graph3.add_edges_from(seqPair1RR)
693
+ networkClusters3 = tuple(nx.connected_components(Graph3))
694
+
695
+ for r,k in enumerate(networkClusters3):
696
+
697
+ for s in k:
698
+ FirstLevelLin = str(i+1)
699
+ SecondLevelLin = str(p+1)
700
+ ThirdLevelLin = str(x+1)
701
+ FourthLevelLin = str(r+1)
702
+ clusterPrefix = cmdValues["prefix"]
703
+ seqLabel = s
704
+ separator = cmdValues["separator"]
705
+
706
+ if len(networkClusters1)==1:
707
+ fhandle.write(f"{seqLabel}\t{clusterPrefix}{separator}{FirstLevelLin}\n")
708
+ else:
709
+ if len(networkClusters2)==1:
710
+ fhandle.write(f"{seqLabel}\t{clusterPrefix}{separator}{FirstLevelLin}.{SecondLevelLin}\n")
711
+ else:
712
+ if len(networkClusters3)==1:
713
+ fhandle.write(f"{seqLabel}\t{clusterPrefix}{separator}{FirstLevelLin}.{SecondLevelLin}.{ThirdLevelLin}\n")
714
+ else:
715
+ fhandle.write(f"{seqLabel}\t{clusterPrefix}{separator}{FirstLevelLin}.{SecondLevelLin}.{ThirdLevelLin}.{FourthLevelLin}\n")
716
+
717
+ fhandle.close();
718
+
719
+
720
+ def cluster(cmdValues):
721
+ for item in cmdValues['thresholds']:
722
+ if float(item)<0 or float(item)>100:
723
+ print("Clustering thresholds should be between 0 and 100")
724
+ sys.exit()
725
+
726
+ else:
727
+ pass
728
+
729
+ for itemPos,item in enumerate(cmdValues['thresholds']):
730
+ if itemPos == 0:
731
+ pass
732
+ else:
733
+ if cmdValues['thresholds'][itemPos-1]<cmdValues['thresholds'][itemPos]:
734
+ pass
735
+ else:
736
+ print("Clustering thresholds should be provided in increasing order... exiting...")
737
+ sys.exit()
738
+
739
+ if len(cmdValues['thresholds'])==1:
740
+ lineages1Levels(cmdValues)
741
+ elif len(cmdValues['thresholds'])==2:
742
+ lineages2Levels(cmdValues)
743
+ elif len(cmdValues['thresholds'])==3:
744
+ lineages3Levels(cmdValues)
745
+ elif len(cmdValues['thresholds'])==4:
746
+ lineages4Levels(cmdValues)
747
+ else:
748
+ print("Maximum number of nested should be 4... exiting...")
749
+ sys.exit()
750
+
751
+
752
+ def order(cmdValues):
753
+ with open(cmdValues["output"],"w") as outputFile:
754
+ dataMatrixDF = loadDataTable(cmdValues)
755
+
756
+ itemNames = [re.split("\t|,|;",str(item).strip())[0] for item in open(cmdValues["rownames"],"r")]
757
+
758
+ bar = ChargingBar('Saving ordered table', max=len(itemNames))
759
+ for itemNum,itemRowName in enumerate(itemNames):
760
+ if itemNum == 0:
761
+ headerName = list(dataMatrixDF.keys())[0]
762
+ outputFile.write(f"{headerName}\t{"\t".join([str(i) for i in dataMatrixDF[headerName]])}\n")
763
+ else:
764
+ pass
765
+
766
+ if itemRowName in list(dataMatrixDF.keys()):
767
+ outputFile.write(f"{itemRowName}\t{"\t".join([str(i) for i in dataMatrixDF[itemRowName]])}\n")
768
+
769
+ else:
770
+ outputFile.write(f"{itemRowName}\n")
771
+
772
+ bar.next()
773
+ bar.finish()
774
+
775
+ def sample(cmdValues):
776
+ np.random.seed(1)
777
+
778
+ dataMatrixDF = loadDataTable(cmdValues)
779
+
780
+ rows = list(dataMatrixDF.keys())
781
+ columns = np.arange(len(dataMatrixDF[rows[0]])).tolist()
782
+
783
+ selectedColumns = np.random.choice(columns,int(np.ceil(float(cmdValues["colfreq"])*len(columns))),replace=False).tolist()
784
+ selectedRows = [rows[0]]
785
+ selectedRows.extend(np.random.choice(rows[1:],int(np.ceil(float(cmdValues["rowfreq"])*len(rows[1:]))),replace=False).tolist())
786
+
787
+ with open(cmdValues["output"],"w") as outputFile:
788
+
789
+ bar = ChargingBar('Saving table', max=len(selectedRows)*len(selectedColumns))
790
+ for i,j in enumerate(selectedRows):
791
+ tmpStr = []
792
+
793
+ for r,s in enumerate(selectedColumns):
794
+ if r == 0:
795
+ tmpStr.append(str(j))
796
+ tmpStr.append(str(dataMatrixDF[j][r]))
797
+ else:
798
+ tmpStr.append(str(dataMatrixDF[j][r]))
799
+
800
+ bar.next()
801
+ outputFile.write(f"{"\t".join(tmpStr)}\n")
802
+
803
+ bar.finish()
804
+
805
+
806
+ def rename(cmdValues):
807
+ with open(cmdValues["output"],"w") as outputFile:
808
+ dataMatrixDF = loadDataTable(cmdValues)
809
+
810
+ columnIndexValues = []
811
+
812
+ if not os.path.exists(cmdValues["newdata"]):
813
+ print(f"File {cmdValues["newdata"]} does not exist... exiting...")
814
+ sys.exit()
815
+ else:
816
+ pass
817
+
818
+ if not isinstance(cmdValues["columns"],str):
819
+ columnIndexValues = [int(i) for i in [re.split("\t|,|;",str(item).strip()) for item in cmdValues["columns"]][0]]
820
+
821
+ else:
822
+ columnIndexValues = [int(i) for i in cmdValues["columns"]]
823
+
824
+ newRowNameValuesDF = {}
825
+
826
+ tmpCmd="wc -l "+str(cmdValues["newdata"])+" | awk '{print $1}'"
827
+ dataFileLines=subprocess.Popen(tmpCmd,shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
828
+ dataLines = int(dataFileLines.communicate()[0])
829
+ dataFileLines.kill()
830
+
831
+ bar = ChargingBar('Loading file with new names', max=int(dataLines))
832
+ with open(cmdValues["newdata"],"r") as newData:
833
+ for rowData in newData:
834
+ newRowNameValues = []
835
+
836
+ if len(rowData.strip().split("\t")) > 0:
837
+ if max(columnIndexValues) < 0 or max(columnIndexValues) > len(rowData.strip().split("\t")):
838
+ print(f"Values for --column/-c should be >0 and <number of columns in the new data table... exiting...")
839
+ sys.exit()
840
+ else:
841
+ pass
842
+ else:
843
+ pass
844
+
845
+ for columnIndex in columnIndexValues:
846
+ newRowNameValues.append(rowData.strip().split("\t")[columnIndex-1])
847
+
848
+ newRowNameValuesDF[rowData.strip().split("\t")[0]] = "_".join(newRowNameValues)
849
+
850
+ bar.next()
851
+ bar.finish()
852
+
853
+ bar = ChargingBar('Saving renamed table', max=len(dataMatrixDF.keys()))
854
+ for rowIndex,rowName in enumerate(dataMatrixDF.keys()):
855
+
856
+ if rowIndex == 0:
857
+ outputFile.write(f"{rowName}\t{"\t".join([str(i) for i in dataMatrixDF[rowName]])}\n")
858
+ else:
859
+ if rowName in newRowNameValuesDF.keys():
860
+ outputFile.write(f"{newRowNameValuesDF[rowName]}\t{"\t".join([str(i) for i in dataMatrixDF[rowName]])}\n")
861
+
862
+ else:
863
+ outputFile.write(f"{rowName}\t{"\t".join([str(i) for i in dataMatrixDF[rowName]])}\n")
864
+
865
+ bar.next()
866
+ bar.finish()
867
+
868
+
869
+ def filter(cmdValues):
870
+ if cmdValues["minCutoff"]>=0 and cmdValues["maxCutoff"]<=100:
871
+ if cmdValues["minCutoff"] > cmdValues["maxCutoff"]:
872
+ print(f"--->>>Value of --min/-a should be less than or equal to value of --max/-b... exiting...")
873
+ sys.exit()
874
+ else:
875
+ pass
876
+
877
+ dataMatrixDF = loadDataTable(cmdValues)
878
+
879
+ tmpMatrixData = {}
880
+
881
+ with open(cmdValues["output"],"w") as outputFile:
882
+ columnValues = []
883
+
884
+ bar = ChargingBar('Filtering table', max=len(dataMatrixDF[list(dataMatrixDF.keys())[0]]))
885
+ for itemPos,itemValue in enumerate(dataMatrixDF[list(dataMatrixDF.keys())[0]]):
886
+ itemMatrixColValues = []
887
+
888
+ for tmpPos,itemMatrixName in enumerate(dataMatrixDF.keys()):
889
+ if tmpPos != 0:
890
+ itemMatrixColValues.append(dataMatrixDF[itemMatrixName][itemPos])
891
+ else:
892
+ pass
893
+
894
+ rowMatrixSum = sum(itemMatrixColValues)
895
+
896
+ if rowMatrixSum >= (cmdValues["minCutoff"]/100)*len(itemMatrixColValues) and \
897
+ rowMatrixSum <= (cmdValues["maxCutoff"]/100)*len(itemMatrixColValues):
898
+
899
+ columnValues.append(itemPos)
900
+ else:
901
+ pass
902
+
903
+ bar.next()
904
+ bar.finish()
905
+
906
+ bar = ChargingBar('Saving filtered table', max=len(list(dataMatrixDF.keys())))
907
+ for itemMatrixName in dataMatrixDF.keys():
908
+ tmpColData = []
909
+
910
+ for colPosition in columnValues:
911
+ tmpColData.append(str(dataMatrixDF[itemMatrixName][colPosition]))
912
+
913
+ bar.next()
914
+ outputFile.write(f"{str(itemMatrixName)}\t{"\t".join(tmpColData)}\n")
915
+ bar.finish()
916
+
917
+ else:
918
+ if cmdValues["minCutoff"] > cmdValues["maxCutoff"]:
919
+ print(f"--->>>Value of --min/-a should be less than or equal to value of --max/-b... exiting...")
920
+ sys.exit()
921
+ else:
922
+ print(f"--->>>Value of --min/-a snd --max/-b values should be between 0 and 100... exiting...")
923
+ sys.exit()
924
+
925
+
926
+ def queryKmers(seqNum,seqFile,kmerFile,cmdValues):
927
+ seqFileName = Path(os.path.basename(seqFile)).stem
928
+ tmpOutFileA = f"bifrost.{str(datetime.datetime.now()).replace(" ","").replace("-",".").replace(":",".")}"
929
+
930
+ tmpCmd1 = f"Bifrost build -t 3 -k 31 -i -d -r {seqFile} -o {tmpOutFileA}.graph"
931
+ subprocess.call(tmpCmd1,shell=True,stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
932
+
933
+ if cmdValues["approximate"]:
934
+ tmpCmd2 = f"Bifrost query -a -t 5 -e 1 -q {kmerFile} -g {tmpOutFileA}.graph.gfa.gz -o {tmpOutFileA}"
935
+ subprocess.call(tmpCmd2,shell=True,stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
936
+ else:
937
+ tmpCmd2 = f"Bifrost query -t 5 -e 1 -q {kmerFile} -g {tmpOutFileA}.graph.gfa.gz -o {tmpOutFileA}"
938
+ subprocess.call(tmpCmd2,shell=True,stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
939
+
940
+ tmpCmd3 = f"echo {seqFileName} > {tmpOutFileA}.out.tsv"
941
+ subprocess.call(tmpCmd3,shell=True,stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
942
+
943
+ tmpCmd4 = f"grep -v query_name {tmpOutFileA}.tsv | sort -k1 -n > {tmpOutFileA}.out.tmp.tsv"
944
+ subprocess.call(tmpCmd4,shell=True,stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
945
+
946
+ tmpCmd5 = "awk '{print $2}' "+f"{tmpOutFileA}.out.tmp.tsv >> {tmpOutFileA}.out.tsv"
947
+ subprocess.call(tmpCmd5,shell=True,stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
948
+
949
+ tmpCmd6 = f"echo KmerID > {tmpOutFileA}.head.tsv"
950
+ subprocess.call(tmpCmd6,shell=True,stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
951
+
952
+ tmpCmd7 = "awk '{print $1}' "+f"{tmpOutFileA}.out.tmp.tsv | sed \"s:query_name:KmerID:g\" >> {tmpOutFileA}.head.tsv";
953
+ subprocess.call(tmpCmd7,shell=True,stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
954
+
955
+ tmpCmd8 = f"datamash transpose < {tmpOutFileA}.head.tsv > {tmpOutFileA}.header.tsv"
956
+ subprocess.call(tmpCmd8,shell=True,stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
957
+
958
+ tmpCmd9 = f"datamash transpose < {tmpOutFileA}.out.tsv > {tmpOutFileA}.tmp.tsv"
959
+ subprocess.call(tmpCmd9,shell=True,stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
960
+
961
+ kmerData = {seqFileName: {"kmerFile": [str(i).strip() for i in open(f"{tmpOutFileA}.tmp.tsv","r")][0],"kmerHeaderFile": [str(j).strip() for j in open(f"{tmpOutFileA}.header.tsv","r")][0] }}
962
+
963
+ if os.path.exists(f"{tmpOutFileA}.graph.bfi"):
964
+ os.remove(f"{tmpOutFileA}.graph.bfi")
965
+ else:
966
+ pass
967
+
968
+ if os.path.exists(f"{tmpOutFileA}.graph.gfa.gz"):
969
+ os.remove(f"{tmpOutFileA}.graph.gfa.gz")
970
+ else:
971
+ pass
972
+
973
+ if os.path.exists(f"{tmpOutFileA}.out.tsv"):
974
+ os.remove(f"{tmpOutFileA}.out.tsv")
975
+ else:
976
+ pass
977
+
978
+ if os.path.exists(f"{tmpOutFileA}.tsv"):
979
+ os.remove(f"{tmpOutFileA}.tsv")
980
+ else:
981
+ pass
982
+
983
+ if os.path.exists(f"{tmpOutFileA}.out.tmp.tsv"):
984
+ os.remove(f"{tmpOutFileA}.out.tmp.tsv")
985
+ else:
986
+ pass
987
+
988
+ if os.path.exists(f"{tmpOutFileA}.out.tsv"):
989
+ os.remove(f"{tmpOutFileA}.out.tsv")
990
+ else:
991
+ pass
992
+
993
+ if os.path.exists(f"{tmpOutFileA}.head.tsv"):
994
+ os.remove(f"{tmpOutFileA}.head.tsv")
995
+ else:
996
+ pass
997
+
998
+ if os.path.exists(f"{tmpOutFileA}.header.tsv"):
999
+ os.remove(f"{tmpOutFileA}.header.tsv")
1000
+ else:
1001
+ pass
1002
+
1003
+ if os.path.exists(f"{tmpOutFileA}.tmp.tsv"):
1004
+ os.remove(f"{tmpOutFileA}.tmp.tsv")
1005
+ else:
1006
+ pass
1007
+
1008
+ return(kmerData)
1009
+
1010
+
1011
+ def table(cmdValues):
1012
+ if not os.path.exists(cmdValues["data"]):
1013
+ print(f"File {cmdValues["data"]} not found... exiting...")
1014
+ sys.exit()
1015
+ else:
1016
+ pass
1017
+
1018
+ if not shutil.which("awk"):
1019
+ print("Install awk and try again... exiting...")
1020
+ sys.exit()
1021
+ else:
1022
+ pass
1023
+
1024
+ if not shutil.which("dsk") and not shutil.which("dsk2ascii"):
1025
+ print("Install DSK and try again... exiting...")
1026
+ sys.exit()
1027
+ else:
1028
+ pass
1029
+
1030
+ if not shutil.which("Bifrost"):
1031
+ print("Install Bifrost and try again... exiting...")
1032
+ sys.exit()
1033
+ else:
1034
+ pass
1035
+
1036
+ if not shutil.which("datamash"):
1037
+ print("Install datamash and try again... exiting...")
1038
+ sys.exit()
1039
+ else:
1040
+ pass
1041
+
1042
+ kmersFile = ""
1043
+
1044
+ if cmdValues["kmers"] != "":
1045
+ kmersFile = cmdValues["kmers"]
1046
+ else:
1047
+ kmersFile = f"{Path(cmdValues["output"]).stem}.fasta"
1048
+
1049
+ with open(cmdValues["data"],"r") as seqFile:
1050
+
1051
+ tmpCmd="wc -l "+str(cmdValues["data"])+" | awk '{print $1}'"
1052
+ dataFileLines = subprocess.Popen(tmpCmd,shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
1053
+ dataLines = int(dataFileLines.communicate()[0])
1054
+ dataFileLines.kill()
1055
+
1056
+ bar = ChargingBar("Generating k-mers (DSK)", max=int(dataLines))
1057
+ for fileNameValue in seqFile:
1058
+ fileName = fileNameValue.strip()
1059
+
1060
+ if os.path.exists(fileName):
1061
+ if os.path.isfile(fileName) and os.path.getsize(fileName) > 0:
1062
+ pass
1063
+ else:
1064
+ print(f"File {fileName} in {cmdValues["data"]} is empty... exiting...")
1065
+ sys.exit()
1066
+ else:
1067
+ print(f"File {fileName} in {cmdValues["data"]} does not exist... exiting...")
1068
+ sys.exit()
1069
+
1070
+ bar.next()
1071
+ bar.finish()
1072
+
1073
+ if cmdValues["dsk"] and not cmdValues["bifrost"]:
1074
+
1075
+ if cmdValues["kmers"] == "":
1076
+ bar = ChargingBar("Generating k-mers (DSK)", max=2)
1077
+ dskCmdTmpFile = f"KMERS.{str(datetime.datetime.now()).replace(" ","").replace("-",".").replace(":",".")}"
1078
+
1079
+ dsk1Cmd = f"dsk -kmer-size {cmdValues["length"]} -abundance-min {cmdValues["minabundance"]} -max-memory {cmdValues["maxmemory"]} -file {cmdValues["data"]} -out {dskCmdTmpFile} -nb-cores {cmdValues["threads"]}"
1080
+ subprocess.call(dsk1Cmd,shell=True,stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
1081
+ bar.next()
1082
+
1083
+ tmpKmersFileR = f"KMERS.{str(datetime.datetime.now()).replace(" ","").replace("-",".").replace(":",".")}"
1084
+
1085
+ dsk2Cmd = f"dsk2ascii -file {dskCmdTmpFile}.h5 -out {tmpKmersFileR} -nb-cores {cmdValues["threads"]}"
1086
+ subprocess.call(dsk2Cmd,shell=True,stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
1087
+ bar.next()
1088
+
1089
+ awkCmd = "awk '{print \">\"NR\"\\n\"$1}' "+str(tmpKmersFileR)+" > "+str(kmersFile)
1090
+ subprocess.call(awkCmd,shell=True,stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
1091
+ bar.next()
1092
+ bar.finish()
1093
+
1094
+ if os.path.exists(f"{dskCmdTmpFile}.h5"):
1095
+ os.remove(f"{dskCmdTmpFile}.h5")
1096
+ else:
1097
+ pass
1098
+
1099
+ if os.path.exists(f"{dskCmdTmpFile}"):
1100
+ os.remove(f"{dskCmdTmpFile}")
1101
+ else:
1102
+ pass
1103
+
1104
+ if os.path.exists(f"{dskCmdTmpFile}.txt"):
1105
+ os.remove(f"{dskCmdTmpFile}.txt")
1106
+ else:
1107
+ pass
1108
+
1109
+ if os.path.exists(f"{tmpKmersFileR}.txt"):
1110
+ os.remove(f"{tmpKmersFileR}.txt")
1111
+ else:
1112
+ pass
1113
+
1114
+ if os.path.exists(f"{tmpKmersFileR}"):
1115
+ os.remove(f"{tmpKmersFileR}")
1116
+ else:
1117
+ pass
1118
+
1119
+ else:
1120
+ pass
1121
+
1122
+ else:
1123
+ if cmdValues["kmers"] != "":
1124
+ tmpKmersFile = f"KMERS.{str(datetime.datetime.now()).replace(" ","").replace("-",".").replace(":",".")}"
1125
+
1126
+ bar = ChargingBar("Generating unitigs (Bifrost)", max=1)
1127
+ bifrostCmd = f"Bifrost build -t {cmdValues["threads"]} -k {cmdValues["length"]} -i -d -r {cmdValues["data"]} -o {tmpKmersFile} -v -a -f -n"
1128
+
1129
+ subprocess.call(bifrostCmd,shell=True,stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
1130
+ bar.next()
1131
+ bar.finish()
1132
+
1133
+ with open(kmersFile,"w") as kmerData:
1134
+ for seqNum,seq in enumerate(SeqIO.parse(f"{tmpKmersFile}.fasta","fasta")):
1135
+ kmerData.write(f">{(seqNum+1)}\n{seq.seq}\n")
1136
+
1137
+ if os.path.exists(f"{tmpKmersFile}.bfi"):
1138
+ os.remove(f"{tmpKmersFile}.bfi")
1139
+ else:
1140
+ pass
1141
+
1142
+ if os.path.exists(f"{tmpKmersFile}.fasta"):
1143
+ os.remove(f"{tmpKmersFile}.fasta")
1144
+ else:
1145
+ pass
1146
+
1147
+ else:
1148
+ pass
1149
+
1150
+ tmpCmd="wc -l "+str(cmdValues["data"])+" | awk '{print $1}'"
1151
+ dataFileLines = subprocess.Popen(tmpCmd,shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
1152
+ dataLines = int(dataFileLines.communicate()[0])
1153
+ dataFileLines.kill()
1154
+
1155
+ dataMatrixDF = []
1156
+
1157
+ bar = ChargingBar("Processing sequence files", max=int(dataLines))
1158
+ for seqFile in open(cmdValues["data"],"r"):
1159
+ dataMatrixDF.append(str(seqFile).strip().split("\t")[0])
1160
+
1161
+ bar.next()
1162
+ bar.finish()
1163
+
1164
+ seqFileDF = [(pos,item) for pos,item in enumerate(dataMatrixDF)]
1165
+
1166
+ print("Query k-mers against sequences... may take longer... be patient...")
1167
+
1168
+ with multiprocessing.Pool(processes=cmdValues["threads"]) as pool:
1169
+ args = [(seqFileNum,seqFileName,kmersFile,cmdValues) for seqFileNum,seqFileName in seqFileDF]
1170
+ kmerResults = pool.starmap(queryKmers, args)
1171
+
1172
+ with open(f"{cmdValues["output"]}","w") as kmerData:
1173
+ kmerFiles = []
1174
+ kmerHeaderFiles = []
1175
+
1176
+ bar = ChargingBar("Writing k-mers table", max=len(kmerResults))
1177
+ for eachResultPos,eachResult in enumerate(kmerResults):
1178
+ seqName = list(eachResult.keys())[0]
1179
+
1180
+ kmerFiles.append(eachResult[seqName])
1181
+ kmerHeaderFiles.append(eachResult[seqName])
1182
+
1183
+ if eachResultPos == 0:
1184
+ kmerData.write(f"{eachResult[seqName]["kmerHeaderFile"]}\n")
1185
+ kmerData.write(f"{eachResult[seqName]["kmerFile"]}\n")
1186
+ else:
1187
+ kmerData.write(f"{eachResult[seqName]["kmerFile"]}\n")
1188
+
1189
+ bar.next()
1190
+ bar.finish()
1191
+
1192
+
1193
+ def loadDataTable(cmdValues):
1194
+ if not (os.path.exists(cmdValues["data"]) and os.path.isfile(cmdValues["data"])):
1195
+ print(f"--->>>Input file {cmdValues["data"]} does not exist... exiting...")
1196
+ sys.exit()
1197
+
1198
+ else:
1199
+ dataMatrixDF = {}
1200
+
1201
+ sepFormat = "\t" if cmdValues["format"] == "tsv" else ","
1202
+
1203
+ if not (os.path.exists(cmdValues["data"]) and os.path.isfile(cmdValues["data"])):
1204
+ print(f"--->>>Input file {cmdValues["data"]} does not exist... exiting...")
1205
+ sys.exit()
1206
+ else:
1207
+ with open(cmdValues["data"],"r") as dataFile:
1208
+
1209
+ if not shutil.which("awk"):
1210
+ print("Install awk and try again... exiting...")
1211
+ sys.exit()
1212
+ else:
1213
+ pass
1214
+
1215
+ tmpCmd="wc -l "+str(cmdValues["data"])+" | awk '{print $1}'"
1216
+ dataFileLines = subprocess.Popen(tmpCmd,shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
1217
+ dataLines = int(dataFileLines.communicate()[0])
1218
+ dataFileLines.kill()
1219
+
1220
+ bar = ChargingBar('Loading data table', max=int(dataLines))
1221
+ for dataPos,dataRow in enumerate(dataFile):
1222
+ if len(dataRow.strip().split(sepFormat)) == 0:
1223
+ pass
1224
+ else:
1225
+ if dataPos == 0:
1226
+ dataRowValues = dataRow.strip().split(sepFormat)
1227
+ dataRowValuesName = dataRowValues[0]
1228
+ dataRowValuesCols = [item for item in dataRowValues[1:]]
1229
+ dataMatrixDF[dataRowValuesName] = dataRowValuesCols
1230
+ else:
1231
+ dataRowValues = dataRow.strip().split(sepFormat)
1232
+ dataRowValuesName = dataRowValues[0]
1233
+ dataRowValuesCols = [int(item) for item in dataRowValues[1:]]
1234
+ dataMatrixDF[dataRowValuesName] = dataRowValuesCols
1235
+
1236
+ if set(dataRowValuesCols) == {0} or set(dataRowValuesCols) == {1} or set(dataRowValuesCols) == {0,1}:
1237
+ pass
1238
+ else:
1239
+ print(f"--->>>Row {dataPos} in {cmdValues["data"]} contains unexpected other values besides 0 and 1... exiting...")
1240
+ sys.exit()
1241
+
1242
+ bar.next()
1243
+ bar.finish()
1244
+
1245
+ return(dataMatrixDF)
1246
+
1247
+
1248
+ def citation():
1249
+ print(f"Chrispin Chaguza. 2026. seqCompare: A toolkit for sequence similarity analysis. https://github.com/ChrispinChaguza/seqCompare")
1250
+
1251
+
1252
+ def printVersion():
1253
+ print(f"seqCompare {__version__}")
1254
+
1255
+
1256
+ def seqCompareMain():
1257
+
1258
+ optionsCmd = argparse.ArgumentParser(sys.argv[0],
1259
+ usage=argparse.SUPPRESS,
1260
+ description='seqCompare: A toolkit for sequence similarity analysis',
1261
+ prefix_chars='-',
1262
+ add_help=True,
1263
+ epilog='Written by Chrispin Chaguza, St Jude Children\'s Research Hospital, 2026')
1264
+
1265
+ optionsCommand = optionsCmd.add_subparsers(dest="command")
1266
+
1267
+ similarityOptions = optionsCommand.add_parser("similarity")
1268
+
1269
+ similarityOptions.add_argument('--data','-d',action='store',required=True,nargs=1,
1270
+ metavar='data',dest='data',
1271
+ help='input tabular text file (input should be file of fasta files when used with --mash/-m and --fastani/-n options or alignment file when used with --aln/-a option)')
1272
+ similarityOptions.add_argument('--format','-f',action='store',required=False,nargs=1,
1273
+ metavar='format',dest='format',default="tsv",
1274
+ choices=['tsv','csv'],
1275
+ help='data format (default: tsv)')
1276
+ similarityOptions.add_argument('--output','-o',action='store',required=False,nargs=1,
1277
+ metavar='output',dest='output',default="out.similarity.tsv",
1278
+ help='output percent similarity')
1279
+ similarityOptions.add_argument('--length','-l',action='store',required=False,nargs=1,
1280
+ metavar='length',dest='length',default=31,type=int,
1281
+ help='k-mer length for --mash/-m or --fastani/-n (default: 31)')
1282
+ similarityOptions.add_argument('--size','-s',action='store',required=False,nargs=1,
1283
+ metavar='size',dest='size',default=1000000,type=int,
1284
+ help='sketch size for MASH; use with --mash (default: 1000000)')
1285
+ similarityOptions.add_argument('--mash','-m',action='store_true',default=False,
1286
+ dest='mash',help='calculate similarity using MASH')
1287
+ similarityOptions.add_argument('--fastani','-n',action='store_true',default=False,
1288
+ dest='fastani',help='calculate similarity using fastANI')
1289
+ similarityOptions.add_argument('--blast','-b',action='store_true',default=False,
1290
+ dest='blast',help='calculate similarity using BLAST')
1291
+ similarityOptions.add_argument('--blastani','-x',action='store_true',default=False,
1292
+ dest='blastani',help='calculate blast ANI similarity')
1293
+ similarityOptions.add_argument('--aln','-a',action='store_true',default=False,
1294
+ dest='aln',help='calculate similarity based on a given sequence alignment')
1295
+ similarityOptions.add_argument('--fraction','-c',action='store',required=False,nargs=1,
1296
+ metavar='fraction',dest='fraction',default=0.70,type=float,
1297
+ help='minimum overlap fraction with shorter sequence for ANI calculation; use with --fastani or --blastani (default: 0.70)')
1298
+ similarityOptions.add_argument('--fragment','-r',action='store',required=False,nargs=1,
1299
+ metavar='fragment',dest='fragment',default=500,type=int,
1300
+ help='fragment length for calculating ANI; use with --fastani (default: 500)')
1301
+ similarityOptions.add_argument('--evalue','-e',action='store',required=False,nargs=1,
1302
+ metavar='evalue',dest='evalue',default=0.001,type=float,
1303
+ help='minimum evalue for hits; use with --blast (default: 0.001)')
1304
+ similarityOptions.add_argument('--threads','-t',action='store',required=False,nargs=1,
1305
+ metavar='threads',dest='threads',default=5,type=int,
1306
+ help='number of threads (default: 5)')
1307
+
1308
+
1309
+ orderOptions = optionsCommand.add_parser("order")
1310
+
1311
+ orderOptions.add_argument('--data','-d',action='store',required=True,nargs=1,
1312
+ metavar='data',dest='data',
1313
+ help='input tabular text file')
1314
+ orderOptions.add_argument('--format','-f',action='store',required=False,nargs=1,
1315
+ metavar='format',dest='format',default="tsv",
1316
+ choices=['tsv','csv'],
1317
+ help='data format (default: tsv)')
1318
+ orderOptions.add_argument('--output','-o',action='store',required=False,nargs=1,
1319
+ metavar='output',dest='output',default="out.ordered.tsv",
1320
+ help='output ordered data file')
1321
+ orderOptions.add_argument('--alphabetic','-a',action='store_true',required=False,
1322
+ dest='alphabetic',default=False,
1323
+ help='show similarity values as percentage instead of fraction')
1324
+ orderOptions.add_argument('--rownames','-r',action='store',required=False,nargs=1,
1325
+ metavar='rownames',dest='rownames',
1326
+ help='order by item names in a given text file (one name per row)')
1327
+ orderOptions.add_argument('--threads','-t',action='store',required=False,nargs=1,
1328
+ metavar='threads',dest='threads',default=5,type=int,
1329
+ help='number of threads (default: 5)')
1330
+
1331
+ renameOptions = optionsCommand.add_parser("rename")
1332
+
1333
+ renameOptions.add_argument('--data','-d',action='store',required=True,nargs=1,
1334
+ metavar='data',dest='data',
1335
+ help='input tabular text file')
1336
+ renameOptions.add_argument('--format','-f',action='store',required=False,nargs=1,
1337
+ metavar='format',dest='format',default="tsv",
1338
+ choices=['tsv','csv'],
1339
+ help='data format (default: tsv)')
1340
+ renameOptions.add_argument('--output','-o',action='store',required=False,nargs=1,
1341
+ metavar='output',dest='output',default="out.renamed.tsv",
1342
+ help='output ordered data file')
1343
+ renameOptions.add_argument('--columns','-c',action='store',required=True,nargs="*",
1344
+ metavar='columns',dest='columns',
1345
+ help='selected column numbers (e.g., --columns 1 2 3 4)')
1346
+ renameOptions.add_argument('--newdata','-n',action='store',required=True,nargs=1,
1347
+ metavar='newdata',dest='newdata',
1348
+ help='order by item names in a given text file (one name per row)')
1349
+
1350
+
1351
+ filterOptions = optionsCommand.add_parser("filter")
1352
+
1353
+ filterOptions.add_argument('--data','-d',action='store',required=True,nargs=1,
1354
+ metavar='data',dest='data',
1355
+ help='input tabular text file')
1356
+ filterOptions.add_argument('--format','-f',action='store',required=False,nargs=1,
1357
+ metavar='format',dest='format',default="tsv",
1358
+ choices=['tsv','csv'],
1359
+ help='data format (default: tsv)')
1360
+ filterOptions.add_argument('--output','-o',action='store',required=False,nargs=1,
1361
+ metavar='output',dest='output',default="out.filter.tsv",
1362
+ help='output percent similarity')
1363
+ filterOptions.add_argument('--min','-a',action='store',required=False,nargs=1,
1364
+ metavar='minCutoff',dest='minCutoff',default=0,type=int,
1365
+ help='select with frequency ≥minCutoff percent (overrides --columns/-c and --rows/-r)')
1366
+ filterOptions.add_argument('--max','-b',action='store',required=False,nargs=1,
1367
+ metavar='maxCutoff',dest='maxCutoff',default=100,type=int,
1368
+ help='select with frequency ≤maxCutoff percent (overrides --columns/-c and --rows/-r)')
1369
+ filterOptions.add_argument('--columns','-c',action='store',required=False,nargs=1,
1370
+ metavar='columns',dest='columns',
1371
+ help='select only columns present in a given text file (one column value per row)')
1372
+ filterOptions.add_argument('--rows','-r',action='store',required=False,nargs=1,
1373
+ metavar='rows',dest='rows',
1374
+ help='select only rows present in a given text file (one column value per row)')
1375
+ filterOptions.add_argument('--threads','-t',action='store',required=False,nargs=1,
1376
+ metavar='threads',dest='threads',default=5,type=int,
1377
+ help='number of threads (default: 5)')
1378
+
1379
+
1380
+ sampleOptions = optionsCommand.add_parser("sample")
1381
+
1382
+ sampleOptions.add_argument('--data','-d',action='store',required=True,nargs=1,
1383
+ metavar='data',dest='data',
1384
+ help='input tabular text file')
1385
+ sampleOptions.add_argument('--format','-f',action='store',required=False,nargs=1,
1386
+ metavar='format',dest='format',default="tsv",
1387
+ choices=['tsv','csv'],
1388
+ help='data format (default: tsv)')
1389
+ sampleOptions.add_argument('--output','-o',action='store',required=False,nargs=1,
1390
+ metavar='output',dest='output',default="out.sample.tsv",
1391
+ help='output file')
1392
+ sampleOptions.add_argument('--cf','-c',action='store',required=False,nargs=1,
1393
+ metavar='colfreq',dest='colfreq',default=0,type=float,
1394
+ help='randomly select ≥colfreq percent of the columns')
1395
+ sampleOptions.add_argument('--rf','-r',action='store',required=False,nargs=1,
1396
+ metavar='rowfreq',dest='rowfreq',default=0,type=float,
1397
+ help='randomly select ≥rowfreq percent of the rows')
1398
+
1399
+
1400
+ transposeOptions = optionsCommand.add_parser("transpose")
1401
+
1402
+ transposeOptions.add_argument('--data','-d',action='store',required=True,nargs=1,
1403
+ metavar='data',dest='data',
1404
+ help='input tabular text file')
1405
+ transposeOptions.add_argument('--format','-f',action='store',required=False,nargs=1,
1406
+ metavar='format',dest='format',default="tsv",
1407
+ choices=['tsv','csv'],
1408
+ help='data format (default: tsv)')
1409
+ transposeOptions.add_argument('--output','-o',action='store',required=False,nargs=1,
1410
+ metavar='output',dest='output',default="out.filter.tsv",
1411
+ help='output percent similarity')
1412
+
1413
+
1414
+ clusterOptions = optionsCommand.add_parser("cluster")
1415
+
1416
+ clusterOptions.add_argument('--data','-d',action='store',required=True,nargs=1,
1417
+ metavar='data',dest='data',
1418
+ help='input tabular text file')
1419
+ clusterOptions.add_argument('--format','-f',action='store',required=False,nargs=1,
1420
+ metavar='format',dest='format',default="tsv",
1421
+ choices=['tsv','csv'],
1422
+ help='data format (default: tsv)')
1423
+ clusterOptions.add_argument('--output','-o',action='store',required=False,nargs=1,
1424
+ metavar='output',dest='output',default="out.clusters.tsv",
1425
+ help='output clusters')
1426
+ clusterOptions.add_argument('--prefix','-p',action='store',required=False,nargs=1,
1427
+ metavar='prefix',dest='prefix',default="CLS",
1428
+ help='cluster label prefix')
1429
+ clusterOptions.add_argument('--sep','-s',action='store',required=False,nargs=1,
1430
+ metavar='separator',dest='separator',default="-",
1431
+ help='lineage label separator')
1432
+ clusterOptions.add_argument('--thresholds','-t',action='store',required=True,nargs="*",
1433
+ metavar='thresholds',dest='thresholds',
1434
+ help='cluster or lineage thresholds')
1435
+
1436
+
1437
+ kmerMatrixOptions = optionsCommand.add_parser("table")
1438
+
1439
+ kmerMatrixOptions.add_argument('--data','-d',action='store',required=True,nargs=1,
1440
+ metavar='data',dest='data',
1441
+ help='input text files containing fasta sequence file names')
1442
+ kmerMatrixOptions.add_argument('--output','-o',action='store',required=False,nargs=1,
1443
+ metavar='output',dest='output',default="db.kmers.fasta",
1444
+ help='output percent similarity')
1445
+ kmerMatrixOptions.add_argument('--length','-l',action='store',required=False,nargs=1,
1446
+ metavar='length',dest='length',default=31,type=int,
1447
+ help='k-mer length (default: 31)')
1448
+ kmerMatrixOptions.add_argument('--min','-m',action='store',required=False,nargs=1,
1449
+ metavar='minabundance',dest='minabundance',default=1,type=int,
1450
+ help='minimum k-mer abundance (default: 1)')
1451
+ kmerMatrixOptions.add_argument('--maxmem','-x',action='store',required=False,nargs=1,
1452
+ metavar='maxmemory',dest='maxmemory',default=31,type=int,
1453
+ help='maximum memory (default: 5000)')
1454
+ kmerMatrixOptions.add_argument('--dsk','-s',action='store_true',default=True,
1455
+ dest='dsk',help='DSK to identify k-mers')
1456
+ kmerMatrixOptions.add_argument('--bifrost','-b',action='store_true',default=False,
1457
+ dest='bifrost',
1458
+ help='bifrost to identify variable length k-mers or unitigs')
1459
+ kmerMatrixOptions.add_argument('--kmers','-k',action='store',required=False,nargs=1,
1460
+ metavar='kmers',dest='kmers',default="",
1461
+ help='only query input sequence against a given k-mer database')
1462
+ kmerMatrixOptions.add_argument('--approx','-a',action='store_true',required=False,
1463
+ dest='approximate',default=False,
1464
+ help='search inexact k-mers (default: False)')
1465
+ kmerMatrixOptions.add_argument('--threads','-t',action='store',required=False,nargs=1,
1466
+ metavar='threads',dest='threads',default=5,type=int,
1467
+ help='number of threads (default: 5)')
1468
+
1469
+
1470
+ versionOptions = optionsCommand.add_parser("version")
1471
+
1472
+ versionOptions.add_argument('--version','-v',action='store_false',default=True,
1473
+ dest='version',
1474
+ help='show software version')
1475
+
1476
+
1477
+ citationOptions = optionsCommand.add_parser("citation")
1478
+
1479
+ citationOptions.add_argument('--citation','-c',action='store_false',default=True,
1480
+ dest='citation',
1481
+ help='show software citation information')
1482
+
1483
+
1484
+ options = optionsCmd.parse_args(args=None if sys.argv[1:] else ['--help'])
1485
+
1486
+ cmdValues = {}
1487
+
1488
+ if options.command == "similarity":
1489
+ cmdValues = {'data': options.data[0:][0],
1490
+ 'format': options.format[0:][0] if isinstance(options.format,list) else options.format,
1491
+ 'output': options.output[0:][0] if isinstance(options.output,list) else options.output,
1492
+ 'length': int(options.length[0:][0]) if isinstance(options.length,list) else int(options.length),
1493
+ 'fragment': int(options.fragment[0:][0]) if isinstance(options.fragment,list) else int(options.fragment),
1494
+ 'fraction': float(options.fraction[0:][0]) if isinstance(options.fraction,list) else float(options.fraction),
1495
+ 'size': float(options.size[0:][0]) if isinstance(options.size,list) else float(options.size),
1496
+ 'mash': options.mash,
1497
+ 'fastani': options.fastani,
1498
+ 'blast': options.blast,
1499
+ 'blastani': options.blastani,
1500
+ 'aln': options.aln,
1501
+ 'evalue': float(options.evalue[0:][0]) if isinstance(options.evalue,list) else float(options.evalue),
1502
+ 'threads': int(options.threads[0:][0]) if isinstance(options.threads,list) else int(options.threads)
1503
+ }
1504
+
1505
+ similarity(cmdValues)
1506
+
1507
+ elif options.command == "filter":
1508
+ cmdValues = {'data': options.data[0:][0],
1509
+ 'format': options.format[0:][0] if isinstance(options.format,list) else options.format,
1510
+ 'output': options.output[0:][0] if isinstance(options.output,list) else options.output,
1511
+ 'minCutoff': int(options.minCutoff[0:][0]) if isinstance(options.minCutoff,list) else int(options.minCutoff),
1512
+ 'maxCutoff': int(options.maxCutoff[0:][0]) if isinstance(options.maxCutoff,list) else int(options.maxCutoff),
1513
+ 'rows': options.rows[0:][0] if isinstance(options.rows,list) else options.rows,
1514
+ 'columns': options.columns[0:][0] if isinstance(options.columns,list) else options.columns,
1515
+ 'threads': int(options.threads[0:][0]) if isinstance(options.threads,list) else int(options.threads)
1516
+ }
1517
+
1518
+ filter(cmdValues)
1519
+
1520
+ elif options.command == "sample":
1521
+ cmdValues = {'data': options.data[0:][0],
1522
+ 'format': options.format[0:][0] if isinstance(options.format,list) else options.format,
1523
+ 'output': options.output[0:][0] if isinstance(options.output,list) else options.output,
1524
+ 'colfreq': float(options.colfreq[0:][0]) if isinstance(options.colfreq,list) else float(options.colfreq),
1525
+ 'rowfreq': float(options.rowfreq[0:][0]) if isinstance(options.rowfreq,list) else float(options.rowfreq)
1526
+ }
1527
+
1528
+ sample(cmdValues)
1529
+
1530
+ elif options.command == "order":
1531
+ cmdValues = {'data': options.data[0:][0],
1532
+ 'format': options.format[0:][0] if isinstance(options.format,list) else options.format,
1533
+ 'output': options.output[0:][0] if isinstance(options.output,list) else options.output,
1534
+ 'rownames': options.rownames[0:][0] if isinstance(options.rownames,list) else options.rownames,
1535
+ 'alphabetic': options.alphabetic,
1536
+ 'threads': int(options.threads[0:][0]) if isinstance(options.threads,list) else int(options.threads)
1537
+ }
1538
+
1539
+ order(cmdValues)
1540
+
1541
+ elif options.command == "rename":
1542
+ cmdValues = {'data': options.data[0:][0],
1543
+ 'format': options.format[0:][0] if isinstance(options.format,list) else options.format,
1544
+ 'output': options.output[0:][0] if isinstance(options.output,list) else options.output,
1545
+ 'newdata': options.newdata[0:][0] if isinstance(options.newdata,list) else options.newdata,
1546
+ 'columns': options.columns[0:] if isinstance(options.columns,list) else options.columns
1547
+ }
1548
+
1549
+ rename(cmdValues)
1550
+
1551
+ elif options.command == "cluster":
1552
+ cmdValues = {'data': options.data[0:][0],
1553
+ 'format': options.format[0:][0] if isinstance(options.format,list) else options.format,
1554
+ 'output': options.output[0:][0] if isinstance(options.output,list) else options.output,
1555
+ 'prefix': options.prefix[0:][0] if isinstance(options.prefix,list) else options.prefix,
1556
+ 'separator': options.separator[0:][0] if isinstance(options.separator,list) else options.separator,
1557
+ 'thresholds': [float(i) for i in options.thresholds[0:]] if isinstance(options.thresholds,list) else float(options.thresholds)
1558
+ }
1559
+
1560
+ cluster(cmdValues)
1561
+
1562
+ elif options.command == "transpose":
1563
+ cmdValues = {'data': options.data[0:][0],
1564
+ 'format': options.format[0:][0] if isinstance(options.format,list) else options.format,
1565
+ 'output': options.output[0:][0] if isinstance(options.output,list) else options.output
1566
+ }
1567
+
1568
+ transpose(cmdValues)
1569
+
1570
+ elif options.command == "table":
1571
+ cmdValues = {'data': options.data[0:][0],
1572
+ 'output': options.output[0:][0] if isinstance(options.output,list) else options.output,
1573
+ 'dsk': options.dsk,
1574
+ 'bifrost': options.bifrost,
1575
+ 'minabundance': int(options.minabundance[0:][0]) if isinstance(options.minabundance,list) else int(options.minabundance),
1576
+ 'maxmemory': int(options.maxmemory[0:][0]) if isinstance(options.maxmemory,list) else int(options.maxmemory),
1577
+ 'length': int(options.length[0:][0]) if isinstance(options.length,list) else int(options.length),
1578
+ 'kmers': options.kmers[0:][0] if isinstance(options.kmers,list) else options.kmers,
1579
+ 'approximate': options.approximate,
1580
+ 'threads': int(options.threads[0:][0]) if isinstance(options.threads,list) else int(options.threads)
1581
+ }
1582
+
1583
+ table(cmdValues)
1584
+
1585
+ elif options.command == "version":
1586
+ printVersion()
1587
+
1588
+ elif options.command == "citation":
1589
+ citation()
1590
+
1591
+ else:
1592
+ sys.exit()
1593
+
1594
+
1595
+ if __name__=="__main__":
1596
+ seqCompareMain()