PyamilySeq 0.5.2__py3-none-any.whl → 0.7.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.
- PyamilySeq/Constants.py +1 -1
- PyamilySeq/PyamilySeq.py +100 -53
- PyamilySeq/PyamilySeq_Genus.py +139 -556
- PyamilySeq/PyamilySeq_Species.py +140 -584
- PyamilySeq/Seq_Combiner.py +26 -7
- PyamilySeq/clusterings.py +362 -0
- PyamilySeq/utils.py +199 -6
- PyamilySeq-0.7.0.dist-info/METADATA +251 -0
- PyamilySeq-0.7.0.dist-info/RECORD +14 -0
- {PyamilySeq-0.5.2.dist-info → PyamilySeq-0.7.0.dist-info}/WHEEL +1 -1
- PyamilySeq/CD-Hit_StORF-Reporter_Cross-Genera_Builder.py +0 -600
- PyamilySeq-0.5.2.dist-info/METADATA +0 -144
- PyamilySeq-0.5.2.dist-info/RECORD +0 -14
- {PyamilySeq-0.5.2.dist-info → PyamilySeq-0.7.0.dist-info}/LICENSE +0 -0
- {PyamilySeq-0.5.2.dist-info → PyamilySeq-0.7.0.dist-info}/entry_points.txt +0 -0
- {PyamilySeq-0.5.2.dist-info → PyamilySeq-0.7.0.dist-info}/top_level.txt +0 -0
PyamilySeq/utils.py
CHANGED
|
@@ -3,8 +3,33 @@ import shutil
|
|
|
3
3
|
import os
|
|
4
4
|
import glob
|
|
5
5
|
import collections
|
|
6
|
+
from tempfile import NamedTemporaryFile
|
|
7
|
+
import sys
|
|
6
8
|
|
|
7
9
|
|
|
10
|
+
################### We are currently fixed using Table 11
|
|
11
|
+
gencode = {
|
|
12
|
+
'ATA':'I', 'ATC':'I', 'ATT':'I', 'ATG':'M',
|
|
13
|
+
'ACA':'T', 'ACC':'T', 'ACG':'T', 'ACT':'T',
|
|
14
|
+
'AAC':'N', 'AAT':'N', 'AAA':'K', 'AAG':'K',
|
|
15
|
+
'AGC':'S', 'AGT':'S', 'AGA':'R', 'AGG':'R',
|
|
16
|
+
'CTA':'L', 'CTC':'L', 'CTG':'L', 'CTT':'L',
|
|
17
|
+
'CCA':'P', 'CCC':'P', 'CCG':'P', 'CCT':'P',
|
|
18
|
+
'CAC':'H', 'CAT':'H', 'CAA':'Q', 'CAG':'Q',
|
|
19
|
+
'CGA':'R', 'CGC':'R', 'CGG':'R', 'CGT':'R',
|
|
20
|
+
'GTA':'V', 'GTC':'V', 'GTG':'V', 'GTT':'V',
|
|
21
|
+
'GCA':'A', 'GCC':'A', 'GCG':'A', 'GCT':'A',
|
|
22
|
+
'GAC':'D', 'GAT':'D', 'GAA':'E', 'GAG':'E',
|
|
23
|
+
'GGA':'G', 'GGC':'G', 'GGG':'G', 'GGT':'G',
|
|
24
|
+
'TCA':'S', 'TCC':'S', 'TCG':'S', 'TCT':'S',
|
|
25
|
+
'TTC':'F', 'TTT':'F', 'TTA':'L', 'TTG':'L',
|
|
26
|
+
'TAC':'Y', 'TAT':'Y', 'TAA':'*', 'TAG':'*',
|
|
27
|
+
'TGC':'C', 'TGT':'C', 'TGA':'*', 'TGG':'W'}
|
|
28
|
+
|
|
29
|
+
def translate_frame(sequence):
|
|
30
|
+
translate = ''.join([gencode.get(sequence[3 * i:3 * i + 3], 'X') for i in range(len(sequence) // 3)])
|
|
31
|
+
return translate
|
|
32
|
+
|
|
8
33
|
def is_tool_installed(tool_name):
|
|
9
34
|
"""Check if a tool is installed and available in PATH."""
|
|
10
35
|
# Check if the tool is in the system PATH
|
|
@@ -30,12 +55,81 @@ def fix_path(path):
|
|
|
30
55
|
return fixed_path
|
|
31
56
|
|
|
32
57
|
|
|
58
|
+
def wrap_sequence(sequence, width=60):
|
|
59
|
+
wrapped_sequence = []
|
|
60
|
+
for i in range(0, len(sequence), width):
|
|
61
|
+
wrapped_sequence.append(sequence[i:i + width])
|
|
62
|
+
return "\n".join(wrapped_sequence)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def read_fasta(fasta_file):
|
|
66
|
+
sequences = {}
|
|
67
|
+
current_sequence = None
|
|
68
|
+
with open(fasta_file, 'r') as file:
|
|
69
|
+
for line in file:
|
|
70
|
+
line = line.strip()
|
|
71
|
+
if not line:
|
|
72
|
+
continue # Skip empty lines
|
|
73
|
+
if line.startswith('>'):
|
|
74
|
+
current_sequence = line[1:] # Remove '>' character
|
|
75
|
+
sequences[current_sequence] = ''
|
|
76
|
+
else:
|
|
77
|
+
sequences[current_sequence] += line
|
|
78
|
+
return sequences
|
|
33
79
|
|
|
34
80
|
|
|
81
|
+
def reorder_dict_by_keys(original_dict, sorted_keys):
|
|
82
|
+
return {k: original_dict[k] for k in sorted_keys}
|
|
83
|
+
def custom_sort_key(k, dict1, dict2):
|
|
84
|
+
return (len(dict1[k]), len(dict2[k]))
|
|
35
85
|
|
|
86
|
+
def sort_keys_by_values(dict1, dict2):
|
|
87
|
+
sorted_keys = sorted(dict1.keys(), key=lambda k: custom_sort_key(k, dict1, dict2), reverse=True)
|
|
88
|
+
return sorted_keys
|
|
36
89
|
|
|
90
|
+
def select_longest_gene(sequences):
|
|
91
|
+
"""Select the longest sequence for each genome."""
|
|
92
|
+
longest_sequences = {}
|
|
93
|
+
for seq_id, sequence in sequences.items():
|
|
94
|
+
genome = seq_id.split('|')[0] # Assuming genome name can be derived from the sequence ID
|
|
95
|
+
if genome not in longest_sequences or len(sequence) > len(longest_sequences[genome][1]):
|
|
96
|
+
longest_sequences[genome] = (seq_id, sequence)
|
|
97
|
+
return longest_sequences
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def run_mafft_on_sequences(options, sequences, output_file):
|
|
101
|
+
#print("Conducting MAFFT alignment.")
|
|
102
|
+
"""Run mafft on the given sequences and write to output file."""
|
|
103
|
+
# Create a temporary input file for mafft
|
|
104
|
+
with NamedTemporaryFile('w', delete=False) as temp_input_file:
|
|
105
|
+
for header, sequence in sequences.items():
|
|
106
|
+
temp_input_file.write(f">{header}\n{sequence}\n")
|
|
107
|
+
temp_input_file_path = temp_input_file.name
|
|
108
|
+
|
|
109
|
+
# Run mafft
|
|
110
|
+
try:
|
|
111
|
+
with open(output_file, 'w') as output_f:
|
|
112
|
+
if options.verbose == True:
|
|
113
|
+
subprocess.run(
|
|
114
|
+
['mafft', '--auto', temp_input_file_path],
|
|
115
|
+
stdout=output_f,
|
|
116
|
+
stderr=sys.stderr,
|
|
117
|
+
check=True
|
|
118
|
+
)
|
|
119
|
+
else:
|
|
120
|
+
subprocess.run(
|
|
121
|
+
['mafft', '--auto', temp_input_file_path],
|
|
122
|
+
stdout=output_f,
|
|
123
|
+
stderr=subprocess.DEVNULL, # Suppress stderr
|
|
124
|
+
check=True
|
|
125
|
+
)
|
|
126
|
+
finally:
|
|
127
|
+
os.remove(temp_input_file_path) # Clean up the temporary file
|
|
37
128
|
|
|
38
|
-
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def read_separate_files(input_dir, name_split, gene_ident, combined_out, translate):
|
|
39
133
|
with open(combined_out, 'w') as combined_out_file:
|
|
40
134
|
for gff_file in glob.glob(os.path.join(input_dir, '*' + name_split)):
|
|
41
135
|
genome_name = os.path.basename(gff_file).split(name_split)[0]
|
|
@@ -45,15 +139,21 @@ def read_separate_files(input_dir, name_split, combined_out):
|
|
|
45
139
|
|
|
46
140
|
gff_features = []
|
|
47
141
|
with open(gff_file, 'r') as file:
|
|
142
|
+
seen_seq_ids = collections.defaultdict(int)
|
|
48
143
|
lines = file.readlines()
|
|
49
144
|
for line in lines:
|
|
50
145
|
line_data = line.split('\t')
|
|
51
146
|
if len(line_data) == 9:
|
|
52
|
-
if line_data[2]
|
|
147
|
+
if any(gene_type in line_data[2] for gene_type in gene_ident):
|
|
53
148
|
contig = line_data[0]
|
|
54
149
|
feature = line_data[2]
|
|
55
150
|
strand = line_data[6]
|
|
56
151
|
start, end = int(line_data[3]), int(line_data[4])
|
|
152
|
+
if seq_id in seen_seq_ids:
|
|
153
|
+
seq_id += '_' + str(seen_seq_ids[seq_id])
|
|
154
|
+
seen_seq_ids[seq_id] + 1
|
|
155
|
+
else:
|
|
156
|
+
seen_seq_ids[seq_id] = 1
|
|
57
157
|
seq_id = line_data[8].split('ID=')[1].split(';')[0]
|
|
58
158
|
gff_features.append((contig, start, end, strand, feature, seq_id))
|
|
59
159
|
fasta_dict = collections.defaultdict(str)
|
|
@@ -81,18 +181,20 @@ def read_separate_files(input_dir, name_split, combined_out):
|
|
|
81
181
|
corrected_stop = max(len(fasta_dict[contig][0]) - int(start - 1), 1)
|
|
82
182
|
full_sequence = fasta_dict[contig][1]
|
|
83
183
|
cds_sequence = full_sequence[corrected_start:corrected_stop]
|
|
84
|
-
|
|
184
|
+
if translate == True:
|
|
185
|
+
cds_sequence = translate_frame(cds_sequence)
|
|
85
186
|
wrapped_sequence = '\n'.join([cds_sequence[i:i + 60] for i in range(0, len(cds_sequence), 60)])
|
|
86
187
|
combined_out_file.write(f">{genome_name}|{seq_id}\n{wrapped_sequence}\n")
|
|
87
188
|
|
|
88
189
|
|
|
89
|
-
def read_combined_files(input_dir, name_split, combined_out):
|
|
190
|
+
def read_combined_files(input_dir, name_split, gene_ident, combined_out, translate):
|
|
90
191
|
with open(combined_out, 'w') as combined_out_file:
|
|
91
192
|
for gff_file in glob.glob(os.path.join(input_dir, '*' + name_split)):
|
|
92
193
|
genome_name = os.path.basename(gff_file).split(name_split)[0]
|
|
93
194
|
fasta_dict = collections.defaultdict(str)
|
|
94
195
|
gff_features = []
|
|
95
196
|
with open(gff_file, 'r') as file:
|
|
197
|
+
seen_seq_ids = collections.defaultdict(int)
|
|
96
198
|
lines = file.readlines()
|
|
97
199
|
fasta_section = False
|
|
98
200
|
for line in lines:
|
|
@@ -108,12 +210,17 @@ def read_combined_files(input_dir, name_split, combined_out):
|
|
|
108
210
|
else:
|
|
109
211
|
line_data = line.split('\t')
|
|
110
212
|
if len(line_data) == 9:
|
|
111
|
-
if line_data[2]
|
|
213
|
+
if any(gene_type in line_data[2] for gene_type in gene_ident):
|
|
112
214
|
contig = line_data[0]
|
|
113
215
|
feature = line_data[2]
|
|
114
216
|
strand = line_data[6]
|
|
115
217
|
start, end = int(line_data[3]), int(line_data[4])
|
|
116
218
|
seq_id = line_data[8].split('ID=')[1].split(';')[0]
|
|
219
|
+
if seq_id in seen_seq_ids:
|
|
220
|
+
seq_id += '_' + str(seen_seq_ids[seq_id])
|
|
221
|
+
seen_seq_ids[seq_id] + 1
|
|
222
|
+
else:
|
|
223
|
+
seen_seq_ids[seq_id] = 1
|
|
117
224
|
gff_features.append((contig, start, end, strand, feature, seq_id))
|
|
118
225
|
|
|
119
226
|
for contig, fasta in fasta_dict.items():
|
|
@@ -132,5 +239,91 @@ def read_combined_files(input_dir, name_split, combined_out):
|
|
|
132
239
|
full_sequence = fasta_dict[contig][1]
|
|
133
240
|
cds_sequence = full_sequence[corrected_start:corrected_stop]
|
|
134
241
|
|
|
242
|
+
if translate == True:
|
|
243
|
+
cds_sequence = translate_frame(cds_sequence)
|
|
135
244
|
wrapped_sequence = '\n'.join([cds_sequence[i:i + 60] for i in range(0, len(cds_sequence), 60)])
|
|
136
|
-
combined_out_file.write(f">{genome_name}|{seq_id}\n{wrapped_sequence}\n")
|
|
245
|
+
combined_out_file.write(f">{genome_name}|{seq_id}\n{wrapped_sequence}\n")
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def read_fasta_files(input_dir, name_split, combined_out, translate):
|
|
249
|
+
with open(combined_out, 'w') as combined_out_file:
|
|
250
|
+
for fasta_file in glob.glob(os.path.join(input_dir, '*' + name_split)):
|
|
251
|
+
genome_name = os.path.basename(fasta_file).split(name_split)[0]
|
|
252
|
+
fasta_dict = collections.defaultdict(str)
|
|
253
|
+
with open(fasta_file, 'r') as file:
|
|
254
|
+
lines = file.readlines()
|
|
255
|
+
for line in lines:
|
|
256
|
+
if line.startswith('>'):
|
|
257
|
+
current_seq = line[1:].split()[0]
|
|
258
|
+
fasta_dict[current_seq] = ''
|
|
259
|
+
else:
|
|
260
|
+
fasta_dict[current_seq] +=line.strip()
|
|
261
|
+
for id, seq in fasta_dict.items():
|
|
262
|
+
if translate == True:
|
|
263
|
+
seq = translate_frame(seq)
|
|
264
|
+
wrapped_sequence = '\n'.join([seq[i:i + 60] for i in range(0, len(seq), 60)])
|
|
265
|
+
combined_out_file.write(f">{genome_name}|{id}\n{wrapped_sequence}\n")
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def write_groups(options,output_dir, key_order, cores, sequences, pangenome_clusters_First_sequences_sorted, combined_pangenome_clusters_Second_sequences):
|
|
269
|
+
# Create output directory if it doesn't exist
|
|
270
|
+
if not os.path.exists(output_dir):
|
|
271
|
+
os.makedirs(output_dir)
|
|
272
|
+
for key_prefix in key_order:
|
|
273
|
+
for key, values in cores.items():
|
|
274
|
+
if any(part in options.write_groups.split(',') for part in key.split('_')):
|
|
275
|
+
if key.startswith(key_prefix):
|
|
276
|
+
for value in values:
|
|
277
|
+
output_filename = f"{key}_{value}.fasta"
|
|
278
|
+
if 'First' in key_prefix:
|
|
279
|
+
sequences_to_write = pangenome_clusters_First_sequences_sorted[value]
|
|
280
|
+
else: # combined_pangenome_clusters_Second_sequences is None if reclustered isn't being used
|
|
281
|
+
sequences_to_write = combined_pangenome_clusters_Second_sequences[value]
|
|
282
|
+
# Write sequences to output file that are in the sequences dictionary
|
|
283
|
+
with open(os.path.join(output_dir, output_filename), 'w') as outfile:
|
|
284
|
+
for header in sequences_to_write:
|
|
285
|
+
if header in sequences:
|
|
286
|
+
outfile.write(f">{header}\n")
|
|
287
|
+
wrapped_sequence = wrap_sequence(sequences[header])
|
|
288
|
+
outfile.write(f"{wrapped_sequence}\n")
|
|
289
|
+
else:
|
|
290
|
+
if options.verbose == True:
|
|
291
|
+
print("Sequence " + header + " Not found in original_fasta file.")
|
|
292
|
+
|
|
293
|
+
def process_gene_families(options, directory, output_file):
|
|
294
|
+
"""Process each gene family file to select the longest sequence per genome and concatenate aligned sequences."""
|
|
295
|
+
concatenated_sequences = {}
|
|
296
|
+
output_file = directory.replace('Gene_Families_Output',output_file)
|
|
297
|
+
|
|
298
|
+
# Iterate over each gene family file
|
|
299
|
+
for gene_file in os.listdir(directory):
|
|
300
|
+
if gene_file.endswith('.fasta'):
|
|
301
|
+
gene_path = os.path.join(directory, gene_file)
|
|
302
|
+
|
|
303
|
+
# Read sequences from the gene family file
|
|
304
|
+
sequences = read_fasta(gene_path)
|
|
305
|
+
|
|
306
|
+
# Select the longest sequence for each genome
|
|
307
|
+
longest_sequences = select_longest_gene(sequences)
|
|
308
|
+
|
|
309
|
+
# Run mafft on the longest sequences
|
|
310
|
+
aligned_file = f"{gene_file}_aligned.fasta"
|
|
311
|
+
run_mafft_on_sequences(options, {seq_id: seq for seq_id, seq in longest_sequences.values()}, aligned_file)
|
|
312
|
+
|
|
313
|
+
# Read aligned sequences and concatenate them
|
|
314
|
+
aligned_sequences = read_fasta(aligned_file)
|
|
315
|
+
for genome, aligned_seq in aligned_sequences.items():
|
|
316
|
+
genome_name = genome.split('|')[0]
|
|
317
|
+
if genome_name not in concatenated_sequences:
|
|
318
|
+
concatenated_sequences[genome_name] = ""
|
|
319
|
+
concatenated_sequences[genome_name] += aligned_seq
|
|
320
|
+
|
|
321
|
+
# Clean up aligned file
|
|
322
|
+
os.remove(aligned_file)
|
|
323
|
+
|
|
324
|
+
# Write the concatenated sequences to the output file
|
|
325
|
+
with open(output_file, 'w') as out:
|
|
326
|
+
for genome, sequence in concatenated_sequences.items():
|
|
327
|
+
out.write(f">{genome}\n")
|
|
328
|
+
wrapped_sequence = wrap_sequence(sequence, 60)
|
|
329
|
+
out.write(f"{wrapped_sequence}\n")
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: PyamilySeq
|
|
3
|
+
Version: 0.7.0
|
|
4
|
+
Summary: PyamilySeq - A a tool to look for sequence-based gene groups identified by clustering methods such as CD-HIT, DIAMOND, BLAST or MMseqs2.
|
|
5
|
+
Home-page: https://github.com/NickJD/PyamilySeq
|
|
6
|
+
Author: Nicholas Dimonaco
|
|
7
|
+
Author-email: nicholas@dimonaco.co.uk
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/NickJD/PyamilySeq/issues
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.6
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
|
|
16
|
+
# PyamilySeq - !BETA!
|
|
17
|
+
**PyamilySeq** is a Python tool for clustering gene sequences into groups based on sequence similarity identified by tools such as CD-HIT, BLAST, DIAMOND or MMseqs2.
|
|
18
|
+
This work is an extension of the gene family / pangenome tool developed for the StORF-Reporter publication in NAR (https://doi.org/10.1093/nar/gkad814).
|
|
19
|
+
|
|
20
|
+
## Features
|
|
21
|
+
- **End-to-End**: PyamilySeq can take a directory of GFF+FASTA files, run CD-HIT for clustering and process the results.
|
|
22
|
+
- **Clustering input**: Supports input from CD-HIT formatted files as well as CSV and TSV edge lists (MMseqs2 and -outfmt 6 from BLAST/DIAMOND).
|
|
23
|
+
- **Reclustering**: Allows for the addition of new sequences post-initial clustering - Ensures continuity of contemporary clustering results and highlights impact of novel gene predictions.
|
|
24
|
+
- **'Genus Mode'**: Unlike other 'pangenome' tools, PyamilySeq can identify gene groups found across multiple genera as unique entities (see below).
|
|
25
|
+
- **Output**: Generates a 'Roary/Panaroo' formatted presence-absence CSV formatted file for downstream analysis.
|
|
26
|
+
- User-define species-/genus-wide gene groups - User has control over grouping parameters (core = 99/95% or min 6 genera etc).
|
|
27
|
+
- Aligns representative sequences using MAFFT.
|
|
28
|
+
- Output concatenated aligned sequences for tree building.
|
|
29
|
+
- Optionally output sequences of each separate identified gene group.
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
PyamilySeq probably requires Python 3.6 or higher. Install using pip:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install PyamilySeq
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Example usage: Below are two examples of running PyamilySeq in its two main modes.
|
|
39
|
+
### 'Full Mode': Will conduct clustering of sequences with CD-HIT as part of PyamilySeq run
|
|
40
|
+
```
|
|
41
|
+
PyamilySeq -run_mode Full -group_mode Species -clustering_format CD-HIT -output_dir .../test_data/testing/Full
|
|
42
|
+
-input_type combined -input_dir .../test_data/genomes -name_split _combined.gff3 -pid 0.95 -len_diff 0.80
|
|
43
|
+
-gpa -a -w 99
|
|
44
|
+
```
|
|
45
|
+
### 'Partial Mode': Will take the output of a sequence clustering.
|
|
46
|
+
```
|
|
47
|
+
PyamilySeq -run_mode Partial -group_mode Species -clustering_format TSV -output_dir .../test_data/Species/testing/Partial
|
|
48
|
+
-cluster_file .../test_data/Species/MMseqs2/combined_Ensmbl_pep_cluster.tsv
|
|
49
|
+
-original_fasta .../test_data/species/combined_Ensmbl_cds.fasta -gpa -a -w 99 -verbose
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
#### Note: '-clustering_format TSV/CSV' requires input to be two in two columns as below (Same format as MMseqs2 tsv) - Genome name and sequence name are separated by '|'.
|
|
53
|
+
```
|
|
54
|
+
Escherichia_coli_110957|ENSB:lL-zIKt-gh0oSno Escherichia_coli_110957|ENSB:lL-zIKt-gh0oSno
|
|
55
|
+
Escherichia_coli_110957|ENSB:lL-zIKt-gh0oSno Escherichia_coli_113290|ENSB:2fj4rJ8e8Z9PNdX
|
|
56
|
+
Escherichia_coli_110957|ENSB:lL-zIKt-gh0oSno Escherichia_coli_b185|ENSB:G_PVe28-ej8q-3S
|
|
57
|
+
Escherichia_coli_110957|ENSB:TIZS9kbTvShDvyX Escherichia_coli_110957|ENSB:TIZS9kbTvShDvyX
|
|
58
|
+
```
|
|
59
|
+
### Example output:
|
|
60
|
+
```
|
|
61
|
+
Running PyamilySeq v0.7.0
|
|
62
|
+
Calculating Groups
|
|
63
|
+
Gene Groups:
|
|
64
|
+
First_core_99: 2682
|
|
65
|
+
First_core_95: 0
|
|
66
|
+
First_core_15: 3789
|
|
67
|
+
First_core_0: 6469
|
|
68
|
+
Total Number of First Gene Groups (Including Singletons): 12940
|
|
69
|
+
Outputting gene_presence_absence file
|
|
70
|
+
Outputting gene group FASTA files
|
|
71
|
+
Processing gene group alignment
|
|
72
|
+
Thank you for using PyamilySeq -- A detailed user manual can be found at https://github.com/NickJD/PyamilySeq
|
|
73
|
+
Please report any issues to: https://github.com/NickJD/PyamilySeq/issues
|
|
74
|
+
```
|
|
75
|
+
## Genus mode:
|
|
76
|
+
### In addition to "Species mode" (see above) which reports gene groups the same as pangenome tools such as Roary and Panaroo, Genus mode reports gene groups identified across multiple genera.
|
|
77
|
+
#### Example:
|
|
78
|
+
```
|
|
79
|
+
PyamilySeq -run_mode Partial -group_mode Genus -clustering_format CD-HIT -output_dir .../test_data/genus/testing/
|
|
80
|
+
-cluster_file .../test_data/genus/CD-HIT/combined_cds_cd-hit_80_60.clstr -gpa
|
|
81
|
+
```
|
|
82
|
+
```commandline
|
|
83
|
+
Running PyamilySeq v0.7.0
|
|
84
|
+
Calculating Groups
|
|
85
|
+
Genus Groups:
|
|
86
|
+
First_genera_1: 28549
|
|
87
|
+
First_genera_2: 12
|
|
88
|
+
First_genera_3: 0
|
|
89
|
+
First_genera_>: 0
|
|
90
|
+
Total Number of First Gene Groups (Including Singletons): 28561
|
|
91
|
+
Outputting gene_presence_absence file
|
|
92
|
+
Thank you for using PyamilySeq -- A detailed user manual can be found at https://github.com/NickJD/PyamilySeq
|
|
93
|
+
Please report any issues to: https://github.com/NickJD/PyamilySeq/issues
|
|
94
|
+
#####
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Reclustering:
|
|
98
|
+
### Reclustering can be used to see where additional sequences/genes lay in relation to a contemporary pangenome/gene grouping.
|
|
99
|
+
```
|
|
100
|
+
PyamilySeq -run_mode Partial -group_mode Species -clustering_format CD-HIT -output_dir .../test_data/species/CD-HIT/testing
|
|
101
|
+
-cluster_file .../test_data/species/CD-HIT/E-coli_extracted_cds_cd-hit_90_60.clstr -gpa
|
|
102
|
+
-reclustered .../test_data/species/CD-HIT/E-coli_extracted_cds_cd-hit_90_60_And_StORFs_cds_90_60.clstr
|
|
103
|
+
```
|
|
104
|
+
#### As can be seen below, the additional sequences recovered by the StORF-Reporter annotation tool have 'extended' contemporary or created entirely new gene groups. 'First' corresponds to the groups identified from the first clustering round and 'Second' for the second. In 'reclustering' mode, First_core_# groups are unaffected thus retaining the initial grouping information.
|
|
105
|
+
```commandline
|
|
106
|
+
Running PyamilySeq v0.7.0
|
|
107
|
+
Calculating Groups
|
|
108
|
+
Gene Groups:
|
|
109
|
+
First_core_99: 69
|
|
110
|
+
First_core_95: 1002
|
|
111
|
+
First_core_15: 4716
|
|
112
|
+
First_core_0: 37960
|
|
113
|
+
extended_core_99: 6
|
|
114
|
+
extended_core_95: 73
|
|
115
|
+
extended_core_15: 331
|
|
116
|
+
extended_core_0: 582
|
|
117
|
+
combined_core_99: 4
|
|
118
|
+
combined_core_95: 88
|
|
119
|
+
combined_core_15: 455
|
|
120
|
+
combined_core_0: 228
|
|
121
|
+
Second_core_99: 0
|
|
122
|
+
Second_core_95: 5
|
|
123
|
+
Second_core_15: 254
|
|
124
|
+
Second_core_0: 3714
|
|
125
|
+
only_Second_core_99: 6
|
|
126
|
+
only_Second_core_95: 364
|
|
127
|
+
only_Second_core_15: 3950
|
|
128
|
+
only_Second_core_0: 31269
|
|
129
|
+
Total Number of First Gene Groups (Including Singletons): 43747
|
|
130
|
+
Total Number of Second Gene Groups (Including Singletons): 66525
|
|
131
|
+
Total Number of First Gene Groups That Had Additional Second Sequences But Not New Genomes: 9593
|
|
132
|
+
Outputting gene_presence_absence file
|
|
133
|
+
Thank you for using PyamilySeq -- A detailed user manual can be found at https://github.com/NickJD/PyamilySeq
|
|
134
|
+
Please report any issues to: https://github.com/NickJD/PyamilySeq/issues
|
|
135
|
+
#####
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## PyamilySeq - Menu:
|
|
139
|
+
### PyamilySeq is separated into two main 'run modes', Full and Partial. They each have their own set of required and optional arguments.
|
|
140
|
+
```
|
|
141
|
+
Running PyamilySeq v0.7.0
|
|
142
|
+
usage: PyamilySeq.py [-h] -run_mode {Full,Partial} -group_mode {Species,Genus} -clustering_format {CD-HIT,TSV,CSV} -output_dir OUTPUT_DIR
|
|
143
|
+
[-input_type {separate,combined}] [-input_dir INPUT_DIR] [-name_split NAME_SPLIT] [-sequence_type {AA,DNA}] [-gene_ident GENE_IDENT]
|
|
144
|
+
[-pid PIDENT] [-len_diff LEN_DIFF] [-mem CLUSTERING_MEMORY] [-t CLUSTERING_THREADS] [-cluster_file CLUSTER_FILE]
|
|
145
|
+
[-reclustered RECLUSTERED] [-seq_tag SEQUENCE_TAG] [-core_groups CORE_GROUPS] [-genus_groups GENUS_GROUPS] [-w WRITE_GROUPS] [-a]
|
|
146
|
+
[-original_fasta ORIGINAL_FASTA] [-gpa] [-verbose] [-v]
|
|
147
|
+
|
|
148
|
+
PyamilySeq v0.7.0: A tool that groups genes into unique clusters.
|
|
149
|
+
|
|
150
|
+
options:
|
|
151
|
+
-h, --help show this help message and exit
|
|
152
|
+
|
|
153
|
+
Required Arguments:
|
|
154
|
+
-run_mode {Full,Partial}
|
|
155
|
+
Run Mode: Should PyamilySeq be run in "Full" or "Partial" mode?
|
|
156
|
+
-group_mode {Species,Genus}
|
|
157
|
+
Group Mode: Should PyamilySeq be run in "Species" or "Genus" mode?
|
|
158
|
+
-clustering_format {CD-HIT,TSV,CSV}
|
|
159
|
+
Clustering format to use: CD-HIT or TSV (MMseqs2, BLAST, DIAMOND) / CSV edge-list file (Node1 Node2).
|
|
160
|
+
-output_dir OUTPUT_DIR
|
|
161
|
+
Directory for all output files.
|
|
162
|
+
|
|
163
|
+
Full-Mode Arguments - Required when "-run_mode Full" is used:
|
|
164
|
+
-input_type {separate,combined}
|
|
165
|
+
Type of input files: 'separate' for separate FASTA and GFF files, 'combined' for GFF files with embedded FASTA sequences.
|
|
166
|
+
-input_dir INPUT_DIR Directory containing GFF/FASTA files.
|
|
167
|
+
-name_split NAME_SPLIT
|
|
168
|
+
substring used to split the filename and extract the genome name ('_combined.gff3' or '.gff').
|
|
169
|
+
-sequence_type {AA,DNA}
|
|
170
|
+
Default - DNA: Should clustering be performed in "DNA" or "AA" mode?
|
|
171
|
+
-gene_ident GENE_IDENT
|
|
172
|
+
Identifier used for extraction of sequences such as
|
|
173
|
+
"misc_RNA,gene,mRNA,CDS,rRNA,tRNA,tmRNA,CRISPR,ncRNA,regulatory_region,oriC,pseudo"
|
|
174
|
+
-pid PIDENT Default 0.95: Pident threshold for clustering.
|
|
175
|
+
-len_diff LEN_DIFF Default 0.80: Minimum length difference between clustered sequences - (-s) threshold for CD-HIT clustering.
|
|
176
|
+
|
|
177
|
+
Clustering Runtime Arguments - Optional when "-run_mode Full" is used:
|
|
178
|
+
-mem CLUSTERING_MEMORY
|
|
179
|
+
Default 4000: Memory to be allocated for clustering (in MBs).
|
|
180
|
+
-t CLUSTERING_THREADS
|
|
181
|
+
Default 4: Threads to be allocated for clustering.
|
|
182
|
+
|
|
183
|
+
Partial-Mode Arguments - Required when "-run_mode Partial" is used:
|
|
184
|
+
-cluster_file CLUSTER_FILE
|
|
185
|
+
Clustering output file containing CD-HIT, TSV or CSV Edge List
|
|
186
|
+
|
|
187
|
+
Grouping Arguments - Use to fine-tune grouping of genes after clustering:
|
|
188
|
+
-reclustered RECLUSTERED
|
|
189
|
+
Currently only works on Partial Mode: Clustering output file from secondary round of clustering.
|
|
190
|
+
-seq_tag SEQUENCE_TAG
|
|
191
|
+
Default - "StORF": Unique identifier to be used to distinguish the second of two rounds of clustered sequences
|
|
192
|
+
-core_groups CORE_GROUPS
|
|
193
|
+
Default - ('99,95,15'): Gene family groups to use for "Species" mode
|
|
194
|
+
-genus_groups GENUS_GROUPS
|
|
195
|
+
Default - ('1,2,3,4,5,6'): Gene family groups to use for "Genus" mode
|
|
196
|
+
|
|
197
|
+
Output Parameters:
|
|
198
|
+
-w WRITE_GROUPS Default - No output: Output sequences of identified groups (provide levels at which to output - Species "-w 99,95" Genus "-w 2,3" -
|
|
199
|
+
Must provide FASTA file with -original_fasta if in Partial run mode.
|
|
200
|
+
-a Default - No output: SLOW! (Only works for Species mode) Output aligned and concatinated sequences of identified groups -provide
|
|
201
|
+
group levels at which to output "-w 99,95" - Must provide FASTA file with -original_fasta in Partialrun mode.
|
|
202
|
+
-original_fasta ORIGINAL_FASTA
|
|
203
|
+
FASTA file to use in conjunction with "-w" or "-con" when running in Partial Mode.
|
|
204
|
+
-gpa Default - False: If selected, a Roary/Panaroo formatted gene_presence_absence.csv will be created - Required for Coinfinder and
|
|
205
|
+
other downstream tools
|
|
206
|
+
|
|
207
|
+
Misc:
|
|
208
|
+
-verbose Default - False: Print out runtime messages
|
|
209
|
+
-v Default - False: Print out version number and exit
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
## Seq-Combiner: This tool is provided to enable the pre-processing of multiple GFF/FASTA files together ready to be clustered by the user.
|
|
217
|
+
### Example:
|
|
218
|
+
```bash
|
|
219
|
+
Seq-Combiner -input_dir .../test_data/genomes -name_split _combined.gff3 -output_dir.../test_data -output_name combine_fasta_seqs.fa -input_type combined
|
|
220
|
+
```
|
|
221
|
+
### Seq-Combiner Menu:
|
|
222
|
+
```
|
|
223
|
+
usage: Seq_Combiner.py [-h] -input_dir INPUT_DIR -input_type {separate,combined,fasta} -name_split NAME_SPLIT -output_dir OUTPUT_DIR -output_name OUTPUT_FILE [-gene_ident GENE_IDENT] [-translate] [-v]
|
|
224
|
+
|
|
225
|
+
Seq-Combiner v0.7.0: A tool to extract sequences from GFF/FASTA files.
|
|
226
|
+
|
|
227
|
+
options:
|
|
228
|
+
-h, --help show this help message and exit
|
|
229
|
+
|
|
230
|
+
Required Arguments:
|
|
231
|
+
-input_dir INPUT_DIR Directory location where the files are located.
|
|
232
|
+
-input_type {separate,combined,fasta}
|
|
233
|
+
Type of input files: "separate" for separate FASTA and GFF files, "combined" for GFF files with embedded FASTA sequences and "fasta" for combining multiple FASTA files together.
|
|
234
|
+
-name_split NAME_SPLIT
|
|
235
|
+
substring used to split the filename and extract the genome name ('_combined.gff3' or '.gff').
|
|
236
|
+
-output_dir OUTPUT_DIR
|
|
237
|
+
Directory for all output files.
|
|
238
|
+
-output_name OUTPUT_FILE
|
|
239
|
+
Output file name.
|
|
240
|
+
|
|
241
|
+
Optional Arguments:
|
|
242
|
+
-gene_ident GENE_IDENT
|
|
243
|
+
Default - "CDS": Identifier used for extraction of sequences such as "misc_RNA,gene,mRNA,CDS,rRNA,tRNA,tmRNA,CRISPR,ncRNA,regulatory_region,oriC,pseudo" - Not compatible with "fasta" input mode.
|
|
244
|
+
-translate Default - False: Translate extracted sequences to their AA counterpart?
|
|
245
|
+
|
|
246
|
+
Misc Arguments:
|
|
247
|
+
-v Print out version number and exit
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
```
|
|
251
|
+
### All example input and output data can be found in the 'test_data' directory.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
PyamilySeq/Constants.py,sha256=RSX5-UuBXOrbEv3ETN415RwqoB6WmNe5eD-p7L15CJA,31
|
|
2
|
+
PyamilySeq/PyamilySeq.py,sha256=wmdOVxxRKqsamsEWgnVVCYETUaYOEQQVYERpClrg4Zw,15203
|
|
3
|
+
PyamilySeq/PyamilySeq_Genus.py,sha256=ZjD61mTW7NgmsfGfFVEXeIZoSCha9PaLtMPnqdTtacU,12413
|
|
4
|
+
PyamilySeq/PyamilySeq_Species.py,sha256=WL6pu8hlGpnemcpu1tLFmlUlPd4vJpQSW4Om5Hclu_k,14438
|
|
5
|
+
PyamilySeq/Seq_Combiner.py,sha256=dPDu6LlT3B-ZDn3wKZ3AeWraDgv2Tub_16l9CLc3tQ0,3353
|
|
6
|
+
PyamilySeq/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
PyamilySeq/clusterings.py,sha256=rcWFv0IiWoS4aUNRjDDwNEL86l1wIKa4vK4htAxy8Hg,18787
|
|
8
|
+
PyamilySeq/utils.py,sha256=-0OZxmX96kOTzms8gnbFBvc5DL6NsqNHNpLpQ4UjNk8,15726
|
|
9
|
+
PyamilySeq-0.7.0.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
10
|
+
PyamilySeq-0.7.0.dist-info/METADATA,sha256=JDhA1JdFaESNwtqzWjRgaA92lrmVFNJWQZUonHgJuvA,13105
|
|
11
|
+
PyamilySeq-0.7.0.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
|
|
12
|
+
PyamilySeq-0.7.0.dist-info/entry_points.txt,sha256=QtXD1tmnLvRAkIpGWZgXm1lfLH8GGeCwxmgoHZaTp98,102
|
|
13
|
+
PyamilySeq-0.7.0.dist-info/top_level.txt,sha256=J6JhugUQTq4rq96yibAlQu3o4KCM9WuYfqr3w1r119M,11
|
|
14
|
+
PyamilySeq-0.7.0.dist-info/RECORD,,
|