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.
@@ -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
+ )