ArraySplitter 1.3.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.
- ArraySplitter/__init__.py +10 -0
- ArraySplitter/classify.py +519 -0
- ArraySplitter/core_functions/__init__.py +0 -0
- ArraySplitter/core_functions/io/__init__.py +0 -0
- ArraySplitter/core_functions/io/block_file.py +156 -0
- ArraySplitter/core_functions/io/fasta_reader.py +51 -0
- ArraySplitter/core_functions/io/satellome_reader.py +20 -0
- ArraySplitter/core_functions/io/tab_file.py +200 -0
- ArraySplitter/core_functions/io/trf_reader.py +79 -0
- ArraySplitter/core_functions/models/__init__.py +0 -0
- ArraySplitter/core_functions/models/trf_model.py +332 -0
- ArraySplitter/core_functions/tools/__init__.py +0 -0
- ArraySplitter/core_functions/tools/anchor_graph.py +726 -0
- ArraySplitter/core_functions/tools/fs_tree.py +210 -0
- ArraySplitter/core_functions/tools/parsers.py +230 -0
- ArraySplitter/core_functions/tools/rotation.py +99 -0
- ArraySplitter/core_functions/tools/sequences.py +20 -0
- ArraySplitter/decompose.py +1784 -0
- ArraySplitter/extract.py +54 -0
- ArraySplitter/main.py +191 -0
- ArraySplitter/rotate.py +35 -0
- arraysplitter-1.3.0.dist-info/METADATA +272 -0
- arraysplitter-1.3.0.dist-info/RECORD +27 -0
- arraysplitter-1.3.0.dist-info/WHEEL +5 -0
- arraysplitter-1.3.0.dist-info/entry_points.txt +2 -0
- arraysplitter-1.3.0.dist-info/licenses/LICENSE +21 -0
- arraysplitter-1.3.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
#
|
|
4
|
+
# @created: 02.02.2024
|
|
5
|
+
# @author: Aleksey Komissarov
|
|
6
|
+
# @contact: ad3002@gmail.com
|
|
7
|
+
|
|
8
|
+
from collections import deque
|
|
9
|
+
|
|
10
|
+
import heapq
|
|
11
|
+
|
|
12
|
+
class WeightedValueHeap:
|
|
13
|
+
def __init__(self):
|
|
14
|
+
self.heap = []
|
|
15
|
+
|
|
16
|
+
def insert(self, weight, value):
|
|
17
|
+
heapq.heappush(self.heap, (weight, value))
|
|
18
|
+
|
|
19
|
+
def pop(self):
|
|
20
|
+
if self.heap:
|
|
21
|
+
return heapq.heappop(self.heap)
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
def peek(self):
|
|
25
|
+
if self.heap:
|
|
26
|
+
return self.heap[0]
|
|
27
|
+
return None
|
|
28
|
+
|
|
29
|
+
def update(
|
|
30
|
+
fs_tree,
|
|
31
|
+
queue,
|
|
32
|
+
nucl,
|
|
33
|
+
fs_x,
|
|
34
|
+
fs_xp,
|
|
35
|
+
current_cid,
|
|
36
|
+
cid,
|
|
37
|
+
cutoff,
|
|
38
|
+
):
|
|
39
|
+
if len(fs_x) > cutoff:
|
|
40
|
+
fs_tree[cid] = (
|
|
41
|
+
cid,
|
|
42
|
+
[nucl],
|
|
43
|
+
fs_x,
|
|
44
|
+
fs_xp,
|
|
45
|
+
None,
|
|
46
|
+
[],
|
|
47
|
+
)
|
|
48
|
+
fs_tree[current_cid][5].append(cid)
|
|
49
|
+
queue.append(cid)
|
|
50
|
+
cid += 1
|
|
51
|
+
return cid
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def build_fs_tree_from_sequence(array, starting_seq_, names_, positions_, cutoff):
|
|
55
|
+
fs_tree = {}
|
|
56
|
+
queue = []
|
|
57
|
+
cid = 0
|
|
58
|
+
seq = starting_seq_
|
|
59
|
+
names = names_[::]
|
|
60
|
+
positions = positions_[::]
|
|
61
|
+
children = []
|
|
62
|
+
fs = (cid, [seq], names, positions, None, children)
|
|
63
|
+
queue.append(cid)
|
|
64
|
+
fs_tree[cid] = fs
|
|
65
|
+
cid += 1
|
|
66
|
+
cutoff = cutoff
|
|
67
|
+
|
|
68
|
+
while queue:
|
|
69
|
+
qcid = queue.pop()
|
|
70
|
+
|
|
71
|
+
fs = fs_tree[qcid]
|
|
72
|
+
|
|
73
|
+
fs_a, fs_c, fs_g, fs_t = [], [], [], []
|
|
74
|
+
fs_ap, fs_cp, fs_gp, fs_tp = [], [], [], []
|
|
75
|
+
|
|
76
|
+
for ii, pos in enumerate(fs[3]):
|
|
77
|
+
current_cid = fs[0]
|
|
78
|
+
seq = fs[1]
|
|
79
|
+
name = fs[2][ii]
|
|
80
|
+
|
|
81
|
+
if pos + 1 == len(array):
|
|
82
|
+
continue
|
|
83
|
+
|
|
84
|
+
nucl = array[pos + 1]
|
|
85
|
+
if nucl == "A":
|
|
86
|
+
fs_a.append(name)
|
|
87
|
+
fs_ap.append(pos + 1)
|
|
88
|
+
elif nucl == "C":
|
|
89
|
+
fs_c.append(name)
|
|
90
|
+
fs_cp.append(pos + 1)
|
|
91
|
+
elif nucl == "G":
|
|
92
|
+
fs_g.append(name)
|
|
93
|
+
fs_gp.append(pos + 1)
|
|
94
|
+
elif nucl == "T":
|
|
95
|
+
fs_t.append(name)
|
|
96
|
+
fs_tp.append(pos + 1)
|
|
97
|
+
|
|
98
|
+
cid = update(
|
|
99
|
+
fs_tree,
|
|
100
|
+
queue,
|
|
101
|
+
"A",
|
|
102
|
+
fs_a,
|
|
103
|
+
fs_ap,
|
|
104
|
+
current_cid,
|
|
105
|
+
cid,
|
|
106
|
+
cutoff,
|
|
107
|
+
)
|
|
108
|
+
cid = update(
|
|
109
|
+
fs_tree,
|
|
110
|
+
queue,
|
|
111
|
+
"C",
|
|
112
|
+
fs_c,
|
|
113
|
+
fs_cp,
|
|
114
|
+
current_cid,
|
|
115
|
+
cid,
|
|
116
|
+
cutoff,
|
|
117
|
+
)
|
|
118
|
+
cid = update(
|
|
119
|
+
fs_tree,
|
|
120
|
+
queue,
|
|
121
|
+
"G",
|
|
122
|
+
fs_g,
|
|
123
|
+
fs_gp,
|
|
124
|
+
current_cid,
|
|
125
|
+
cid,
|
|
126
|
+
cutoff,
|
|
127
|
+
)
|
|
128
|
+
cid = update(
|
|
129
|
+
fs_tree,
|
|
130
|
+
queue,
|
|
131
|
+
"T",
|
|
132
|
+
fs_t,
|
|
133
|
+
fs_tp,
|
|
134
|
+
current_cid,
|
|
135
|
+
cid,
|
|
136
|
+
cutoff,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
return fs_tree
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def iter_fs_tree_from_sequence(array, starting_seq_, names_, positions_, cutoff):
|
|
143
|
+
heap = []
|
|
144
|
+
names = names_[::]
|
|
145
|
+
positions = positions_[::]
|
|
146
|
+
fs = (1, names, positions)
|
|
147
|
+
heapq.heappush(heap, (1, fs))
|
|
148
|
+
cutoff = cutoff
|
|
149
|
+
while heap:
|
|
150
|
+
L, fs = heapq.heappop(heap)
|
|
151
|
+
|
|
152
|
+
fs_a, fs_c, fs_g, fs_t = [], [], [], []
|
|
153
|
+
fs_ap, fs_cp, fs_gp, fs_tp = [], [], [], []
|
|
154
|
+
|
|
155
|
+
for ii, pos in enumerate(fs[2]):
|
|
156
|
+
|
|
157
|
+
name = fs[1][ii]
|
|
158
|
+
|
|
159
|
+
if pos + 1 == len(array):
|
|
160
|
+
continue
|
|
161
|
+
|
|
162
|
+
nucl = array[pos + 1]
|
|
163
|
+
if nucl == "A":
|
|
164
|
+
fs_a.append(name)
|
|
165
|
+
fs_ap.append(pos + 1)
|
|
166
|
+
elif nucl == "C":
|
|
167
|
+
fs_c.append(name)
|
|
168
|
+
fs_cp.append(pos + 1)
|
|
169
|
+
elif nucl == "G":
|
|
170
|
+
fs_g.append(name)
|
|
171
|
+
fs_gp.append(pos + 1)
|
|
172
|
+
elif nucl == "T":
|
|
173
|
+
fs_t.append(name)
|
|
174
|
+
fs_tp.append(pos + 1)
|
|
175
|
+
|
|
176
|
+
if len(fs_a) > cutoff:
|
|
177
|
+
fs = (
|
|
178
|
+
L+1,
|
|
179
|
+
fs_a,
|
|
180
|
+
fs_ap,
|
|
181
|
+
)
|
|
182
|
+
heapq.heappush(heap, (L+1, fs))
|
|
183
|
+
yield fs
|
|
184
|
+
|
|
185
|
+
if len(fs_c) > cutoff:
|
|
186
|
+
fs = (
|
|
187
|
+
L+1,
|
|
188
|
+
fs_c,
|
|
189
|
+
fs_cp,
|
|
190
|
+
)
|
|
191
|
+
heapq.heappush(heap, (L+1, fs))
|
|
192
|
+
yield fs
|
|
193
|
+
|
|
194
|
+
if len(fs_g) > cutoff:
|
|
195
|
+
fs = (
|
|
196
|
+
L+1,
|
|
197
|
+
fs_g,
|
|
198
|
+
fs_gp,
|
|
199
|
+
)
|
|
200
|
+
heapq.heappush(heap, (L+1, fs))
|
|
201
|
+
yield fs
|
|
202
|
+
|
|
203
|
+
if len(fs_t) > cutoff:
|
|
204
|
+
fs = (
|
|
205
|
+
L+1,
|
|
206
|
+
fs_t,
|
|
207
|
+
fs_tp,
|
|
208
|
+
)
|
|
209
|
+
heapq.heappush(heap, (L+1, fs))
|
|
210
|
+
yield fs
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
#
|
|
4
|
+
# Copyright (C) 2007-2009 Aleksey Komissarov ( ad3002@gmail.com )
|
|
5
|
+
# All rights reserved.
|
|
6
|
+
#
|
|
7
|
+
# This software is licensed as described in the file COPYING, which
|
|
8
|
+
# you should have received as part of this distribution.
|
|
9
|
+
"""
|
|
10
|
+
Function for various parsing tasks.
|
|
11
|
+
|
|
12
|
+
- parse_fasta_head(fa_head) -> (P1,P2,P3)
|
|
13
|
+
- parse_chromosome_name(head) -> string
|
|
14
|
+
- trf_parse_line(line) -> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '0', '0']
|
|
15
|
+
- trf_parse_param(line) -> string
|
|
16
|
+
- trf_parse_head(line) -> string
|
|
17
|
+
"""
|
|
18
|
+
import re
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def parse_fasta_head(fa_head):
|
|
22
|
+
"""Parsing fasta head.
|
|
23
|
+
|
|
24
|
+
- head -> (...) -> head parameters
|
|
25
|
+
- Head parsing <= fasta subtypes
|
|
26
|
+
|
|
27
|
+
#TODO: Write other variants
|
|
28
|
+
|
|
29
|
+
Valid examples of head format:
|
|
30
|
+
|
|
31
|
+
- >gi|20928815|ref|NW_003237.1|MmUn_WIFeb01_12612 Mus musculus chromosome Un genomic contig
|
|
32
|
+
- Short variant >(\d*)\t(\S*)
|
|
33
|
+
- >134124-14124\n
|
|
34
|
+
- >134124\n
|
|
35
|
+
- >probe|misat|ref|CAAA01154094|start|991|end|1019
|
|
36
|
+
- Repbase: >TC5A Mariner/Tc1 Caenorhabditis elegans (split by \t <=== settings)
|
|
37
|
+
- >lcl|HmaUn_WGA106_1 Hydra magnipapillata genomic contig, re...
|
|
38
|
+
- plants: >gi|293886233|dbj|BABO01423189.1|
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
head_regexp = re.compile("^>?gi\|(\d+)\|(?:ref|dbj)\|([\w.]+)\|(.*)")
|
|
42
|
+
regexp_obj = head_regexp.search(fa_head)
|
|
43
|
+
|
|
44
|
+
head_regexp_lcl = re.compile("^>?lcl\|(.*?) (.*)")
|
|
45
|
+
regexp_obj_lcl = head_regexp_lcl.search(fa_head)
|
|
46
|
+
|
|
47
|
+
head_regexp_psu = re.compile("^>?psu\|(.*?) (.*)")
|
|
48
|
+
regexp_obj_psu = head_regexp_psu.search(fa_head)
|
|
49
|
+
|
|
50
|
+
head_regexp_short = re.compile("^>(\d+)\t(\S+)")
|
|
51
|
+
regexp_obj_short = head_regexp_short.search(fa_head)
|
|
52
|
+
|
|
53
|
+
head_regexp_comp = re.compile("^>(\d+)-(\d+)")
|
|
54
|
+
regexp_obj_comp = head_regexp_comp.search(fa_head)
|
|
55
|
+
|
|
56
|
+
head_regexp_number = re.compile("^>(\d+)")
|
|
57
|
+
regexp_obj_number = head_regexp_number.search(fa_head)
|
|
58
|
+
|
|
59
|
+
head_regexp_probe_index = re.compile(
|
|
60
|
+
"^>probe\|(.*?)\|ref\|(.*?)\|start\|(\d*?)\|end\|(\d*)"
|
|
61
|
+
)
|
|
62
|
+
regexp_obj_probe_index = head_regexp_probe_index.search(fa_head)
|
|
63
|
+
|
|
64
|
+
head_wgs = re.compile("^>?gi\|(\d+)\|\w+?\|([\w.]+)\|(.*)")
|
|
65
|
+
regexp_obj_wgs = head_wgs.search(fa_head)
|
|
66
|
+
|
|
67
|
+
head_trace = re.compile("^>gnl\|ti\|(\d+) (.*)")
|
|
68
|
+
regexp_obj_trace = head_trace.search(fa_head)
|
|
69
|
+
|
|
70
|
+
if regexp_obj:
|
|
71
|
+
match = regexp_obj.groups()
|
|
72
|
+
return list(match)
|
|
73
|
+
elif regexp_obj_probe_index:
|
|
74
|
+
match_l = []
|
|
75
|
+
|
|
76
|
+
match = regexp_obj_probe_index.groups()
|
|
77
|
+
|
|
78
|
+
gi = "%s_%s_%s" % (match[1], match[2], match[3])
|
|
79
|
+
desc = "%s_%s_%s_%s" % (match[1], match[2], match[3], match[0])
|
|
80
|
+
|
|
81
|
+
match_l.append(gi)
|
|
82
|
+
match_l.append("None")
|
|
83
|
+
match_l.append(desc)
|
|
84
|
+
return list(match_l)
|
|
85
|
+
elif regexp_obj_lcl:
|
|
86
|
+
match_l = []
|
|
87
|
+
match = regexp_obj_lcl.groups()
|
|
88
|
+
match_l.append(match[0])
|
|
89
|
+
match_l.append(match[0])
|
|
90
|
+
match_l.append(match[1])
|
|
91
|
+
return list(match_l)
|
|
92
|
+
elif regexp_obj_psu:
|
|
93
|
+
match_l = []
|
|
94
|
+
match = regexp_obj_psu.groups()
|
|
95
|
+
match_l.append(match[0])
|
|
96
|
+
match_l.append(match[0])
|
|
97
|
+
match_l.append(match[1])
|
|
98
|
+
return list(match_l)
|
|
99
|
+
elif regexp_obj_short:
|
|
100
|
+
match = regexp_obj_short.groups()
|
|
101
|
+
match = list(match)
|
|
102
|
+
match.append("Unknown")
|
|
103
|
+
return list(match)
|
|
104
|
+
elif regexp_obj_comp:
|
|
105
|
+
match = regexp_obj_comp.groups()
|
|
106
|
+
match = list(match)
|
|
107
|
+
match.append("Unknown")
|
|
108
|
+
return list(match)
|
|
109
|
+
elif regexp_obj_number:
|
|
110
|
+
match = regexp_obj_number.groups()
|
|
111
|
+
match = list(match)
|
|
112
|
+
match.append("Unknown")
|
|
113
|
+
match.append("Unknown")
|
|
114
|
+
return list(match)
|
|
115
|
+
elif regexp_obj_wgs:
|
|
116
|
+
match = regexp_obj_wgs.groups()
|
|
117
|
+
return list(match)
|
|
118
|
+
elif regexp_obj_trace:
|
|
119
|
+
match = list(regexp_obj_trace.groups())
|
|
120
|
+
match.append(match[-1])
|
|
121
|
+
return list(match)
|
|
122
|
+
else:
|
|
123
|
+
match = ("Unknown", "Unknown", "Unknown")
|
|
124
|
+
# print "Failed parse sequence head: %s" % (fa_head)
|
|
125
|
+
return list(match)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def parse_chromosome_name(head):
|
|
129
|
+
"""Parse chromosome name in ncbi fasta head"""
|
|
130
|
+
# Head -> (...) -> Chromosome name or ""
|
|
131
|
+
# TODO: write parse_chromosome_name function
|
|
132
|
+
try:
|
|
133
|
+
chr0 = re.compile("chromosome ([^, ]+)").findall(head)
|
|
134
|
+
chr1 = re.compile("chromosome (\S+?),").findall(head)
|
|
135
|
+
chr2 = re.compile("chromosome (\S+?),?").findall(head)
|
|
136
|
+
chr3 = re.compile("chr(\S+?) ").findall(head)
|
|
137
|
+
mit = re.compile(" (mitochon\S+?) ").findall(head)
|
|
138
|
+
if chr0:
|
|
139
|
+
return chr0[0]
|
|
140
|
+
if chr1:
|
|
141
|
+
return chr1[0]
|
|
142
|
+
if chr2:
|
|
143
|
+
return chr2[0]
|
|
144
|
+
if chr3:
|
|
145
|
+
return chr3[0]
|
|
146
|
+
if mit:
|
|
147
|
+
return "MT"
|
|
148
|
+
return "?"
|
|
149
|
+
except:
|
|
150
|
+
return "?"
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def trf_parse_line(line):
|
|
154
|
+
"""Parse TRF data line"""
|
|
155
|
+
line = line.strip()
|
|
156
|
+
groups = re.split("\s", line)
|
|
157
|
+
|
|
158
|
+
if groups and len(groups) == 15:
|
|
159
|
+
return list(groups)
|
|
160
|
+
else:
|
|
161
|
+
print(("Failed parse ta: %s" % (line)))
|
|
162
|
+
return [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "0", "0"]
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def trf_parse_param(line):
|
|
166
|
+
"""TRF parameters line"""
|
|
167
|
+
try:
|
|
168
|
+
res = re.compile("Parameters: ([\d ]*)", re.S).findall(line)[0]
|
|
169
|
+
return res
|
|
170
|
+
except:
|
|
171
|
+
res = "Unknown"
|
|
172
|
+
print(("Failed parse param: %s" % (line)))
|
|
173
|
+
return res
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def trf_parse_head(line):
|
|
177
|
+
"""Parse TRF head"""
|
|
178
|
+
try:
|
|
179
|
+
res = re.compile("Sequence: (.*?)\n", re.S).findall(line)
|
|
180
|
+
res2 = re.compile("Sequence: (.*)", re.S).findall(line)
|
|
181
|
+
if res:
|
|
182
|
+
return res[0]
|
|
183
|
+
if res2:
|
|
184
|
+
return res2[0]
|
|
185
|
+
except:
|
|
186
|
+
res = "Unknown"
|
|
187
|
+
print(("Failed parse head: %s" % (line)))
|
|
188
|
+
return res
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def get_wgs_prefix_from_ref(ref):
|
|
192
|
+
"""Function parse WGS prefix from Genbank ref."""
|
|
193
|
+
reg_exp = "([A-Z]+)"
|
|
194
|
+
res = re.search(reg_exp, ref)
|
|
195
|
+
if res:
|
|
196
|
+
return res.group(0)
|
|
197
|
+
else:
|
|
198
|
+
return "UNKN"
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def get_wgs_prefix_from_head(head):
|
|
202
|
+
"""Function parse WGS prefix from fasta head."""
|
|
203
|
+
reg_exp = "ref.([A-Z]{4,})"
|
|
204
|
+
res = re.search(reg_exp, head)
|
|
205
|
+
if res:
|
|
206
|
+
return res.group(1)
|
|
207
|
+
else:
|
|
208
|
+
reg_exp = "gb.([A-Z]{4,})"
|
|
209
|
+
res = re.search(reg_exp, head)
|
|
210
|
+
if res:
|
|
211
|
+
return res.group(1)
|
|
212
|
+
else:
|
|
213
|
+
return None
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def refine_name(i, trf_obj):
|
|
217
|
+
"""Refine TRF name
|
|
218
|
+
:param trf_obj: TRF object
|
|
219
|
+
:return: TRF object with refined name
|
|
220
|
+
"""
|
|
221
|
+
name = trf_obj.trf_head.split()
|
|
222
|
+
if len(name):
|
|
223
|
+
name = name[0]
|
|
224
|
+
else:
|
|
225
|
+
name = name
|
|
226
|
+
trf_obj.trf_id = f"{name}_{trf_obj.trf_l_ind}_{trf_obj.trf_r_ind}"
|
|
227
|
+
trf_obj.id = f"AGT{(i+1) * 100:013d}"
|
|
228
|
+
trf_obj.trf_consensus = trf_obj.trf_consensus.upper()
|
|
229
|
+
trf_obj.trf_array = trf_obj.trf_array.upper()
|
|
230
|
+
return trf_obj
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
#
|
|
4
|
+
# @created: 20.02.2024
|
|
5
|
+
# @author: Aleksey Komissarov
|
|
6
|
+
# @contact: ad3002@gmail.com
|
|
7
|
+
|
|
8
|
+
from .sequences import get_revcomp
|
|
9
|
+
from collections import Counter
|
|
10
|
+
from tqdm import tqdm
|
|
11
|
+
|
|
12
|
+
def best_kmer_for_start(arrays, k=5):
|
|
13
|
+
k = 5
|
|
14
|
+
kmers = Counter()
|
|
15
|
+
for array in arrays:
|
|
16
|
+
repeat = "".join(array)
|
|
17
|
+
prev_kmer = None
|
|
18
|
+
for i in range(len(repeat)-k+1):
|
|
19
|
+
kmer = repeat[i:i+k]
|
|
20
|
+
if kmer == prev_kmer:
|
|
21
|
+
continue
|
|
22
|
+
prev_kmer = kmer
|
|
23
|
+
kmers[kmer] += 1
|
|
24
|
+
|
|
25
|
+
best_kmer = None
|
|
26
|
+
max_f1 = 0
|
|
27
|
+
for kmer, _ in tqdm(kmers.most_common(100)):
|
|
28
|
+
copy_number = []
|
|
29
|
+
for array in arrays:
|
|
30
|
+
for repeat in array:
|
|
31
|
+
copy_number.append(repeat.count(kmer))
|
|
32
|
+
|
|
33
|
+
n = len(copy_number)
|
|
34
|
+
r = len([x for x in copy_number if x > 0])/n
|
|
35
|
+
p = len([x for x in copy_number if x == 1])/n
|
|
36
|
+
f1 = 2 * (r * p) / (r + p)
|
|
37
|
+
if f1 < 0.5:
|
|
38
|
+
continue
|
|
39
|
+
if f1 > max_f1:
|
|
40
|
+
best_kmer = (r, p, f1, kmer)
|
|
41
|
+
max_f1 = f1
|
|
42
|
+
return max_f1, best_kmer
|
|
43
|
+
|
|
44
|
+
def rotate_arrays(arrays_, starting_kmer=None):
|
|
45
|
+
|
|
46
|
+
if not starting_kmer:
|
|
47
|
+
arrays = []
|
|
48
|
+
for array in arrays_:
|
|
49
|
+
feature1 = array.count("C") < array.count("G")
|
|
50
|
+
feature2 = array.count("A") < array.count("T")
|
|
51
|
+
if feature1 and feature2 or (not feature1 and not feature2):
|
|
52
|
+
if feature1 and feature2:
|
|
53
|
+
arrays.append(array.split())
|
|
54
|
+
else:
|
|
55
|
+
arrays.append(get_revcomp(array).split())
|
|
56
|
+
else:
|
|
57
|
+
if feature1 and not feature2:
|
|
58
|
+
arrays.append(get_revcomp(array).split())
|
|
59
|
+
else:
|
|
60
|
+
arrays.append(array.split())
|
|
61
|
+
max_f1, (r, p, f1, best_kmer) = best_kmer_for_start(arrays)
|
|
62
|
+
else:
|
|
63
|
+
best_kmer = starting_kmer
|
|
64
|
+
best_rev_kmer = get_revcomp(best_kmer)
|
|
65
|
+
arrays = []
|
|
66
|
+
for array in arrays_:
|
|
67
|
+
forward_feature = array.count(best_rev_kmer) < array.count(best_kmer)
|
|
68
|
+
if forward_feature:
|
|
69
|
+
arrays.append(array.split())
|
|
70
|
+
else:
|
|
71
|
+
arrays.append(get_revcomp(array).split())
|
|
72
|
+
|
|
73
|
+
new_arrays = []
|
|
74
|
+
for array in arrays:
|
|
75
|
+
new_array = []
|
|
76
|
+
prev_repeat = ""
|
|
77
|
+
queue = array[::]
|
|
78
|
+
while queue:
|
|
79
|
+
repeat = prev_repeat + queue.pop(0)
|
|
80
|
+
pos = repeat.find(best_kmer, 1)
|
|
81
|
+
if pos == -1:
|
|
82
|
+
prev_repeat = repeat
|
|
83
|
+
continue
|
|
84
|
+
new_array.append(repeat[:pos])
|
|
85
|
+
prev_repeat = repeat[pos:]
|
|
86
|
+
if prev_repeat:
|
|
87
|
+
while True:
|
|
88
|
+
pos = prev_repeat.find(best_kmer, 1)
|
|
89
|
+
if pos == -1:
|
|
90
|
+
new_array.append(prev_repeat)
|
|
91
|
+
break
|
|
92
|
+
new_array.append(prev_repeat[:pos])
|
|
93
|
+
prev_repeat = prev_repeat[pos:]
|
|
94
|
+
|
|
95
|
+
assert "".join(array) == "".join(new_array)
|
|
96
|
+
|
|
97
|
+
new_arrays.append(new_array)
|
|
98
|
+
|
|
99
|
+
return new_arrays
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
#
|
|
4
|
+
# @created: 20.02.2024
|
|
5
|
+
# @author: Aleksey Komissarov
|
|
6
|
+
# @contact: ad3002@gmail.com
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
REVCOMP_DICTIONARY = dict(list(zip("ATCGNatcgn~[] ", "TAGCNtagcn~][ ")))
|
|
10
|
+
|
|
11
|
+
def get_revcomp(sequence):
|
|
12
|
+
"""Return reverse complementary sequence.
|
|
13
|
+
|
|
14
|
+
>>> complementary('AT CG')
|
|
15
|
+
'CGAT'
|
|
16
|
+
|
|
17
|
+
"""
|
|
18
|
+
return "".join(
|
|
19
|
+
REVCOMP_DICTIONARY.get(nucleotide, "") for nucleotide in reversed(sequence)
|
|
20
|
+
)
|