phykit 2.1.84__tar.gz → 2.1.87__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.84 → phykit-2.1.87}/PKG-INFO +1 -1
- {phykit-2.1.84 → phykit-2.1.87}/phykit/cli_registry.py +3 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/phykit.py +71 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/service_factories.py +1 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/__init__.py +1 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/ancestral_reconstruction.py +3 -3
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/base.py +20 -2
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/branch_length_multiplier.py +2 -2
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/character_map.py +2 -2
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/collapse_branches.py +2 -2
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/concordance_asr.py +159 -3
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/cont_map.py +2 -2
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/independent_contrasts.py +2 -2
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/internode_labeler.py +2 -2
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/last_common_ancestor_subtree.py +2 -2
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/ou_shift_detection.py +2 -2
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/ouwie.py +2 -2
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/parsimony_score.py +2 -2
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/phenogram.py +2 -2
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/phylo_logistic.py +2 -2
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/phylogenetic_ordination.py +2 -2
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/phylomorphospace.py +2 -2
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/polytomy_test.py +12 -4
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/print_tree.py +2 -2
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/prune_tree.py +2 -2
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/rate_heterogeneity.py +2 -2
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/rename_tree_tips.py +2 -2
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/root_tree.py +2 -2
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/spectral_discordance.py +3 -3
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/spr.py +2 -2
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/trait_rate_map.py +2 -2
- phykit-2.1.87/phykit/services/tree/transfer_annotations.py +166 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/tree_space.py +2 -2
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/vcv_utils.py +2 -2
- phykit-2.1.87/phykit/version.py +1 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit.egg-info/PKG-INFO +1 -1
- {phykit-2.1.84 → phykit-2.1.87}/phykit.egg-info/SOURCES.txt +1 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit.egg-info/entry_points.txt +3 -0
- phykit-2.1.84/phykit/version.py +0 -1
- {phykit-2.1.84 → phykit-2.1.87}/LICENSE.md +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/README.md +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/__init__.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/__main__.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/errors.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/helpers/__init__.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/helpers/boolean_argument_parsing.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/helpers/caching.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/helpers/circular_layout.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/helpers/color_annotations.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/helpers/discrete_models.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/helpers/files.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/helpers/json_output.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/helpers/parallel.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/helpers/parsimony_utils.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/helpers/pgls_utils.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/helpers/plot_config.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/helpers/quartet_utils.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/helpers/stats_summary.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/helpers/streaming.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/helpers/trait_parsing.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/__init__.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/__init__.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/alignment_entropy.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/alignment_length.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/alignment_length_no_gaps.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/alignment_outlier_taxa.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/alignment_recoding.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/alignment_subsample.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/base.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/column_score.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/composition_per_taxon.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/compositional_bias_per_site.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/create_concatenation_matrix.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/dfoil.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/dna_threader.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/dstatistic.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/evolutionary_rate_per_site.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/faidx.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/gc_content.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/identity_matrix.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/mask_alignment.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/occupancy_filter.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/occupancy_per_taxon.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/pairwise_identity.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/parsimony_informative_sites.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/phylo_gwas.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/plot_alignment_qc.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/rcv.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/rcvt.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/rename_fasta_entries.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/sum_of_pairs_score.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/taxon_groups.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/alignment/variable_sites.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/base.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/bipartition_support_stats.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/consensus_network.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/consensus_tree.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/cophylo.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/covarying_evolutionary_rates.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/density_map.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/discordance_asymmetry.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/dvmc.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/evo_tempo_map.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/evolutionary_rate.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/fit_continuous.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/fit_discrete.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/hidden_paralogy_check.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/hybridization.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/internal_branch_stats.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/kf_distance.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/lb_score.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/ltt.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/monophyly_check.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/nearest_neighbor_interchange.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/neighbor_net.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/network_signal.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/patristic_distances.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/phylo_anova.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/phylo_heatmap.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/phylo_impute.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/phylo_path.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/phylogenetic_glm.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/phylogenetic_regression.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/phylogenetic_signal.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/quartet_network.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/quartet_pie.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/relative_rate_test.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/rf_distance.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/saturation.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/simmap_summary.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/spurious_sequence.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/stochastic_character_map.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/terminal_branch_stats.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/threshold_model.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/tip_labels.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/tip_to_tip_distance.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/tip_to_tip_node_distance.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/total_tree_length.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/trait_correlation.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/treeness.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit/services/tree/treeness_over_rcv.py +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit.egg-info/dependency_links.txt +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit.egg-info/requires.txt +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/phykit.egg-info/top_level.txt +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/setup.cfg +0 -0
- {phykit-2.1.84 → phykit-2.1.87}/setup.py +0 -0
|
@@ -215,6 +215,9 @@ ALIAS_TO_HANDLER: Dict[str, str] = {
|
|
|
215
215
|
"tree_space": "tree_space",
|
|
216
216
|
"tspace": "tree_space",
|
|
217
217
|
"tree_landscape": "tree_space",
|
|
218
|
+
"transfer_annotations": "transfer_annotations",
|
|
219
|
+
"transfer_annot": "transfer_annotations",
|
|
220
|
+
"annotate_tree": "transfer_annotations",
|
|
218
221
|
"tgroups": "taxon_groups",
|
|
219
222
|
"shared_taxa": "taxon_groups",
|
|
220
223
|
"occupancy_filter": "occupancy_filter",
|
|
@@ -308,6 +308,8 @@ class Phykit:
|
|
|
308
308
|
- prune taxa from a phylogeny
|
|
309
309
|
subtree_prune_regraft (alias: spr)
|
|
310
310
|
- generate all SPR rearrangements for a specified subtree
|
|
311
|
+
transfer_annotations (alias: transfer_annot; annotate_tree)
|
|
312
|
+
- transfer node annotations between trees (e.g., wASTRAL to RAxML/IQ-TREE)
|
|
311
313
|
relative_rate_test (alias: rrt; tajima_rrt)
|
|
312
314
|
- Tajima's relative rate test for equal evolutionary
|
|
313
315
|
rates between two ingroup lineages
|
|
@@ -2823,6 +2825,14 @@ class Phykit:
|
|
|
2823
2825
|
--plot output path for concordance
|
|
2824
2826
|
ASR plot
|
|
2825
2827
|
|
|
2828
|
+
--plot-uncertainty output path for uncertainty
|
|
2829
|
+
plot showing the distribution
|
|
2830
|
+
of ancestral estimates across
|
|
2831
|
+
gene trees (distribution
|
|
2832
|
+
method) or concordance
|
|
2833
|
+
sources (weighted method)
|
|
2834
|
+
as violin + boxplots
|
|
2835
|
+
|
|
2826
2836
|
--missing-taxa how to handle taxa mismatches:
|
|
2827
2837
|
shared (default) or error
|
|
2828
2838
|
|
|
@@ -2900,6 +2910,10 @@ class Phykit:
|
|
|
2900
2910
|
"--plot", type=str, required=False, default=None,
|
|
2901
2911
|
help=SUPPRESS, metavar=""
|
|
2902
2912
|
)
|
|
2913
|
+
parser.add_argument(
|
|
2914
|
+
"--plot-uncertainty", type=str, required=False, default=None,
|
|
2915
|
+
help=SUPPRESS, metavar=""
|
|
2916
|
+
)
|
|
2903
2917
|
parser.add_argument(
|
|
2904
2918
|
"--missing-taxa", type=str, required=False, default="shared",
|
|
2905
2919
|
choices=["error", "shared"], help=SUPPRESS, metavar=""
|
|
@@ -6821,6 +6835,59 @@ class Phykit:
|
|
|
6821
6835
|
_add_json_argument(parser)
|
|
6822
6836
|
_run_service(parser, argv, Spr)
|
|
6823
6837
|
|
|
6838
|
+
@staticmethod
|
|
6839
|
+
def transfer_annotations(argv):
|
|
6840
|
+
parser = _new_parser(
|
|
6841
|
+
description=textwrap.dedent(
|
|
6842
|
+
f"""\
|
|
6843
|
+
{help_header}
|
|
6844
|
+
|
|
6845
|
+
Transfer internal node annotations from one tree onto
|
|
6846
|
+
another. Matches nodes by bipartition (descendant taxa
|
|
6847
|
+
set) and copies the annotation labels.
|
|
6848
|
+
|
|
6849
|
+
Typical use case: transfer wASTRAL support annotations
|
|
6850
|
+
(q1/q2/q3, pp1, f1, etc.) from an annotated ASTRAL
|
|
6851
|
+
tree onto a branch-length-optimized topology from
|
|
6852
|
+
RAxML-NG, IQ-TREE, or any other tool. The output tree
|
|
6853
|
+
has the target's branch lengths with the source's
|
|
6854
|
+
annotations.
|
|
6855
|
+
|
|
6856
|
+
Aliases:
|
|
6857
|
+
transfer_annotations, transfer_annot, annotate_tree
|
|
6858
|
+
Command line interfaces:
|
|
6859
|
+
pk_transfer_annotations, pk_transfer_annot, pk_annotate_tree
|
|
6860
|
+
|
|
6861
|
+
Usage:
|
|
6862
|
+
phykit transfer_annotations --source <annotated_tree>
|
|
6863
|
+
--target <branch_length_tree> [-o/--output <file>]
|
|
6864
|
+
[--json]
|
|
6865
|
+
|
|
6866
|
+
Options
|
|
6867
|
+
=====================================================
|
|
6868
|
+
--source annotated tree file (e.g.,
|
|
6869
|
+
wASTRAL output with
|
|
6870
|
+
--support 3)
|
|
6871
|
+
|
|
6872
|
+
--target target tree file with
|
|
6873
|
+
branch lengths to keep
|
|
6874
|
+
(e.g., RAxML-NG or
|
|
6875
|
+
IQ-TREE output)
|
|
6876
|
+
|
|
6877
|
+
-o/--output output file for the
|
|
6878
|
+
annotated tree (default:
|
|
6879
|
+
target file + ".annotated")
|
|
6880
|
+
|
|
6881
|
+
--json output results as JSON
|
|
6882
|
+
"""
|
|
6883
|
+
),
|
|
6884
|
+
)
|
|
6885
|
+
parser.add_argument("--source", type=str, required=True, help=SUPPRESS, metavar="")
|
|
6886
|
+
parser.add_argument("--target", type=str, required=True, help=SUPPRESS, metavar="")
|
|
6887
|
+
parser.add_argument("-o", "--output", type=str, default=None, help=SUPPRESS, metavar="")
|
|
6888
|
+
_add_json_argument(parser)
|
|
6889
|
+
_run_service(parser, argv, TransferAnnotations)
|
|
6890
|
+
|
|
6824
6891
|
@staticmethod
|
|
6825
6892
|
def relative_rate_test(argv):
|
|
6826
6893
|
parser = _new_parser(
|
|
@@ -9190,6 +9257,10 @@ def subtree_prune_regraft(argv=None):
|
|
|
9190
9257
|
Phykit.subtree_prune_regraft(sys.argv[1:])
|
|
9191
9258
|
|
|
9192
9259
|
|
|
9260
|
+
def transfer_annotations(argv=None):
|
|
9261
|
+
Phykit.transfer_annotations(sys.argv[1:])
|
|
9262
|
+
|
|
9263
|
+
|
|
9193
9264
|
def relative_rate_test(argv=None):
|
|
9194
9265
|
Phykit.relative_rate_test(sys.argv[1:])
|
|
9195
9266
|
|
|
@@ -108,6 +108,7 @@ RobinsonFouldsDistance = _LazyServiceFactory("phykit.services.tree.rf_distance",
|
|
|
108
108
|
RootTree = _LazyServiceFactory("phykit.services.tree.root_tree", "RootTree")
|
|
109
109
|
Saturation = _LazyServiceFactory("phykit.services.tree.saturation", "Saturation")
|
|
110
110
|
Spr = _LazyServiceFactory("phykit.services.tree.spr", "Spr")
|
|
111
|
+
TransferAnnotations = _LazyServiceFactory("phykit.services.tree.transfer_annotations", "TransferAnnotations")
|
|
111
112
|
SpuriousSequence = _LazyServiceFactory("phykit.services.tree.spurious_sequence", "SpuriousSequence")
|
|
112
113
|
TerminalBranchStats = _LazyServiceFactory("phykit.services.tree.terminal_branch_stats", "TerminalBranchStats")
|
|
113
114
|
TipLabels = _LazyServiceFactory("phykit.services.tree.tip_labels", "TipLabels")
|
|
@@ -48,6 +48,7 @@ _EXPORTS = {
|
|
|
48
48
|
"TipToTipDistance": "tip_to_tip_distance",
|
|
49
49
|
"TipToTipNodeDistance": "tip_to_tip_node_distance",
|
|
50
50
|
"TotalTreeLength": "total_tree_length",
|
|
51
|
+
"TransferAnnotations": "transfer_annotations",
|
|
51
52
|
"Treeness": "treeness",
|
|
52
53
|
"ThresholdModel": "threshold_model",
|
|
53
54
|
"TreenessOverRCV": "treeness_over_rcv",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import copy
|
|
2
1
|
import math
|
|
2
|
+
import pickle
|
|
3
3
|
import sys
|
|
4
4
|
from typing import Dict, List, Tuple
|
|
5
5
|
|
|
@@ -78,7 +78,7 @@ class AncestralReconstruction(Tree):
|
|
|
78
78
|
x = np.array([trait_values[name] for name in ordered_names])
|
|
79
79
|
|
|
80
80
|
# Prune tree to shared taxa
|
|
81
|
-
tree_copy =
|
|
81
|
+
tree_copy = pickle.loads(pickle.dumps(tree, protocol=pickle.HIGHEST_PROTOCOL))
|
|
82
82
|
tip_names_in_tree = [t.name for t in tree_copy.get_terminals()]
|
|
83
83
|
tips_to_prune = [t for t in tip_names_in_tree if t not in trait_values]
|
|
84
84
|
if tips_to_prune:
|
|
@@ -1368,7 +1368,7 @@ class AncestralReconstruction(Tree):
|
|
|
1368
1368
|
trait_name = "trait"
|
|
1369
1369
|
|
|
1370
1370
|
# Prune tree to shared taxa
|
|
1371
|
-
tree_copy =
|
|
1371
|
+
tree_copy = pickle.loads(pickle.dumps(tree, protocol=pickle.HIGHEST_PROTOCOL))
|
|
1372
1372
|
tip_names_in_tree = [t.name for t in tree_copy.get_terminals()]
|
|
1373
1373
|
tips_to_prune = [t for t in tip_names_in_tree if t not in tip_states]
|
|
1374
1374
|
if tips_to_prune:
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import copy
|
|
2
|
+
import pickle
|
|
2
3
|
from typing import List
|
|
3
4
|
from functools import lru_cache
|
|
4
5
|
import os
|
|
@@ -80,12 +81,29 @@ class Tree(BaseService):
|
|
|
80
81
|
def read_reference_tree_file(self):
|
|
81
82
|
return self._read_tree_with_error(self.reference, "reference")
|
|
82
83
|
|
|
84
|
+
@staticmethod
|
|
85
|
+
def _fast_copy(tree):
|
|
86
|
+
"""Copy a tree using pickle instead of deepcopy.
|
|
87
|
+
|
|
88
|
+
Avoids RecursionError on deeply nested trees (e.g., ladder-like
|
|
89
|
+
topologies with hundreds of cascading bifurcations) where
|
|
90
|
+
copy.deepcopy exceeds Python's default recursion limit.
|
|
91
|
+
Falls back to deepcopy for objects that can't be pickled (e.g.,
|
|
92
|
+
mocks in unit tests).
|
|
93
|
+
"""
|
|
94
|
+
try:
|
|
95
|
+
return pickle.loads(pickle.dumps(tree, protocol=pickle.HIGHEST_PROTOCOL))
|
|
96
|
+
except (pickle.PicklingError, TypeError, AttributeError):
|
|
97
|
+
return copy.deepcopy(tree)
|
|
98
|
+
|
|
83
99
|
def _read_tree_with_error(self, tree_path: str, attr_name: str):
|
|
84
100
|
try:
|
|
85
101
|
file_hash = self._get_file_hash(tree_path)
|
|
86
102
|
tree = self._cached_tree_read(tree_path, self.tree_format, file_hash)
|
|
87
|
-
# Return a
|
|
88
|
-
|
|
103
|
+
# Return a copy to prevent modifications to the cached tree.
|
|
104
|
+
# Uses pickle instead of deepcopy to avoid RecursionError on
|
|
105
|
+
# deeply nested trees.
|
|
106
|
+
return self._fast_copy(tree)
|
|
89
107
|
except FileNotFoundError:
|
|
90
108
|
path = getattr(self, attr_name)
|
|
91
109
|
raise PhykitUserError(
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from typing import Dict
|
|
2
|
-
import
|
|
2
|
+
import pickle
|
|
3
3
|
|
|
4
4
|
from Bio.Phylo import Newick
|
|
5
5
|
|
|
@@ -20,7 +20,7 @@ class BranchLengthMultiplier(Tree):
|
|
|
20
20
|
def run(self) -> None:
|
|
21
21
|
tree = self.read_tree_file()
|
|
22
22
|
# Make a deep copy to avoid modifying the cached tree
|
|
23
|
-
tree_copy =
|
|
23
|
+
tree_copy = pickle.loads(pickle.dumps(tree, protocol=pickle.HIGHEST_PROTOCOL))
|
|
24
24
|
scaled_count = self.multiply_branch_lengths_by_factor(tree_copy, self.factor)
|
|
25
25
|
self.write_tree_file(tree_copy, self.output_file_path)
|
|
26
26
|
|
|
@@ -7,7 +7,7 @@ produces a phylogram/cladogram plot with annotated character changes.
|
|
|
7
7
|
|
|
8
8
|
Uses the generalized parsimony utilities in phykit.helpers.parsimony_utils.
|
|
9
9
|
"""
|
|
10
|
-
import
|
|
10
|
+
import pickle
|
|
11
11
|
from collections import Counter
|
|
12
12
|
from typing import Dict, List, Optional, Set, Tuple
|
|
13
13
|
|
|
@@ -79,7 +79,7 @@ class CharacterMap(Tree):
|
|
|
79
79
|
|
|
80
80
|
def run(self) -> None:
|
|
81
81
|
tree = self.read_tree_file()
|
|
82
|
-
tree =
|
|
82
|
+
tree = pickle.loads(pickle.dumps(tree, protocol=pickle.HIGHEST_PROTOCOL))
|
|
83
83
|
|
|
84
84
|
char_names, tip_states = self._parse_character_matrix(self.data_path)
|
|
85
85
|
n_chars = len(char_names)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from typing import Dict
|
|
2
|
-
import
|
|
2
|
+
import pickle
|
|
3
3
|
|
|
4
4
|
from .base import Tree
|
|
5
5
|
from ...helpers.json_output import print_json
|
|
@@ -18,7 +18,7 @@ class CollapseBranches(Tree):
|
|
|
18
18
|
def run(self):
|
|
19
19
|
tree = self.read_tree_file()
|
|
20
20
|
# Make a deep copy to avoid modifying the cached tree
|
|
21
|
-
tree_copy =
|
|
21
|
+
tree_copy = pickle.loads(pickle.dumps(tree, protocol=pickle.HIGHEST_PROTOCOL))
|
|
22
22
|
initial_nonterminals = len(tree_copy.get_nonterminals())
|
|
23
23
|
tree_copy.collapse_all(
|
|
24
24
|
lambda c: c.confidence and c.confidence < self.support
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import copy
|
|
2
1
|
import math
|
|
3
2
|
import pickle
|
|
4
3
|
from io import StringIO
|
|
@@ -37,6 +36,7 @@ class ConcordanceAsr(Tree):
|
|
|
37
36
|
self.method = parsed["method"]
|
|
38
37
|
self.ci = parsed["ci"]
|
|
39
38
|
self.plot_output = parsed["plot_output"]
|
|
39
|
+
self.plot_uncertainty = parsed["plot_uncertainty"]
|
|
40
40
|
self.missing_taxa = parsed["missing_taxa"]
|
|
41
41
|
self.json_output = parsed["json_output"]
|
|
42
42
|
self.plot_config = parsed["plot_config"]
|
|
@@ -50,6 +50,7 @@ class ConcordanceAsr(Tree):
|
|
|
50
50
|
method=getattr(args, "method", "weighted"),
|
|
51
51
|
ci=getattr(args, "ci", False),
|
|
52
52
|
plot_output=getattr(args, "plot", None),
|
|
53
|
+
plot_uncertainty=getattr(args, "plot_uncertainty", None),
|
|
53
54
|
missing_taxa=getattr(args, "missing_taxa", "shared"),
|
|
54
55
|
json_output=getattr(args, "json", False),
|
|
55
56
|
plot_config=PlotConfig.from_args(args),
|
|
@@ -105,7 +106,7 @@ class ConcordanceAsr(Tree):
|
|
|
105
106
|
)
|
|
106
107
|
|
|
107
108
|
# Prune species tree to shared taxa with trait data
|
|
108
|
-
species_copy =
|
|
109
|
+
species_copy = pickle.loads(pickle.dumps(species_tree, protocol=pickle.HIGHEST_PROTOCOL))
|
|
109
110
|
sp_tips = [t.name for t in species_copy.get_terminals()]
|
|
110
111
|
tips_to_prune = [t for t in sp_tips if t not in trait_values]
|
|
111
112
|
if tips_to_prune:
|
|
@@ -131,6 +132,12 @@ class ConcordanceAsr(Tree):
|
|
|
131
132
|
)
|
|
132
133
|
result["plot_output"] = self.plot_output
|
|
133
134
|
|
|
135
|
+
if self.plot_uncertainty:
|
|
136
|
+
self._plot_uncertainty(
|
|
137
|
+
species_copy, result, self.plot_uncertainty
|
|
138
|
+
)
|
|
139
|
+
result["plot_uncertainty"] = self.plot_uncertainty
|
|
140
|
+
|
|
134
141
|
if self.json_output:
|
|
135
142
|
print_json(result)
|
|
136
143
|
else:
|
|
@@ -409,7 +416,7 @@ class ConcordanceAsr(Tree):
|
|
|
409
416
|
|
|
410
417
|
def _run_asr_on_tree(self, tree, trait_values):
|
|
411
418
|
"""Run _fast_anc on one tree, return results keyed by descendant frozensets."""
|
|
412
|
-
tree_copy =
|
|
419
|
+
tree_copy = pickle.loads(pickle.dumps(tree, protocol=pickle.HIGHEST_PROTOCOL))
|
|
413
420
|
|
|
414
421
|
# Prune to trait taxa
|
|
415
422
|
tip_names = [t.name for t in tree_copy.get_terminals()]
|
|
@@ -586,6 +593,8 @@ class ConcordanceAsr(Tree):
|
|
|
586
593
|
"gdf2": float(gdf2),
|
|
587
594
|
"var_topology": float(between_var),
|
|
588
595
|
"var_parameter": float(within_var),
|
|
596
|
+
"source_estimates": [float(m) for m in means],
|
|
597
|
+
"source_weights": [float(w) for w in weights],
|
|
589
598
|
}
|
|
590
599
|
|
|
591
600
|
if self.ci:
|
|
@@ -658,6 +667,7 @@ class ConcordanceAsr(Tree):
|
|
|
658
667
|
"n_gene_trees_with_node": len(estimates_list),
|
|
659
668
|
"var_topology": float(np.var(estimates_list)),
|
|
660
669
|
"var_parameter": 0.0,
|
|
670
|
+
"gene_tree_estimates": estimates_list,
|
|
661
671
|
}
|
|
662
672
|
|
|
663
673
|
if self.ci and len(estimates_list) >= 2:
|
|
@@ -961,3 +971,149 @@ class ConcordanceAsr(Tree):
|
|
|
961
971
|
fig.savefig(output_path, dpi=config.dpi, bbox_inches="tight")
|
|
962
972
|
plt.close(fig)
|
|
963
973
|
print(f"Saved concordance ASR plot: {output_path}")
|
|
974
|
+
|
|
975
|
+
# ------------------------------------------------------------------
|
|
976
|
+
# Uncertainty plot
|
|
977
|
+
# ------------------------------------------------------------------
|
|
978
|
+
|
|
979
|
+
def _plot_uncertainty(self, species_tree, result, output_path) -> None:
|
|
980
|
+
"""Violin + boxplot showing distribution of ancestral estimates.
|
|
981
|
+
|
|
982
|
+
For the distribution method: per-gene-tree estimates.
|
|
983
|
+
For the weighted method: concordant + discordant source estimates.
|
|
984
|
+
"""
|
|
985
|
+
try:
|
|
986
|
+
import matplotlib
|
|
987
|
+
matplotlib.use("Agg")
|
|
988
|
+
import matplotlib.pyplot as plt
|
|
989
|
+
except ImportError:
|
|
990
|
+
print("matplotlib is required for plotting.")
|
|
991
|
+
return
|
|
992
|
+
|
|
993
|
+
anc = result.get("ancestral_estimates", {})
|
|
994
|
+
if not anc:
|
|
995
|
+
return
|
|
996
|
+
|
|
997
|
+
config = self.plot_config
|
|
998
|
+
|
|
999
|
+
# Collect data: (node_label, estimates_list, gcf, estimate)
|
|
1000
|
+
node_data = []
|
|
1001
|
+
for clade in species_tree.find_clades(order="preorder"):
|
|
1002
|
+
if clade.is_terminal():
|
|
1003
|
+
continue
|
|
1004
|
+
# Match by descendants
|
|
1005
|
+
desc = sorted(t.name for t in clade.get_terminals())
|
|
1006
|
+
for label, entry in anc.items():
|
|
1007
|
+
if entry.get("descendants") == desc:
|
|
1008
|
+
if result["method"] == "distribution":
|
|
1009
|
+
estimates = entry.get("gene_tree_estimates", [])
|
|
1010
|
+
else:
|
|
1011
|
+
estimates = entry.get("source_estimates", [])
|
|
1012
|
+
if len(estimates) >= 2:
|
|
1013
|
+
# Short label for display
|
|
1014
|
+
if len(desc) <= 2:
|
|
1015
|
+
short = f"({', '.join(desc)})"
|
|
1016
|
+
else:
|
|
1017
|
+
short = f"({desc[0]}, ..., {desc[-1]})"
|
|
1018
|
+
node_data.append((
|
|
1019
|
+
short, estimates,
|
|
1020
|
+
entry.get("gcf", 1.0),
|
|
1021
|
+
entry.get("estimate", 0.0),
|
|
1022
|
+
))
|
|
1023
|
+
break
|
|
1024
|
+
|
|
1025
|
+
if not node_data:
|
|
1026
|
+
print("No nodes with enough estimates for uncertainty plot.")
|
|
1027
|
+
return
|
|
1028
|
+
|
|
1029
|
+
# Sort by weighted/mean estimate
|
|
1030
|
+
node_data.sort(key=lambda x: x[3])
|
|
1031
|
+
|
|
1032
|
+
labels = [d[0] for d in node_data]
|
|
1033
|
+
data = [d[1] for d in node_data]
|
|
1034
|
+
gcfs = [d[2] for d in node_data]
|
|
1035
|
+
|
|
1036
|
+
n_nodes = len(node_data)
|
|
1037
|
+
fig_h = max(4, n_nodes * 0.5)
|
|
1038
|
+
fig_w = config.fig_width or 8
|
|
1039
|
+
if config.fig_height:
|
|
1040
|
+
fig_h = config.fig_height
|
|
1041
|
+
fig, ax = plt.subplots(figsize=(fig_w, fig_h))
|
|
1042
|
+
|
|
1043
|
+
positions = list(range(n_nodes))
|
|
1044
|
+
|
|
1045
|
+
# Color by gCF: high concordance = blue, low = red
|
|
1046
|
+
gcf_colors = []
|
|
1047
|
+
for g in gcfs:
|
|
1048
|
+
if g >= 0.7:
|
|
1049
|
+
gcf_colors.append("#2b8cbe")
|
|
1050
|
+
elif g >= 0.4:
|
|
1051
|
+
gcf_colors.append("#969696")
|
|
1052
|
+
else:
|
|
1053
|
+
gcf_colors.append("#d62728")
|
|
1054
|
+
|
|
1055
|
+
# Violin plots (horizontal)
|
|
1056
|
+
vp = ax.violinplot(
|
|
1057
|
+
data, positions=positions, vert=False,
|
|
1058
|
+
showmeans=False, showmedians=False, showextrema=False,
|
|
1059
|
+
)
|
|
1060
|
+
for i, body in enumerate(vp["bodies"]):
|
|
1061
|
+
body.set_facecolor(gcf_colors[i])
|
|
1062
|
+
body.set_alpha(0.3)
|
|
1063
|
+
body.set_edgecolor(gcf_colors[i])
|
|
1064
|
+
|
|
1065
|
+
# Boxplots
|
|
1066
|
+
bp = ax.boxplot(
|
|
1067
|
+
data, positions=positions, vert=False,
|
|
1068
|
+
widths=0.3, patch_artist=True, showfliers=True, zorder=3,
|
|
1069
|
+
)
|
|
1070
|
+
for i, box in enumerate(bp["boxes"]):
|
|
1071
|
+
box.set_facecolor(gcf_colors[i])
|
|
1072
|
+
box.set_alpha(0.6)
|
|
1073
|
+
for element in ["whiskers", "caps", "medians"]:
|
|
1074
|
+
for line in bp[element]:
|
|
1075
|
+
line.set_color("black")
|
|
1076
|
+
|
|
1077
|
+
# Mean markers
|
|
1078
|
+
for i, d in enumerate(data):
|
|
1079
|
+
mean_val = np.mean(d)
|
|
1080
|
+
ax.plot(
|
|
1081
|
+
mean_val, i, "D", color="white",
|
|
1082
|
+
markeredgecolor="black", markersize=5, zorder=4,
|
|
1083
|
+
)
|
|
1084
|
+
|
|
1085
|
+
ax.set_yticks(positions)
|
|
1086
|
+
ax.set_yticklabels(labels, fontsize=7)
|
|
1087
|
+
xlabel = "Ancestral state estimate"
|
|
1088
|
+
ax.set_xlabel(xlabel)
|
|
1089
|
+
|
|
1090
|
+
ax.spines["top"].set_visible(False)
|
|
1091
|
+
ax.spines["right"].set_visible(False)
|
|
1092
|
+
|
|
1093
|
+
# Legend for gCF colors
|
|
1094
|
+
from matplotlib.patches import Patch
|
|
1095
|
+
legend_handles = [
|
|
1096
|
+
Patch(facecolor="#2b8cbe", alpha=0.6, label="gCF >= 0.7"),
|
|
1097
|
+
Patch(facecolor="#969696", alpha=0.6, label="0.4 <= gCF < 0.7"),
|
|
1098
|
+
Patch(facecolor="#d62728", alpha=0.6, label="gCF < 0.4"),
|
|
1099
|
+
]
|
|
1100
|
+
ax.legend(
|
|
1101
|
+
handles=legend_handles, loc="lower right",
|
|
1102
|
+
fontsize=7, title="Concordance", title_fontsize=8,
|
|
1103
|
+
)
|
|
1104
|
+
|
|
1105
|
+
method_label = (
|
|
1106
|
+
"gene trees" if result["method"] == "distribution"
|
|
1107
|
+
else "concordance sources"
|
|
1108
|
+
)
|
|
1109
|
+
if config.show_title:
|
|
1110
|
+
ax.set_title(
|
|
1111
|
+
config.title
|
|
1112
|
+
or f"Ancestral state uncertainty across {method_label}",
|
|
1113
|
+
fontsize=config.title_fontsize,
|
|
1114
|
+
)
|
|
1115
|
+
|
|
1116
|
+
fig.tight_layout()
|
|
1117
|
+
fig.savefig(output_path, dpi=config.dpi, bbox_inches="tight")
|
|
1118
|
+
plt.close(fig)
|
|
1119
|
+
print(f"Saved uncertainty plot: {output_path}")
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import copy
|
|
2
1
|
import math
|
|
2
|
+
import pickle
|
|
3
3
|
import sys
|
|
4
4
|
from typing import Dict, List, Tuple
|
|
5
5
|
|
|
@@ -47,7 +47,7 @@ class ContMap(Tree):
|
|
|
47
47
|
x = np.array([trait_values[name] for name in ordered_names])
|
|
48
48
|
|
|
49
49
|
# Prune tree to shared taxa
|
|
50
|
-
tree_copy =
|
|
50
|
+
tree_copy = pickle.loads(pickle.dumps(tree, protocol=pickle.HIGHEST_PROTOCOL))
|
|
51
51
|
tip_names_in_tree = [t.name for t in tree_copy.get_terminals()]
|
|
52
52
|
tips_to_prune = [t for t in tip_names_in_tree if t not in trait_values]
|
|
53
53
|
if tips_to_prune:
|
|
@@ -7,7 +7,7 @@ contrast (standardized difference), producing n-1 contrasts for n tips.
|
|
|
7
7
|
|
|
8
8
|
Cross-validated against R's ape::pic().
|
|
9
9
|
"""
|
|
10
|
-
import
|
|
10
|
+
import pickle
|
|
11
11
|
import sys
|
|
12
12
|
from typing import Dict, List, Tuple
|
|
13
13
|
|
|
@@ -27,7 +27,7 @@ class IndependentContrasts(Tree):
|
|
|
27
27
|
|
|
28
28
|
def run(self) -> None:
|
|
29
29
|
tree = self.read_tree_file()
|
|
30
|
-
tree =
|
|
30
|
+
tree = pickle.loads(pickle.dumps(tree, protocol=pickle.HIGHEST_PROTOCOL))
|
|
31
31
|
self.validate_tree(tree, min_tips=3, assign_default_branch_length=1e-8, context="independent contrasts")
|
|
32
32
|
|
|
33
33
|
tree_tips = [t.name for t in tree.get_terminals()]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from typing import Dict
|
|
2
|
-
import
|
|
2
|
+
import pickle
|
|
3
3
|
|
|
4
4
|
from Bio.Phylo import Newick
|
|
5
5
|
|
|
@@ -19,7 +19,7 @@ class InternodeLabeler(Tree):
|
|
|
19
19
|
def run(self):
|
|
20
20
|
tree = self.read_tree_file()
|
|
21
21
|
# Make a deep copy to avoid modifying the cached tree
|
|
22
|
-
tree_copy =
|
|
22
|
+
tree_copy = pickle.loads(pickle.dumps(tree, protocol=pickle.HIGHEST_PROTOCOL))
|
|
23
23
|
labeled_count = self.add_labels_to_tree(tree_copy)
|
|
24
24
|
self.write_tree_file(tree_copy, self.output_file_path)
|
|
25
25
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import pickle
|
|
1
2
|
import sys
|
|
2
|
-
import copy
|
|
3
3
|
from typing import Dict
|
|
4
4
|
|
|
5
5
|
from .base import Tree
|
|
@@ -21,7 +21,7 @@ class LastCommonAncestorSubtree(Tree):
|
|
|
21
21
|
def run(self):
|
|
22
22
|
tree = self.read_tree_file()
|
|
23
23
|
# Make a deep copy to avoid issues with cached tree modifications
|
|
24
|
-
tree_copy =
|
|
24
|
+
tree_copy = pickle.loads(pickle.dumps(tree, protocol=pickle.HIGHEST_PROTOCOL))
|
|
25
25
|
try:
|
|
26
26
|
taxa = read_single_column_file_to_list(self.list_of_taxa)
|
|
27
27
|
except FileNotFoundError:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import pickle
|
|
2
2
|
import sys
|
|
3
3
|
from typing import Dict, List, Tuple
|
|
4
4
|
|
|
@@ -61,7 +61,7 @@ class OUShiftDetection(Tree):
|
|
|
61
61
|
shared = set(traits.keys())
|
|
62
62
|
|
|
63
63
|
# Prune tree to shared taxa
|
|
64
|
-
tree_copy =
|
|
64
|
+
tree_copy = pickle.loads(pickle.dumps(tree, protocol=pickle.HIGHEST_PROTOCOL))
|
|
65
65
|
tip_names_in_tree = [t.name for t in tree_copy.get_terminals()]
|
|
66
66
|
to_prune = [t for t in tip_names_in_tree if t not in shared]
|
|
67
67
|
if to_prune:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import pickle
|
|
2
2
|
import sys
|
|
3
3
|
from typing import Dict, List, Tuple
|
|
4
4
|
|
|
@@ -44,7 +44,7 @@ class OUwie(Tree):
|
|
|
44
44
|
traits = {k: traits[k] for k in shared}
|
|
45
45
|
regime_assignments = {k: regime_assignments[k] for k in shared}
|
|
46
46
|
|
|
47
|
-
tree_copy =
|
|
47
|
+
tree_copy = pickle.loads(pickle.dumps(tree, protocol=pickle.HIGHEST_PROTOCOL))
|
|
48
48
|
tip_names_in_tree = [t.name for t in tree_copy.get_terminals()]
|
|
49
49
|
tips_to_prune = [t for t in tip_names_in_tree if t not in shared]
|
|
50
50
|
if tips_to_prune:
|
|
@@ -8,7 +8,7 @@ total is summed.
|
|
|
8
8
|
|
|
9
9
|
Cross-validated against R's phangorn::parsimony().
|
|
10
10
|
"""
|
|
11
|
-
import
|
|
11
|
+
import pickle
|
|
12
12
|
from typing import Dict, List
|
|
13
13
|
|
|
14
14
|
from Bio import SeqIO
|
|
@@ -28,7 +28,7 @@ class ParsimonyScore(Tree):
|
|
|
28
28
|
|
|
29
29
|
def run(self) -> None:
|
|
30
30
|
tree = self.read_tree_file()
|
|
31
|
-
tree =
|
|
31
|
+
tree = pickle.loads(pickle.dumps(tree, protocol=pickle.HIGHEST_PROTOCOL))
|
|
32
32
|
self.validate_tree(tree, min_tips=3, context="parsimony score")
|
|
33
33
|
self._resolve_polytomies(tree)
|
|
34
34
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import copy
|
|
2
1
|
import math
|
|
2
|
+
import pickle
|
|
3
3
|
import sys
|
|
4
4
|
from typing import Dict, List, Tuple
|
|
5
5
|
|
|
@@ -34,7 +34,7 @@ class Phenogram(Tree):
|
|
|
34
34
|
x = np.array([trait_values[name] for name in ordered_names])
|
|
35
35
|
|
|
36
36
|
# Prune tree to shared taxa
|
|
37
|
-
tree_copy =
|
|
37
|
+
tree_copy = pickle.loads(pickle.dumps(tree, protocol=pickle.HIGHEST_PROTOCOL))
|
|
38
38
|
tip_names_in_tree = [t.name for t in tree_copy.get_terminals()]
|
|
39
39
|
tips_to_prune = [t for t in tip_names_in_tree if t not in trait_values]
|
|
40
40
|
if tips_to_prune:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import pickle
|
|
2
2
|
from typing import Dict, List, Tuple
|
|
3
3
|
|
|
4
4
|
import numpy as np
|
|
@@ -156,7 +156,7 @@ class PhyloLogistic(Tree):
|
|
|
156
156
|
return vcv, diag_corr
|
|
157
157
|
|
|
158
158
|
# Transform branch lengths
|
|
159
|
-
tree_t =
|
|
159
|
+
tree_t = pickle.loads(pickle.dumps(tree, protocol=pickle.HIGHEST_PROTOCOL))
|
|
160
160
|
for clade in tree_t.find_clades():
|
|
161
161
|
if clade.branch_length is not None and clade.branch_length > 0:
|
|
162
162
|
bl = clade.branch_length
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import copy
|
|
2
1
|
import os
|
|
2
|
+
import pickle
|
|
3
3
|
from typing import Dict, List, Tuple
|
|
4
4
|
|
|
5
5
|
import numpy as np
|
|
@@ -346,7 +346,7 @@ class PhylogeneticOrdination(Tree):
|
|
|
346
346
|
def _reconstruct_ancestral_scores(
|
|
347
347
|
self, tree, scores: np.ndarray, ordered_names: List[str]
|
|
348
348
|
) -> Tuple[Dict, Dict]:
|
|
349
|
-
tree_copy =
|
|
349
|
+
tree_copy = pickle.loads(pickle.dumps(tree, protocol=pickle.HIGHEST_PROTOCOL))
|
|
350
350
|
|
|
351
351
|
tip_names_in_tree = [t.name for t in tree_copy.get_terminals()]
|
|
352
352
|
tips_to_prune = [t for t in tip_names_in_tree if t not in ordered_names]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import copy
|
|
2
1
|
import os
|
|
2
|
+
import pickle
|
|
3
3
|
from typing import Dict, List, Tuple
|
|
4
4
|
|
|
5
5
|
import numpy as np
|
|
@@ -136,7 +136,7 @@ class Phylomorphospace(Tree):
|
|
|
136
136
|
def _reconstruct_ancestral_scores(
|
|
137
137
|
self, tree, scores: np.ndarray, ordered_names: List[str]
|
|
138
138
|
) -> Tuple[Dict, Dict, object]:
|
|
139
|
-
tree_copy =
|
|
139
|
+
tree_copy = pickle.loads(pickle.dumps(tree, protocol=pickle.HIGHEST_PROTOCOL))
|
|
140
140
|
|
|
141
141
|
tip_names_in_tree = [t.name for t in tree_copy.get_terminals()]
|
|
142
142
|
tips_to_prune = [t for t in tip_names_in_tree if t not in ordered_names]
|