eefinder 1.1.2__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.
- eefinder/__init__.py +6 -0
- eefinder/bed.py +206 -0
- eefinder/clean_data.py +64 -0
- eefinder/compare_results.py +30 -0
- eefinder/filter_table.py +135 -0
- eefinder/get_length.py +21 -0
- eefinder/get_taxonomy.py +179 -0
- eefinder/log.py +4 -0
- eefinder/make_database.py +43 -0
- eefinder/prepare_data.py +29 -0
- eefinder/run_message.py +29 -0
- eefinder/scripts/__init__.py +0 -0
- eefinder/scripts/main.py +553 -0
- eefinder/similarity_analysis.py +57 -0
- eefinder/tag_elements.py +66 -0
- eefinder/utils.py +64 -0
- eefinder-1.1.2.dist-info/METADATA +79 -0
- eefinder-1.1.2.dist-info/RECORD +21 -0
- eefinder-1.1.2.dist-info/WHEEL +4 -0
- eefinder-1.1.2.dist-info/entry_points.txt +2 -0
- eefinder-1.1.2.dist-info/licenses/LICENSE +21 -0
eefinder/__init__.py
ADDED
eefinder/bed.py
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import numpy as np
|
|
3
|
+
import shlex
|
|
4
|
+
import subprocess
|
|
5
|
+
import re
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class GetFasta:
|
|
9
|
+
"""
|
|
10
|
+
This function execute the bedtools getfasta.
|
|
11
|
+
|
|
12
|
+
Keyword arguments:
|
|
13
|
+
input_file: input_file, parsed with -in argument.
|
|
14
|
+
bed_file: bed file, genereated along the pipeline
|
|
15
|
+
out_file: output file
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, input_file: str, bed_file: str, out_file: str) -> object:
|
|
19
|
+
self.input_file = input_file
|
|
20
|
+
self.bed_file = bed_file
|
|
21
|
+
self.out_file = out_file
|
|
22
|
+
|
|
23
|
+
self.get_fasta()
|
|
24
|
+
|
|
25
|
+
def get_fasta(self) -> None:
|
|
26
|
+
get_fasta = f"bedtools getfasta -fi {self.input_file} -bed {self.bed_file} -fo {self.out_file}"
|
|
27
|
+
get_fasta = shlex.split(get_fasta)
|
|
28
|
+
cmd_get_fasta = subprocess.Popen(
|
|
29
|
+
get_fasta, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
|
|
30
|
+
)
|
|
31
|
+
cmd_get_fasta.wait()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class GetAnnotBed:
|
|
35
|
+
"""
|
|
36
|
+
Create a bed file that will be used to merge truncated EVEs of the same
|
|
37
|
+
family in the same sense based on a limite length treshold.
|
|
38
|
+
|
|
39
|
+
Keyword arguments:
|
|
40
|
+
blast_tax_info: csv file generated in the get_taxonomy_info function on get_taxonomy.py
|
|
41
|
+
merge_level: genus or family, choose which level going to merge nearby elements
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def __init__(self, blast_tax_info: str, merge_level: str) -> object:
|
|
45
|
+
self.blast_tax_info = blast_tax_info
|
|
46
|
+
self.merge_level = merge_level
|
|
47
|
+
|
|
48
|
+
self.get_annotated_bed()
|
|
49
|
+
|
|
50
|
+
def get_annotated_bed(self) -> None:
|
|
51
|
+
df_blast_tax_info = pd.read_csv(self.blast_tax_info, sep=",")
|
|
52
|
+
df_blast_tax_info["qseqid"] = df_blast_tax_info["qseqid"].str.replace(
|
|
53
|
+
r"\:.*", "", regex=True
|
|
54
|
+
)
|
|
55
|
+
df_blast_tax_info["sseqid"] = (
|
|
56
|
+
df_blast_tax_info["sseqid"]
|
|
57
|
+
+ "|"
|
|
58
|
+
+ df_blast_tax_info["sense"]
|
|
59
|
+
+ "|"
|
|
60
|
+
+ df_blast_tax_info["pident"].astype(str)
|
|
61
|
+
)
|
|
62
|
+
df_blast_tax_info["Family"] = df_blast_tax_info["Family"].fillna("Unknown")
|
|
63
|
+
df_blast_tax_info["Genus"] = df_blast_tax_info["Genus"].fillna("Unknown")
|
|
64
|
+
|
|
65
|
+
if self.merge_level == "genus":
|
|
66
|
+
df_blast_tax_info["formated_name"] = np.where(
|
|
67
|
+
df_blast_tax_info["Genus"] != "Unknown",
|
|
68
|
+
df_blast_tax_info["qseqid"]
|
|
69
|
+
+ "|"
|
|
70
|
+
+ df_blast_tax_info["Family"]
|
|
71
|
+
+ "|"
|
|
72
|
+
+ df_blast_tax_info["Genus"]
|
|
73
|
+
+ "|"
|
|
74
|
+
+ df_blast_tax_info["sense"],
|
|
75
|
+
df_blast_tax_info["qseqid"]
|
|
76
|
+
+ "|"
|
|
77
|
+
+ df_blast_tax_info["sseqid"]
|
|
78
|
+
+ "|"
|
|
79
|
+
+ df_blast_tax_info["Genus"],
|
|
80
|
+
)
|
|
81
|
+
else:
|
|
82
|
+
df_blast_tax_info["formated_name"] = np.where(
|
|
83
|
+
df_blast_tax_info["Family"] != "Unknown",
|
|
84
|
+
df_blast_tax_info["qseqid"]
|
|
85
|
+
+ "|"
|
|
86
|
+
+ df_blast_tax_info["Family"]
|
|
87
|
+
+ "|"
|
|
88
|
+
+ df_blast_tax_info["sense"],
|
|
89
|
+
df_blast_tax_info["qseqid"]
|
|
90
|
+
+ "|"
|
|
91
|
+
+ df_blast_tax_info["sseqid"]
|
|
92
|
+
+ "|"
|
|
93
|
+
+ df_blast_tax_info["Family"],
|
|
94
|
+
)
|
|
95
|
+
bed_blast_info = df_blast_tax_info[
|
|
96
|
+
["formated_name", "qstart", "qend", "sseqid"]
|
|
97
|
+
].copy()
|
|
98
|
+
bed_blast_info = bed_blast_info.sort_values(
|
|
99
|
+
["formated_name", "qstart"], ascending=(True, True)
|
|
100
|
+
)
|
|
101
|
+
bed_blast_info.to_csv(
|
|
102
|
+
f"{self.blast_tax_info}.bed", index=False, header=False, sep="\t"
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class RemoveAnnotation:
|
|
107
|
+
"""
|
|
108
|
+
Remove the annotated information generate into the get_annotated_bed function.
|
|
109
|
+
|
|
110
|
+
Keyword arguments:
|
|
111
|
+
bed_annotated_merged_file: tsv file generated in the merge_bedfile function on bed_merge.py
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
def __init__(self, bed_annotated_merged_file: str) -> object:
|
|
115
|
+
self.bed_annotated_merged_file = bed_annotated_merged_file
|
|
116
|
+
|
|
117
|
+
self.reformat_bed()
|
|
118
|
+
|
|
119
|
+
def reformat_bed(self) -> None:
|
|
120
|
+
df_merge_file = pd.read_csv(
|
|
121
|
+
self.bed_annotated_merged_file, sep="\t", header=None
|
|
122
|
+
)
|
|
123
|
+
df_merge_file.iloc[:, 0] = df_merge_file.iloc[:, 0].str.replace(
|
|
124
|
+
"\|.*", "", regex=True
|
|
125
|
+
)
|
|
126
|
+
df_merge_file.to_csv(
|
|
127
|
+
f"{self.bed_annotated_merged_file}.fmt", index=False, header=False, sep="\t"
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class MergeBed:
|
|
132
|
+
"""
|
|
133
|
+
Execute the bedtools merge.
|
|
134
|
+
|
|
135
|
+
Keyword arguments:
|
|
136
|
+
bed_annotated_file: annotated bed file created at get_annotated_bed function
|
|
137
|
+
limit_merge: Limit of bases to merge regions, parsed with -lm argument
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
def __init__(self, bed_annotated_file: str, limit_merge: int) -> object:
|
|
141
|
+
self.bed_annotated_file = bed_annotated_file
|
|
142
|
+
self.limit_merge = limit_merge
|
|
143
|
+
|
|
144
|
+
self.merge_bed()
|
|
145
|
+
|
|
146
|
+
def merge_bed(self) -> None:
|
|
147
|
+
bed_merge_output = open(f"{self.bed_annotated_file}.merge", "w")
|
|
148
|
+
bed_merge_cmd = f'bedtools merge -d {int(self.limit_merge)} -i {self.bed_annotated_file} -c 4 -o collapse -delim " AND "'
|
|
149
|
+
bed_merge_cmd = shlex.split(bed_merge_cmd)
|
|
150
|
+
bed_merge_process = subprocess.Popen(bed_merge_cmd, stdout=bed_merge_output)
|
|
151
|
+
bed_merge_process.wait()
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class BedFlank:
|
|
155
|
+
"""
|
|
156
|
+
Extract flanking regions of EEs using bedtools slop.
|
|
157
|
+
|
|
158
|
+
Keyword arguments:
|
|
159
|
+
input_file: bed file generated by get_bed function
|
|
160
|
+
lenght_file: lenght file produced by get_length function
|
|
161
|
+
flank_region: desired lenght regions for extraction, parsed from
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
def __init__(self, input_file: str, length_file: str, flank_region: int) -> object:
|
|
165
|
+
self.input_file = input_file
|
|
166
|
+
self.length_file = length_file
|
|
167
|
+
self.flank_region = flank_region
|
|
168
|
+
|
|
169
|
+
self.bedtools_flank()
|
|
170
|
+
|
|
171
|
+
def bedtools_flank(self) -> None:
|
|
172
|
+
with open(f"{self.input_file}.flank", "w") as flank_out:
|
|
173
|
+
bed_flank_cmd = f"bedtools slop -i {self.input_file} -g {self.length_file} -b {str(self.flank_region)}"
|
|
174
|
+
bed_flank_cmd = shlex.split(bed_flank_cmd)
|
|
175
|
+
bed_flank_process = subprocess.Popen(bed_flank_cmd, stdout=flank_out)
|
|
176
|
+
bed_flank_process.wait()
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class GetBed:
|
|
180
|
+
"""
|
|
181
|
+
Create a bed file from fasta file using replace logic.
|
|
182
|
+
|
|
183
|
+
Keyword arguments:
|
|
184
|
+
input_file: fasta file for desired bed file
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
def __init__(self, input_file: str) -> object:
|
|
188
|
+
self.input_file = input_file
|
|
189
|
+
|
|
190
|
+
self.get_bed()
|
|
191
|
+
|
|
192
|
+
def get_bed(self) -> None:
|
|
193
|
+
with open(f"{self.input_file}", "r") as repeat_eves, open(
|
|
194
|
+
f"{self.input_file}.bed", "w"
|
|
195
|
+
) as repeat_eves_bed_out:
|
|
196
|
+
repeat_eves_lines = repeat_eves.readlines()
|
|
197
|
+
for line in repeat_eves_lines:
|
|
198
|
+
if ">" in line:
|
|
199
|
+
line_name = line.replace(">", "")
|
|
200
|
+
line_name = re.sub(":.*", "", line_name).rstrip("\n")
|
|
201
|
+
line_start = re.sub(".*:", "", line)
|
|
202
|
+
line_start = re.sub("-.*", "", line_start).rstrip("\n")
|
|
203
|
+
line_end = re.sub(".*-", "", line).rstrip("\n")
|
|
204
|
+
repeat_eves_bed_out.write(
|
|
205
|
+
f"{line_name}\t{line_start}\t{line_end}\n"
|
|
206
|
+
)
|
eefinder/clean_data.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from Bio import SeqIO
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class RemoveShortSequences:
|
|
5
|
+
"""
|
|
6
|
+
Remove sequences bellow the cutoff threshold.
|
|
7
|
+
|
|
8
|
+
Keyword arguments:
|
|
9
|
+
input_file: input fasta file
|
|
10
|
+
cutoff: cutoff length, parsed by -ln
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, input_file: str, cutoff: int) -> object:
|
|
14
|
+
self.input_file = input_file
|
|
15
|
+
self.cutoff = cutoff
|
|
16
|
+
|
|
17
|
+
self.cut_seq()
|
|
18
|
+
|
|
19
|
+
def cut_seq(self) -> None:
|
|
20
|
+
new_sequences = []
|
|
21
|
+
input_handle = open(self.input_file, "r")
|
|
22
|
+
output_handle = open(self.input_file + ".fmt", "w")
|
|
23
|
+
for record in SeqIO.parse(input_handle, "fasta"):
|
|
24
|
+
if len(record.seq) >= int(self.cutoff):
|
|
25
|
+
new_sequences.append(record)
|
|
26
|
+
SeqIO.write(new_sequences, output_handle, "fasta")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class MaskClean:
|
|
30
|
+
"""
|
|
31
|
+
Remove sequences of EE on regions with a certain % of soft masked bases.
|
|
32
|
+
|
|
33
|
+
Keyword arguments:
|
|
34
|
+
input_file: fasta file, with putative EEs
|
|
35
|
+
m_per: treshold masked percentage value, parsed with -mp argument
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(self, input_file: str, m_per: int) -> object:
|
|
39
|
+
self.input_file = input_file
|
|
40
|
+
self.m_per = m_per
|
|
41
|
+
|
|
42
|
+
self.mask_clean()
|
|
43
|
+
|
|
44
|
+
def mask_clean(self) -> None:
|
|
45
|
+
sequences = {}
|
|
46
|
+
for seq_record in SeqIO.parse(self.input_file, "fasta"):
|
|
47
|
+
sequence = str(seq_record.seq)
|
|
48
|
+
sequence_id = str(seq_record.id)
|
|
49
|
+
if (
|
|
50
|
+
float(
|
|
51
|
+
sequence.count("a")
|
|
52
|
+
+ sequence.count("t")
|
|
53
|
+
+ sequence.count("c")
|
|
54
|
+
+ sequence.count("g")
|
|
55
|
+
+ sequence.count("n")
|
|
56
|
+
+ sequence.count("N")
|
|
57
|
+
)
|
|
58
|
+
/ float(len(sequence))
|
|
59
|
+
) * 100 <= float(self.m_per):
|
|
60
|
+
if sequence_id not in sequences:
|
|
61
|
+
sequences[sequence_id] = sequence
|
|
62
|
+
with open(self.input_file + ".cl", "w+") as output_file:
|
|
63
|
+
for sequence_id, sequence in sequences.items():
|
|
64
|
+
output_file.write(f">{sequence_id}\n{sequence}\n")
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class CompareResults:
|
|
5
|
+
"""
|
|
6
|
+
This function compares 2 blast results, for queries with same ID, only the one
|
|
7
|
+
with the major bitscore is keept. In a final step only queries with tag EE are maintained
|
|
8
|
+
|
|
9
|
+
Keyword arguments:
|
|
10
|
+
vir_result: filtred blast against ee database
|
|
11
|
+
host_result: filtred blast against filter database
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, vir_result: str, host_result: str) -> object:
|
|
15
|
+
self.vir_result = vir_result
|
|
16
|
+
self.host_result = host_result
|
|
17
|
+
|
|
18
|
+
self.compare_results()
|
|
19
|
+
|
|
20
|
+
def compare_results(self) -> None:
|
|
21
|
+
df_vir = pd.read_csv(self.vir_result, sep="\t")
|
|
22
|
+
df_vir["qseqid"] = df_vir["bed_name"]
|
|
23
|
+
df_host = pd.read_csv(self.host_result, sep="\t")
|
|
24
|
+
df_hybrid = pd.concat([df_vir, df_host], ignore_index=True)
|
|
25
|
+
df_hybrid = df_hybrid.sort_values(by=["qseqid", "bitscore"], ascending=False)
|
|
26
|
+
df_hybrid.to_csv(self.host_result + ".concat", sep="\t", index=False)
|
|
27
|
+
df_nr = df_hybrid.drop_duplicates(subset=["qseqid"])
|
|
28
|
+
df_nr.to_csv(self.host_result + ".concat.nr", sep="\t", index=False)
|
|
29
|
+
df_nr_vir = df_nr[df_nr.tag == "EE"]
|
|
30
|
+
df_nr_vir.to_csv(self.host_result + ".concat.nr", sep="\t", index=False)
|
eefinder/filter_table.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import csv
|
|
3
|
+
import os
|
|
4
|
+
import re
|
|
5
|
+
import glob
|
|
6
|
+
import shutil
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class FilterTable:
|
|
10
|
+
"""
|
|
11
|
+
Receives a blastx result and filter based on query ID and ranges of qstart and qend.
|
|
12
|
+
|
|
13
|
+
Keyword arguments:
|
|
14
|
+
blast_result: input blastx result
|
|
15
|
+
rangejunction: range for filter redundant hits
|
|
16
|
+
tag: HOST or EE, tells which blastx is
|
|
17
|
+
out_dir: output directory, parsed by -od
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(
|
|
21
|
+
self, blast_result: str, rangejunction: int, tag: str, out_dir: str
|
|
22
|
+
) -> object:
|
|
23
|
+
self.blast_result = blast_result
|
|
24
|
+
self.rangejunction = rangejunction
|
|
25
|
+
self.tag = tag
|
|
26
|
+
self.out_dir = out_dir
|
|
27
|
+
|
|
28
|
+
self.filter_blast()
|
|
29
|
+
|
|
30
|
+
def filter_blast(self) -> None:
|
|
31
|
+
header_outfmt6 = [
|
|
32
|
+
"qseqid",
|
|
33
|
+
"sseqid",
|
|
34
|
+
"pident",
|
|
35
|
+
"length",
|
|
36
|
+
"mismatch",
|
|
37
|
+
"gapopen",
|
|
38
|
+
"qstart",
|
|
39
|
+
"qend",
|
|
40
|
+
"sstart",
|
|
41
|
+
"send",
|
|
42
|
+
"evalue",
|
|
43
|
+
"bitscore",
|
|
44
|
+
] # creates a blast header output in format = 6
|
|
45
|
+
df = pd.read_csv(
|
|
46
|
+
self.blast_result, sep="\t", header=None, names=header_outfmt6
|
|
47
|
+
).sort_values(by="bitscore", ascending=False)
|
|
48
|
+
df["sense"] = ""
|
|
49
|
+
df["bed_name"] = ""
|
|
50
|
+
df["tag"] = ""
|
|
51
|
+
df["new_qstart"] = df["qstart"]
|
|
52
|
+
df["new_qend"] = df["qend"]
|
|
53
|
+
df.to_csv(self.blast_result + ".csv", sep="\t")
|
|
54
|
+
chunks = df = pd.read_csv(
|
|
55
|
+
f"{self.blast_result}.csv", sep="\t", chunksize=200000
|
|
56
|
+
)
|
|
57
|
+
count = 0
|
|
58
|
+
tmp_path = f"{self.out_dir}/tmp/"
|
|
59
|
+
if os.path.exists(tmp_path) == False:
|
|
60
|
+
os.mkdir(tmp_path)
|
|
61
|
+
for df in chunks:
|
|
62
|
+
df["sense"] = df["sense"].astype(object)
|
|
63
|
+
df.loc[
|
|
64
|
+
df["qstart"].astype(int) > df["qend"].values.astype(int), "sense"
|
|
65
|
+
] = "neg"
|
|
66
|
+
df.loc[
|
|
67
|
+
df["qend"].values.astype(int) > df["qstart"].astype(int), "sense"
|
|
68
|
+
] = "pos"
|
|
69
|
+
df.loc[df["sense"] == "neg", "new_qstart"] = df["qend"]
|
|
70
|
+
df.loc[df["sense"] == "neg", "new_qend"] = df["qstart"]
|
|
71
|
+
df.loc[df["sense"] == "neg", "qstart"] = df["new_qstart"]
|
|
72
|
+
df.loc[df["sense"] == "neg", "qend"] = df["new_qend"]
|
|
73
|
+
df.drop(columns=["new_qstart", "new_qend"], inplace=True)
|
|
74
|
+
if self.tag == "EE":
|
|
75
|
+
df["tag"] = "EE"
|
|
76
|
+
df["bed_name"] = df.apply(
|
|
77
|
+
lambda x: "%s:%s-%s" % (x["qseqid"], x["qstart"], x["qend"]), axis=1
|
|
78
|
+
)
|
|
79
|
+
else:
|
|
80
|
+
df["tag"] = "HOST"
|
|
81
|
+
df["bed_name"] = df["qseqid"]
|
|
82
|
+
pd.options.display.float_format = "{:,.2f}".format
|
|
83
|
+
df["evalue"] = pd.to_numeric(df["evalue"], downcast="float")
|
|
84
|
+
df = df[df.length >= 33]
|
|
85
|
+
header = [
|
|
86
|
+
"qseqid",
|
|
87
|
+
"sseqid",
|
|
88
|
+
"pident",
|
|
89
|
+
"length",
|
|
90
|
+
"mismatch",
|
|
91
|
+
"gapopen",
|
|
92
|
+
"qstart",
|
|
93
|
+
"qend",
|
|
94
|
+
"sstart",
|
|
95
|
+
"send",
|
|
96
|
+
"evalue",
|
|
97
|
+
"bitscore",
|
|
98
|
+
"sense",
|
|
99
|
+
"bed_name",
|
|
100
|
+
"tag",
|
|
101
|
+
]
|
|
102
|
+
df = df[header]
|
|
103
|
+
with open(f"{tmp_path}chunk.{count}.tsv", "w") as chunk_writer:
|
|
104
|
+
df.to_csv(chunk_writer, sep="\t", index=False)
|
|
105
|
+
count += 1
|
|
106
|
+
all_chunks = glob.glob(f"{tmp_path}/*.tsv")
|
|
107
|
+
final_filtred_file = pd.DataFrame()
|
|
108
|
+
chunks_list = []
|
|
109
|
+
for chunk in all_chunks:
|
|
110
|
+
df = pd.read_csv(chunk, sep="\t")
|
|
111
|
+
chunks_list.append(df)
|
|
112
|
+
final_filtred_file = pd.concat(chunks_list, ignore_index=True)
|
|
113
|
+
final_filtred_file["qstart_rng"] = final_filtred_file.qstart.floordiv(
|
|
114
|
+
self.rangejunction
|
|
115
|
+
)
|
|
116
|
+
final_filtred_file["qend_rng"] = final_filtred_file.qend.floordiv(
|
|
117
|
+
self.rangejunction
|
|
118
|
+
)
|
|
119
|
+
final_filtred_file = (
|
|
120
|
+
final_filtred_file.drop_duplicates(subset=["qseqid", "qstart_rng", "sense"])
|
|
121
|
+
.drop_duplicates(subset=["qseqid", "qstart_rng", "sense"])
|
|
122
|
+
.sort_values(by=["qseqid"])
|
|
123
|
+
)
|
|
124
|
+
final_filtred_file.to_csv(
|
|
125
|
+
f"{self.blast_result}.filtred", sep="\t", index=False, columns=header
|
|
126
|
+
)
|
|
127
|
+
if self.tag == "EE":
|
|
128
|
+
final_filtred_file.to_csv(
|
|
129
|
+
f"{self.blast_result}.filtred.bed",
|
|
130
|
+
header=False,
|
|
131
|
+
sep="\t",
|
|
132
|
+
index=False,
|
|
133
|
+
columns=["qseqid", "qstart", "qend"],
|
|
134
|
+
)
|
|
135
|
+
shutil.rmtree(tmp_path, ignore_errors=True)
|
eefinder/get_length.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from Bio import SeqIO
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class GetLength:
|
|
5
|
+
"""
|
|
6
|
+
Creates a length file with the module SeqIO.
|
|
7
|
+
|
|
8
|
+
Keywords arguments:
|
|
9
|
+
input_file: formated genome, generated by cut_seq function
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def __init__(self, input_file: str) -> object:
|
|
13
|
+
self.input_file = input_file
|
|
14
|
+
|
|
15
|
+
self.get_length()
|
|
16
|
+
|
|
17
|
+
def get_length(self) -> None:
|
|
18
|
+
with open(f"{self.input_file}.rn.fmt.lenght", "w") as output_length:
|
|
19
|
+
length_list = []
|
|
20
|
+
for seq_record in SeqIO.parse(self.input_file, "fasta"):
|
|
21
|
+
output_length.write(f"{seq_record.id}\t{str(len(seq_record))}\n")
|
eefinder/get_taxonomy.py
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import re, csv
|
|
3
|
+
from Bio import SeqIO
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class GetTaxonomy:
|
|
7
|
+
"""
|
|
8
|
+
Merge the filtred blast results with taxonomy information, creating a taxonomy signature for being used in get_final_taxonomy.
|
|
9
|
+
|
|
10
|
+
Keyword arguments:
|
|
11
|
+
blast_file: tsv filtred blast results
|
|
12
|
+
tax_file: table with taxonomy and other metadata, parsed with -mt parameter
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, blast_file: str, tax_file: str) -> object:
|
|
16
|
+
self.blast_file = blast_file
|
|
17
|
+
self.tax_file = tax_file
|
|
18
|
+
|
|
19
|
+
self.get_taxonomy()
|
|
20
|
+
|
|
21
|
+
def get_taxonomy(self) -> None:
|
|
22
|
+
df_blast_file = pd.read_csv(self.blast_file, sep="\t")
|
|
23
|
+
df_tax_file = pd.read_csv(self.tax_file)
|
|
24
|
+
df_tax_file.rename(columns={"Accession": "sseqid"}, inplace=True)
|
|
25
|
+
df_merged = pd.merge(df_blast_file, df_tax_file, on="sseqid", how="left")
|
|
26
|
+
df_merged.to_csv(f"{self.blast_file}.tax", index=False, header=True)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class GetFinalTaxonomy:
|
|
30
|
+
"""
|
|
31
|
+
Mount taxonomy for each putative EEs.
|
|
32
|
+
|
|
33
|
+
Keyword arguments:
|
|
34
|
+
bed_formated: bed file, generated by cut_seq function
|
|
35
|
+
taxonomy_info: taxonomy signature generated by get_taxonomy function
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(self, bed_formated: str, taxonomy_info: str) -> object:
|
|
39
|
+
self.bed_formated = bed_formated
|
|
40
|
+
self.taxonomy_info = taxonomy_info
|
|
41
|
+
|
|
42
|
+
self.get_final_taxonomy()
|
|
43
|
+
|
|
44
|
+
def get_final_taxonomy(self) -> None:
|
|
45
|
+
with open(self.bed_formated, "r") as bed_merge_file, open(
|
|
46
|
+
f"{self.bed_formated}.fa.tax", "w"
|
|
47
|
+
) as bed_merge_tax_out:
|
|
48
|
+
bed_merge_tax_list = []
|
|
49
|
+
bed_merge_file_reader = csv.reader(bed_merge_file, delimiter="\t")
|
|
50
|
+
bed_merge_tax_out_writer = csv.writer(bed_merge_tax_out, delimiter="\t")
|
|
51
|
+
bed_merge_tax_out_writer.writerow(
|
|
52
|
+
[
|
|
53
|
+
"Element-ID",
|
|
54
|
+
"Sense",
|
|
55
|
+
"Protein-IDs",
|
|
56
|
+
"Protein-Products",
|
|
57
|
+
"Molecule_type",
|
|
58
|
+
"Family",
|
|
59
|
+
"Genus",
|
|
60
|
+
"Species",
|
|
61
|
+
"Host",
|
|
62
|
+
]
|
|
63
|
+
)
|
|
64
|
+
for line in bed_merge_file_reader:
|
|
65
|
+
element_merged_id = (
|
|
66
|
+
line[0].rstrip("\n")
|
|
67
|
+
+ ":"
|
|
68
|
+
+ line[1].strip("\n")
|
|
69
|
+
+ "-"
|
|
70
|
+
+ line[2].strip("\n")
|
|
71
|
+
)
|
|
72
|
+
if "pos" in line[3]:
|
|
73
|
+
sense = "pos"
|
|
74
|
+
line[3] = re.sub("\|pos", "", line[3]).rstrip("\n")
|
|
75
|
+
elif "neg" in line[3]:
|
|
76
|
+
sense = "neg"
|
|
77
|
+
line[3] = re.sub("\|neg", "", line[3]).rstrip("\n")
|
|
78
|
+
protein_ids = line[3].rstrip("\n")
|
|
79
|
+
with open(self.taxonomy_info, "r") as prot_info:
|
|
80
|
+
prot_info_reader = csv.reader(prot_info, delimiter=",")
|
|
81
|
+
protein_terms = ""
|
|
82
|
+
genus = ""
|
|
83
|
+
species = ""
|
|
84
|
+
host = ""
|
|
85
|
+
if "AND" in protein_ids:
|
|
86
|
+
for line_prot in prot_info_reader:
|
|
87
|
+
protein_ids = re.sub("AND", "|", line[3]).rstrip("\n")
|
|
88
|
+
if line_prot[1].rstrip("\n") in protein_ids:
|
|
89
|
+
if line_prot[19].rstrip("\n") not in protein_terms:
|
|
90
|
+
protein_terms += line_prot[19] + " AND "
|
|
91
|
+
mol_type = line_prot[18]
|
|
92
|
+
family = line_prot[17]
|
|
93
|
+
if line_prot[16].rstrip("\n") not in genus:
|
|
94
|
+
genus += line_prot[16] + " AND "
|
|
95
|
+
if line_prot[15].rstrip("\n") not in species:
|
|
96
|
+
species += line_prot[15] + " AND "
|
|
97
|
+
if line_prot[20].rstrip("\n") not in host:
|
|
98
|
+
host += line_prot[20] + " AND "
|
|
99
|
+
else:
|
|
100
|
+
for line_prot in prot_info_reader:
|
|
101
|
+
if line_prot[1].rstrip("\n") in protein_ids:
|
|
102
|
+
protein_terms = line_prot[19]
|
|
103
|
+
mol_type = line_prot[18]
|
|
104
|
+
family = line_prot[17]
|
|
105
|
+
genus = line_prot[16]
|
|
106
|
+
species = line_prot[15]
|
|
107
|
+
host = line_prot[20]
|
|
108
|
+
|
|
109
|
+
protein_terms = re.sub(r" AND $", "", protein_terms)
|
|
110
|
+
genus = re.sub(r" AND $", "", genus)
|
|
111
|
+
species = re.sub(r" AND $", "", species)
|
|
112
|
+
host = re.sub(r" AND $", "", host)
|
|
113
|
+
if mol_type == "":
|
|
114
|
+
vir_order = "Undefined"
|
|
115
|
+
if family == "":
|
|
116
|
+
family = "Unclassified"
|
|
117
|
+
if genus == "":
|
|
118
|
+
genus = "Unclassified"
|
|
119
|
+
if species == "":
|
|
120
|
+
species = "Unclassified"
|
|
121
|
+
if host == "":
|
|
122
|
+
host = "Undefined"
|
|
123
|
+
|
|
124
|
+
bed_merge_tax_list.append(
|
|
125
|
+
[
|
|
126
|
+
element_merged_id,
|
|
127
|
+
sense,
|
|
128
|
+
protein_ids,
|
|
129
|
+
protein_terms,
|
|
130
|
+
mol_type,
|
|
131
|
+
family,
|
|
132
|
+
genus,
|
|
133
|
+
species,
|
|
134
|
+
host,
|
|
135
|
+
]
|
|
136
|
+
)
|
|
137
|
+
bed_merge_tax_out_writer.writerows(bed_merge_tax_list)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class GetCleanedTaxonomy:
|
|
141
|
+
"""
|
|
142
|
+
Mount cleaned taxonomy for each putative EEs, parsed by -cm parameter.
|
|
143
|
+
|
|
144
|
+
Keyword arguments:
|
|
145
|
+
bed_formated: bed file, generated by cut_seq function
|
|
146
|
+
taxonomy_info: taxonomy signature generated by get_taxonomy function
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
def __init__(self, cleaned_file: str, taxonomy_file: str) -> object:
|
|
150
|
+
self.cleaned_file = cleaned_file
|
|
151
|
+
self.taxonomy_file = taxonomy_file
|
|
152
|
+
|
|
153
|
+
self.get_cleaned_taxonomy()
|
|
154
|
+
|
|
155
|
+
def get_cleaned_taxonomy(self) -> None:
|
|
156
|
+
output_list = [
|
|
157
|
+
[
|
|
158
|
+
"Element-ID",
|
|
159
|
+
"Sense",
|
|
160
|
+
"Protein-IDs",
|
|
161
|
+
"Protein-Products",
|
|
162
|
+
"Molecule_type",
|
|
163
|
+
"Family",
|
|
164
|
+
"Genus",
|
|
165
|
+
"Species",
|
|
166
|
+
"Host",
|
|
167
|
+
]
|
|
168
|
+
]
|
|
169
|
+
|
|
170
|
+
for seq_record in SeqIO.parse(self.cleaned_file, "fasta"):
|
|
171
|
+
with open(self.taxonomy_file, "r") as tax_file:
|
|
172
|
+
taxonomy_file_reader = csv.reader(tax_file, delimiter="\t")
|
|
173
|
+
for line in taxonomy_file_reader:
|
|
174
|
+
if line[0] == seq_record.id:
|
|
175
|
+
output_list.append(line)
|
|
176
|
+
|
|
177
|
+
with open(f"{self.cleaned_file}.tax", "w") as output_file:
|
|
178
|
+
output_file_writer = csv.writer(output_file, delimiter="\t")
|
|
179
|
+
output_file_writer.writerows(output_list)
|
eefinder/log.py
ADDED