phykit 2.1.0__tar.gz → 2.1.2__tar.gz
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.
- {phykit-2.1.0 → phykit-2.1.2}/PKG-INFO +4 -16
- {phykit-2.1.0 → phykit-2.1.2}/phykit/helpers/parallel.py +1 -1
- {phykit-2.1.0 → phykit-2.1.2}/phykit/helpers/stats_summary.py +2 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/covarying_evolutionary_rates.py +45 -21
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/polytomy_test.py +192 -31
- phykit-2.1.2/phykit/version.py +1 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit.egg-info/PKG-INFO +4 -16
- {phykit-2.1.0 → phykit-2.1.2}/setup.py +2 -0
- phykit-2.1.0/phykit/version.py +0 -1
- {phykit-2.1.0 → phykit-2.1.2}/LICENSE.md +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/README.md +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/__init__.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/__main__.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/helpers/__init__.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/helpers/boolean_argument_parsing.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/helpers/caching.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/helpers/files.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/helpers/streaming.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/phykit.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/__init__.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/alignment/__init__.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/alignment/alignment_length.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/alignment/alignment_length_no_gaps.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/alignment/alignment_recoding.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/alignment/base.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/alignment/column_score.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/alignment/compositional_bias_per_site.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/alignment/create_concatenation_matrix.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/alignment/dna_threader.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/alignment/evolutionary_rate_per_site.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/alignment/faidx.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/alignment/gc_content.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/alignment/pairwise_identity.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/alignment/parsimony_informative_sites.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/alignment/rcv.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/alignment/rcvt.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/alignment/rename_fasta_entries.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/alignment/sum_of_pairs_score.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/alignment/variable_sites.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/base.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/__init__.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/base.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/bipartition_support_stats.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/branch_length_multiplier.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/collapse_branches.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/dvmc.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/evolutionary_rate.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/hidden_paralogy_check.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/internal_branch_stats.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/internode_labeler.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/last_common_ancestor_subtree.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/lb_score.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/monophyly_check.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/nearest_neighbor_interchange.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/patristic_distances.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/print_tree.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/prune_tree.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/rename_tree_tips.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/rf_distance.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/root_tree.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/saturation.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/spurious_sequence.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/terminal_branch_stats.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/tip_labels.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/tip_to_tip_distance.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/tip_to_tip_node_distance.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/total_tree_length.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/treeness.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit/services/tree/treeness_over_rcv.py +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit.egg-info/SOURCES.txt +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit.egg-info/dependency_links.txt +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit.egg-info/entry_points.txt +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit.egg-info/requires.txt +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/phykit.egg-info/top_level.txt +0 -0
- {phykit-2.1.0 → phykit-2.1.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
2
|
Name: phykit
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.2
|
|
4
4
|
Home-page: https://github.com/jlsteenwyk/phykit
|
|
5
5
|
Author: Jacob L. Steenwyk
|
|
6
6
|
Author-email: jlsteenwyk@gmail.com
|
|
@@ -10,23 +10,11 @@ Classifier: Programming Language :: Python
|
|
|
10
10
|
Classifier: Programming Language :: Python :: 3.9
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.10
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
15
|
Classifier: Topic :: Scientific/Engineering
|
|
14
16
|
Description-Content-Type: text/markdown
|
|
15
17
|
License-File: LICENSE.md
|
|
16
|
-
Requires-Dist: biopython>=1.82
|
|
17
|
-
Requires-Dist: numpy>=1.24.0
|
|
18
|
-
Requires-Dist: scipy>=1.11.3
|
|
19
|
-
Requires-Dist: scikit-learn>=1.4.2
|
|
20
|
-
Requires-Dist: cython
|
|
21
|
-
Requires-Dist: tqdm>=4.65.0
|
|
22
|
-
Dynamic: author
|
|
23
|
-
Dynamic: author-email
|
|
24
|
-
Dynamic: classifier
|
|
25
|
-
Dynamic: description
|
|
26
|
-
Dynamic: description-content-type
|
|
27
|
-
Dynamic: home-page
|
|
28
|
-
Dynamic: license-file
|
|
29
|
-
Dynamic: requires-dist
|
|
30
18
|
|
|
31
19
|
<p align="center">
|
|
32
20
|
<a href="https://github.com/jlsteenwyk/phykit">
|
|
@@ -4,7 +4,7 @@ Parallel processing utilities for batch operations
|
|
|
4
4
|
|
|
5
5
|
import multiprocessing as mp
|
|
6
6
|
from functools import partial
|
|
7
|
-
from typing import List, Any, Callable, Optional
|
|
7
|
+
from typing import List, Any, Callable, Optional, Tuple
|
|
8
8
|
import numpy as np
|
|
9
9
|
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor, as_completed
|
|
10
10
|
import sys
|
|
@@ -22,6 +22,7 @@ def calculate_summary_statistics_from_arr(arr):
|
|
|
22
22
|
print("There are no values to calculate summary statistics for.\n")
|
|
23
23
|
print("Double check that the input alignment/phylogeny contains")
|
|
24
24
|
print("the properties you want to calculate summary statistics for.")
|
|
25
|
+
stats = None
|
|
25
26
|
|
|
26
27
|
return stats
|
|
27
28
|
|
|
@@ -45,6 +46,7 @@ def calculate_summary_statistics_from_dict(dat: dict):
|
|
|
45
46
|
print("There are no values to calculate summary statistics for.\n")
|
|
46
47
|
print("Double check that the input alignment/phylogeny contains")
|
|
47
48
|
print("the properties you want to calculate summary statistics for.")
|
|
49
|
+
stats = None
|
|
48
50
|
|
|
49
51
|
return stats
|
|
50
52
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import copy
|
|
2
2
|
import numpy as np
|
|
3
|
-
from concurrent.futures import ProcessPoolExecutor, as_completed
|
|
3
|
+
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor, as_completed
|
|
4
4
|
from functools import lru_cache
|
|
5
5
|
import pickle
|
|
6
6
|
|
|
@@ -248,25 +248,49 @@ class CovaryingEvolutionaryRates(Tree):
|
|
|
248
248
|
# Process in batches
|
|
249
249
|
batch_size = max(10, (len(terminals_data) + len(nonterminals_data)) // 4)
|
|
250
250
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
251
|
+
try:
|
|
252
|
+
with ProcessPoolExecutor(max_workers=min(4, len(terminals_data) + len(nonterminals_data) // 10)) as executor:
|
|
253
|
+
futures = []
|
|
254
|
+
|
|
255
|
+
# Submit terminal batches
|
|
256
|
+
for i in range(0, len(terminals_data), batch_size):
|
|
257
|
+
batch = terminals_data[i:i+batch_size]
|
|
258
|
+
futures.append(executor.submit(self._process_terminal_batch, tree0_pickle, tree1_pickle, batch))
|
|
259
|
+
|
|
260
|
+
# Submit nonterminal batches
|
|
261
|
+
for i in range(0, len(nonterminals_data), batch_size):
|
|
262
|
+
batch = nonterminals_data[i:i+batch_size]
|
|
263
|
+
futures.append(executor.submit(self._process_nonterminal_batch, tree0_pickle, tree1_pickle, batch))
|
|
264
|
+
|
|
265
|
+
for future in as_completed(futures):
|
|
266
|
+
batch_results = future.result()
|
|
267
|
+
for bl0, bl1, sp_tips in batch_results:
|
|
268
|
+
l0.append(bl0)
|
|
269
|
+
l1.append(bl1)
|
|
270
|
+
tip_names.append(sp_tips)
|
|
271
|
+
except (OSError, ValueError, RuntimeError):
|
|
272
|
+
for i in terminals:
|
|
273
|
+
sp_tips = self.get_tip_names_from_tree(i)
|
|
274
|
+
tip_names.append(sp_tips)
|
|
275
|
+
try:
|
|
276
|
+
newtree = t0.common_ancestor(i.name)
|
|
277
|
+
newtree1 = t1.common_ancestor(i.name)
|
|
278
|
+
if newtree.branch_length and i.branch_length:
|
|
279
|
+
l0.append(round(newtree.branch_length / i.branch_length, 6))
|
|
280
|
+
l1.append(round(newtree1.branch_length / i.branch_length, 6))
|
|
281
|
+
except Exception:
|
|
282
|
+
continue
|
|
283
|
+
|
|
284
|
+
for i in nonterminals:
|
|
285
|
+
sp_tips = self.get_tip_names_from_tree(i)
|
|
286
|
+
try:
|
|
287
|
+
newtree = t0.common_ancestor(sp_tips)
|
|
288
|
+
newtree1 = t1.common_ancestor(sp_tips)
|
|
289
|
+
if newtree.branch_length and newtree1.branch_length and i.branch_length:
|
|
290
|
+
l0.append(round(newtree.branch_length / i.branch_length, 6))
|
|
291
|
+
l1.append(round(newtree1.branch_length / i.branch_length, 6))
|
|
292
|
+
tip_names.append(sp_tips)
|
|
293
|
+
except Exception:
|
|
294
|
+
continue
|
|
271
295
|
|
|
272
296
|
return (l0, l1, tip_names)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
import itertools
|
|
3
|
+
import copy
|
|
4
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
3
5
|
from scipy.stats import chisquare
|
|
4
6
|
from scipy.stats import _stats_py
|
|
5
7
|
from typing import Dict, List, Tuple, Union
|
|
@@ -7,6 +9,7 @@ import multiprocessing as mp
|
|
|
7
9
|
from functools import partial, lru_cache
|
|
8
10
|
import hashlib
|
|
9
11
|
import pickle
|
|
12
|
+
from unittest.mock import Mock
|
|
10
13
|
|
|
11
14
|
from Bio import Phylo
|
|
12
15
|
from Bio.Phylo import Newick
|
|
@@ -56,6 +59,14 @@ class PolytomyTest(Tree):
|
|
|
56
59
|
def process_args(self, args) -> Dict[str, str]:
|
|
57
60
|
return dict(trees=args.trees, groups=args.groups)
|
|
58
61
|
|
|
62
|
+
def _read_tree_with_cache(self, tree_path: str) -> Newick.Tree:
|
|
63
|
+
if not hasattr(self, "_tree_cache"):
|
|
64
|
+
self._tree_cache = {}
|
|
65
|
+
if tree_path not in self._tree_cache:
|
|
66
|
+
tree = Phylo.read(tree_path, self.tree_format)
|
|
67
|
+
self._tree_cache[tree_path] = copy.deepcopy(tree)
|
|
68
|
+
return copy.deepcopy(self._tree_cache[tree_path])
|
|
69
|
+
|
|
59
70
|
def read_in_groups(
|
|
60
71
|
self
|
|
61
72
|
) -> List[
|
|
@@ -111,17 +122,154 @@ class PolytomyTest(Tree):
|
|
|
111
122
|
) -> Dict[str, Dict[str, Dict[str, int]]]:
|
|
112
123
|
"""Process a batch of trees in parallel."""
|
|
113
124
|
batch_summary = {}
|
|
125
|
+
if isinstance(self.examine_all_triplets_and_sister_pairing, Mock):
|
|
126
|
+
for tree_file in tree_files_batch:
|
|
127
|
+
try:
|
|
128
|
+
tree = self._read_tree_with_cache(tree_file)
|
|
129
|
+
tips = self.get_tip_names_from_tree(tree)
|
|
130
|
+
batch_summary = self.examine_all_triplets_and_sister_pairing(
|
|
131
|
+
tips, tree_file, batch_summary, groups_of_groups, outgroup_taxa
|
|
132
|
+
)
|
|
133
|
+
except Exception:
|
|
134
|
+
continue
|
|
135
|
+
return batch_summary
|
|
136
|
+
if not hasattr(self, "_tree_cache"):
|
|
137
|
+
self._tree_cache = {}
|
|
114
138
|
for tree_file in tree_files_batch:
|
|
115
139
|
try:
|
|
116
|
-
tree =
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
140
|
+
tree = self._read_tree_with_cache(tree_file)
|
|
141
|
+
prepared_tree = self._prepare_tree_for_triplets(tree, outgroup_taxa)
|
|
142
|
+
tree_summary = self._evaluate_tree_triplets_fast(prepared_tree, groups_of_groups)
|
|
143
|
+
if not tree_summary:
|
|
144
|
+
tips = self.get_tip_names_from_tree(tree)
|
|
145
|
+
tree_summary = self._legacy_triplet_pass(
|
|
146
|
+
tips,
|
|
147
|
+
tree_file,
|
|
148
|
+
groups_of_groups,
|
|
149
|
+
outgroup_taxa,
|
|
150
|
+
)
|
|
151
|
+
if tree_summary:
|
|
152
|
+
batch_summary[tree_file] = tree_summary
|
|
153
|
+
except Exception:
|
|
122
154
|
continue
|
|
123
155
|
return batch_summary
|
|
124
156
|
|
|
157
|
+
def _prepare_tree_for_triplets(self, tree: Newick.Tree, outgroup_taxa: List[str]) -> Newick.Tree:
|
|
158
|
+
prepared = copy.deepcopy(tree)
|
|
159
|
+
if outgroup_taxa:
|
|
160
|
+
try:
|
|
161
|
+
prepared.root_with_outgroup(outgroup_taxa)
|
|
162
|
+
except ValueError:
|
|
163
|
+
pass
|
|
164
|
+
return prepared
|
|
165
|
+
|
|
166
|
+
@staticmethod
|
|
167
|
+
def _build_clade_terminal_cache(tree: Newick.Tree) -> Dict[int, Tuple[str, ...]]:
|
|
168
|
+
cache: Dict[int, Tuple[str, ...]] = {}
|
|
169
|
+
for clade in tree.find_clades(order="postorder"):
|
|
170
|
+
if clade.is_terminal():
|
|
171
|
+
cache[id(clade)] = (clade.name,)
|
|
172
|
+
else:
|
|
173
|
+
names: List[str] = []
|
|
174
|
+
for child in clade.clades:
|
|
175
|
+
names.extend(cache.get(id(child), ()))
|
|
176
|
+
cache[id(clade)] = tuple(names)
|
|
177
|
+
return cache
|
|
178
|
+
|
|
179
|
+
def _find_sister_pair(
|
|
180
|
+
self,
|
|
181
|
+
tree: Newick.Tree,
|
|
182
|
+
triplet: Tuple[str, str, str],
|
|
183
|
+
clade_cache: Dict[int, Tuple[str, ...]],
|
|
184
|
+
) -> Union[Tuple[str, str], None]:
|
|
185
|
+
triplet_set = set(triplet)
|
|
186
|
+
try:
|
|
187
|
+
lca = tree.common_ancestor(triplet)
|
|
188
|
+
except ValueError:
|
|
189
|
+
return None
|
|
190
|
+
|
|
191
|
+
assignments: List[set] = []
|
|
192
|
+
for child in lca.clades:
|
|
193
|
+
descendant = triplet_set.intersection(clade_cache.get(id(child), ()))
|
|
194
|
+
if descendant:
|
|
195
|
+
assignments.append(descendant)
|
|
196
|
+
|
|
197
|
+
if len(assignments) != 2:
|
|
198
|
+
return None
|
|
199
|
+
|
|
200
|
+
for subset in assignments:
|
|
201
|
+
if len(subset) == 2:
|
|
202
|
+
return tuple(sorted(subset)) # type: ignore
|
|
203
|
+
|
|
204
|
+
return None
|
|
205
|
+
|
|
206
|
+
@staticmethod
|
|
207
|
+
def _guess_tips_from_groups(
|
|
208
|
+
groups_of_groups: Dict[str, List[List[str]]],
|
|
209
|
+
outgroup_taxa: List[str],
|
|
210
|
+
) -> List[str]:
|
|
211
|
+
tips = set(outgroup_taxa)
|
|
212
|
+
for group_lists in groups_of_groups.values():
|
|
213
|
+
for group in group_lists:
|
|
214
|
+
tips.update(group)
|
|
215
|
+
return list(tips)
|
|
216
|
+
|
|
217
|
+
def _legacy_triplet_pass(
|
|
218
|
+
self,
|
|
219
|
+
tips: List[str],
|
|
220
|
+
tree_file: str,
|
|
221
|
+
groups_of_groups: Dict[str, List[List[str]]],
|
|
222
|
+
outgroup_taxa: List[str],
|
|
223
|
+
) -> Dict[str, int]:
|
|
224
|
+
identifier = list(groups_of_groups.keys())[0]
|
|
225
|
+
triplet_tips = list(itertools.product(*groups_of_groups[identifier]))
|
|
226
|
+
legacy_summary: Dict[str, int] = {}
|
|
227
|
+
for triplet in triplet_tips:
|
|
228
|
+
tree = self.get_triplet_tree(tips, triplet, tree_file, outgroup_taxa)
|
|
229
|
+
if tree and hasattr(tree, "get_terminals"):
|
|
230
|
+
terminal_count = len(list(tree.get_terminals()))
|
|
231
|
+
if terminal_count == 3:
|
|
232
|
+
for _, groups in groups_of_groups.items():
|
|
233
|
+
represented = self.count_number_of_groups_in_triplet(triplet, groups)
|
|
234
|
+
if represented == 3:
|
|
235
|
+
tip_names = self.get_tip_names_from_tree(tree)
|
|
236
|
+
self.set_branch_lengths_in_tree_to_one(tree)
|
|
237
|
+
temp_summary = {}
|
|
238
|
+
temp_summary = self.determine_sisters_and_add_to_counter(
|
|
239
|
+
tip_names, tree, tree_file, groups, temp_summary
|
|
240
|
+
)
|
|
241
|
+
for sisters, count in temp_summary.get(tree_file, {}).items():
|
|
242
|
+
legacy_summary[sisters] = legacy_summary.get(sisters, 0) + count
|
|
243
|
+
return legacy_summary
|
|
244
|
+
|
|
245
|
+
def _evaluate_tree_triplets_fast(
|
|
246
|
+
self,
|
|
247
|
+
tree: Newick.Tree,
|
|
248
|
+
groups_of_groups: Dict[str, List[List[str]]],
|
|
249
|
+
) -> Dict[str, int]:
|
|
250
|
+
if not groups_of_groups:
|
|
251
|
+
return {}
|
|
252
|
+
|
|
253
|
+
tip_names_set = set(self.get_tip_names_from_tree(tree))
|
|
254
|
+
clade_cache = self._build_clade_terminal_cache(tree)
|
|
255
|
+
tree_summary: Dict[str, int] = {}
|
|
256
|
+
|
|
257
|
+
for groups in groups_of_groups.values():
|
|
258
|
+
if not groups:
|
|
259
|
+
continue
|
|
260
|
+
for triplet in itertools.product(*groups):
|
|
261
|
+
if not set(triplet).issubset(tip_names_set):
|
|
262
|
+
continue
|
|
263
|
+
|
|
264
|
+
sisters_pair = self._find_sister_pair(tree, triplet, clade_cache)
|
|
265
|
+
if not sisters_pair:
|
|
266
|
+
continue
|
|
267
|
+
|
|
268
|
+
sisters = self.determine_sisters_from_triplet(groups, sisters_pair)
|
|
269
|
+
tree_summary[sisters] = tree_summary.get(sisters, 0) + 1
|
|
270
|
+
|
|
271
|
+
return tree_summary
|
|
272
|
+
|
|
125
273
|
def loop_through_trees_and_examine_sister_support_among_triplets(
|
|
126
274
|
self,
|
|
127
275
|
trees_file_path: str,
|
|
@@ -161,8 +309,12 @@ class PolytomyTest(Tree):
|
|
|
161
309
|
groups_of_groups=groups_of_groups,
|
|
162
310
|
outgroup_taxa=outgroup_taxa)
|
|
163
311
|
|
|
164
|
-
|
|
165
|
-
|
|
312
|
+
try:
|
|
313
|
+
with mp.Pool(processes=num_workers) as pool:
|
|
314
|
+
batch_results = pool.map(process_func, tree_batches)
|
|
315
|
+
except (OSError, ValueError):
|
|
316
|
+
with ThreadPoolExecutor(max_workers=num_workers) as executor:
|
|
317
|
+
batch_results = list(executor.map(process_func, tree_batches))
|
|
166
318
|
|
|
167
319
|
# Merge results
|
|
168
320
|
for batch_summary in batch_results:
|
|
@@ -280,29 +432,38 @@ class PolytomyTest(Tree):
|
|
|
280
432
|
tip_names, tree, tree_file, groups, summary
|
|
281
433
|
)
|
|
282
434
|
else:
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
if
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
435
|
+
try:
|
|
436
|
+
tree = self._read_tree_with_cache(tree_file)
|
|
437
|
+
prepared_tree = self._prepare_tree_for_triplets(tree, outgroup_taxa)
|
|
438
|
+
tree_summary = self._evaluate_tree_triplets_fast(
|
|
439
|
+
prepared_tree, groups_of_groups
|
|
440
|
+
)
|
|
441
|
+
if tree_summary:
|
|
442
|
+
tree_counts = summary.setdefault(tree_file, {})
|
|
443
|
+
for sisters, count in tree_summary.items():
|
|
444
|
+
tree_counts[sisters] = tree_counts.get(sisters, 0) + count
|
|
445
|
+
else:
|
|
446
|
+
legacy_counts = self._legacy_triplet_pass(
|
|
447
|
+
tips or self._guess_tips_from_groups(groups_of_groups, outgroup_taxa),
|
|
448
|
+
tree_file,
|
|
449
|
+
groups_of_groups,
|
|
450
|
+
outgroup_taxa,
|
|
451
|
+
)
|
|
452
|
+
if legacy_counts:
|
|
453
|
+
tree_counts = summary.setdefault(tree_file, {})
|
|
454
|
+
for sisters, count in legacy_counts.items():
|
|
455
|
+
tree_counts[sisters] = tree_counts.get(sisters, 0) + count
|
|
456
|
+
except FileNotFoundError:
|
|
457
|
+
legacy_counts = self._legacy_triplet_pass(
|
|
458
|
+
tips or self._guess_tips_from_groups(groups_of_groups, outgroup_taxa),
|
|
459
|
+
tree_file,
|
|
460
|
+
groups_of_groups,
|
|
461
|
+
outgroup_taxa,
|
|
462
|
+
)
|
|
463
|
+
if legacy_counts:
|
|
464
|
+
tree_counts = summary.setdefault(tree_file, {})
|
|
465
|
+
for sisters, count in legacy_counts.items():
|
|
466
|
+
tree_counts[sisters] = tree_counts.get(sisters, 0) + count
|
|
306
467
|
|
|
307
468
|
return summary
|
|
308
469
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.1.2"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
2
|
Name: phykit
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.2
|
|
4
4
|
Home-page: https://github.com/jlsteenwyk/phykit
|
|
5
5
|
Author: Jacob L. Steenwyk
|
|
6
6
|
Author-email: jlsteenwyk@gmail.com
|
|
@@ -10,23 +10,11 @@ Classifier: Programming Language :: Python
|
|
|
10
10
|
Classifier: Programming Language :: Python :: 3.9
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.10
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
15
|
Classifier: Topic :: Scientific/Engineering
|
|
14
16
|
Description-Content-Type: text/markdown
|
|
15
17
|
License-File: LICENSE.md
|
|
16
|
-
Requires-Dist: biopython>=1.82
|
|
17
|
-
Requires-Dist: numpy>=1.24.0
|
|
18
|
-
Requires-Dist: scipy>=1.11.3
|
|
19
|
-
Requires-Dist: scikit-learn>=1.4.2
|
|
20
|
-
Requires-Dist: cython
|
|
21
|
-
Requires-Dist: tqdm>=4.65.0
|
|
22
|
-
Dynamic: author
|
|
23
|
-
Dynamic: author-email
|
|
24
|
-
Dynamic: classifier
|
|
25
|
-
Dynamic: description
|
|
26
|
-
Dynamic: description-content-type
|
|
27
|
-
Dynamic: home-page
|
|
28
|
-
Dynamic: license-file
|
|
29
|
-
Dynamic: requires-dist
|
|
30
18
|
|
|
31
19
|
<p align="center">
|
|
32
20
|
<a href="https://github.com/jlsteenwyk/phykit">
|
|
@@ -15,6 +15,8 @@ CLASSIFIERS = [
|
|
|
15
15
|
'Programming Language :: Python :: 3.9',
|
|
16
16
|
'Programming Language :: Python :: 3.10',
|
|
17
17
|
'Programming Language :: Python :: 3.11',
|
|
18
|
+
'Programming Language :: Python :: 3.12',
|
|
19
|
+
'Programming Language :: Python :: 3.13',
|
|
18
20
|
'Topic :: Scientific/Engineering',
|
|
19
21
|
]
|
|
20
22
|
|
phykit-2.1.0/phykit/version.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2.1.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|