phykit 2.1.87__tar.gz → 2.1.89__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.
Files changed (149) hide show
  1. {phykit-2.1.87 → phykit-2.1.89}/PKG-INFO +1 -1
  2. {phykit-2.1.87 → phykit-2.1.89}/phykit/cli_registry.py +5 -0
  3. phykit-2.1.89/phykit/helpers/geological_timescale.py +119 -0
  4. {phykit-2.1.87 → phykit-2.1.89}/phykit/phykit.py +149 -0
  5. {phykit-2.1.87 → phykit-2.1.89}/phykit/service_factories.py +2 -0
  6. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/__init__.py +2 -0
  7. phykit-2.1.89/phykit/services/tree/chronogram.py +661 -0
  8. phykit-2.1.89/phykit/services/tree/dtt.py +411 -0
  9. phykit-2.1.89/phykit/version.py +1 -0
  10. {phykit-2.1.87 → phykit-2.1.89}/phykit.egg-info/PKG-INFO +1 -1
  11. {phykit-2.1.87 → phykit-2.1.89}/phykit.egg-info/SOURCES.txt +3 -0
  12. {phykit-2.1.87 → phykit-2.1.89}/phykit.egg-info/entry_points.txt +5 -0
  13. phykit-2.1.87/phykit/version.py +0 -1
  14. {phykit-2.1.87 → phykit-2.1.89}/LICENSE.md +0 -0
  15. {phykit-2.1.87 → phykit-2.1.89}/README.md +0 -0
  16. {phykit-2.1.87 → phykit-2.1.89}/phykit/__init__.py +0 -0
  17. {phykit-2.1.87 → phykit-2.1.89}/phykit/__main__.py +0 -0
  18. {phykit-2.1.87 → phykit-2.1.89}/phykit/errors.py +0 -0
  19. {phykit-2.1.87 → phykit-2.1.89}/phykit/helpers/__init__.py +0 -0
  20. {phykit-2.1.87 → phykit-2.1.89}/phykit/helpers/boolean_argument_parsing.py +0 -0
  21. {phykit-2.1.87 → phykit-2.1.89}/phykit/helpers/caching.py +0 -0
  22. {phykit-2.1.87 → phykit-2.1.89}/phykit/helpers/circular_layout.py +0 -0
  23. {phykit-2.1.87 → phykit-2.1.89}/phykit/helpers/color_annotations.py +0 -0
  24. {phykit-2.1.87 → phykit-2.1.89}/phykit/helpers/discrete_models.py +0 -0
  25. {phykit-2.1.87 → phykit-2.1.89}/phykit/helpers/files.py +0 -0
  26. {phykit-2.1.87 → phykit-2.1.89}/phykit/helpers/json_output.py +0 -0
  27. {phykit-2.1.87 → phykit-2.1.89}/phykit/helpers/parallel.py +0 -0
  28. {phykit-2.1.87 → phykit-2.1.89}/phykit/helpers/parsimony_utils.py +0 -0
  29. {phykit-2.1.87 → phykit-2.1.89}/phykit/helpers/pgls_utils.py +0 -0
  30. {phykit-2.1.87 → phykit-2.1.89}/phykit/helpers/plot_config.py +0 -0
  31. {phykit-2.1.87 → phykit-2.1.89}/phykit/helpers/quartet_utils.py +0 -0
  32. {phykit-2.1.87 → phykit-2.1.89}/phykit/helpers/stats_summary.py +0 -0
  33. {phykit-2.1.87 → phykit-2.1.89}/phykit/helpers/streaming.py +0 -0
  34. {phykit-2.1.87 → phykit-2.1.89}/phykit/helpers/trait_parsing.py +0 -0
  35. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/__init__.py +0 -0
  36. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/__init__.py +0 -0
  37. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/alignment_entropy.py +0 -0
  38. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/alignment_length.py +0 -0
  39. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/alignment_length_no_gaps.py +0 -0
  40. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/alignment_outlier_taxa.py +0 -0
  41. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/alignment_recoding.py +0 -0
  42. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/alignment_subsample.py +0 -0
  43. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/base.py +0 -0
  44. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/column_score.py +0 -0
  45. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/composition_per_taxon.py +0 -0
  46. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/compositional_bias_per_site.py +0 -0
  47. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/create_concatenation_matrix.py +0 -0
  48. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/dfoil.py +0 -0
  49. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/dna_threader.py +0 -0
  50. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/dstatistic.py +0 -0
  51. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/evolutionary_rate_per_site.py +0 -0
  52. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/faidx.py +0 -0
  53. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/gc_content.py +0 -0
  54. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/identity_matrix.py +0 -0
  55. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/mask_alignment.py +0 -0
  56. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/occupancy_filter.py +0 -0
  57. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/occupancy_per_taxon.py +0 -0
  58. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/pairwise_identity.py +0 -0
  59. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/parsimony_informative_sites.py +0 -0
  60. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/phylo_gwas.py +0 -0
  61. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/plot_alignment_qc.py +0 -0
  62. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/rcv.py +0 -0
  63. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/rcvt.py +0 -0
  64. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/rename_fasta_entries.py +0 -0
  65. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/sum_of_pairs_score.py +0 -0
  66. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/taxon_groups.py +0 -0
  67. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/alignment/variable_sites.py +0 -0
  68. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/base.py +0 -0
  69. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/ancestral_reconstruction.py +0 -0
  70. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/base.py +0 -0
  71. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/bipartition_support_stats.py +0 -0
  72. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/branch_length_multiplier.py +0 -0
  73. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/character_map.py +0 -0
  74. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/collapse_branches.py +0 -0
  75. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/concordance_asr.py +0 -0
  76. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/consensus_network.py +0 -0
  77. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/consensus_tree.py +0 -0
  78. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/cont_map.py +0 -0
  79. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/cophylo.py +0 -0
  80. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/covarying_evolutionary_rates.py +0 -0
  81. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/density_map.py +0 -0
  82. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/discordance_asymmetry.py +0 -0
  83. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/dvmc.py +0 -0
  84. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/evo_tempo_map.py +0 -0
  85. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/evolutionary_rate.py +0 -0
  86. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/fit_continuous.py +0 -0
  87. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/fit_discrete.py +0 -0
  88. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/hidden_paralogy_check.py +0 -0
  89. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/hybridization.py +0 -0
  90. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/independent_contrasts.py +0 -0
  91. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/internal_branch_stats.py +0 -0
  92. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/internode_labeler.py +0 -0
  93. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/kf_distance.py +0 -0
  94. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/last_common_ancestor_subtree.py +0 -0
  95. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/lb_score.py +0 -0
  96. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/ltt.py +0 -0
  97. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/monophyly_check.py +0 -0
  98. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/nearest_neighbor_interchange.py +0 -0
  99. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/neighbor_net.py +0 -0
  100. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/network_signal.py +0 -0
  101. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/ou_shift_detection.py +0 -0
  102. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/ouwie.py +0 -0
  103. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/parsimony_score.py +0 -0
  104. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/patristic_distances.py +0 -0
  105. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/phenogram.py +0 -0
  106. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/phylo_anova.py +0 -0
  107. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/phylo_heatmap.py +0 -0
  108. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/phylo_impute.py +0 -0
  109. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/phylo_logistic.py +0 -0
  110. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/phylo_path.py +0 -0
  111. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/phylogenetic_glm.py +0 -0
  112. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/phylogenetic_ordination.py +0 -0
  113. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/phylogenetic_regression.py +0 -0
  114. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/phylogenetic_signal.py +0 -0
  115. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/phylomorphospace.py +0 -0
  116. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/polytomy_test.py +0 -0
  117. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/print_tree.py +0 -0
  118. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/prune_tree.py +0 -0
  119. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/quartet_network.py +0 -0
  120. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/quartet_pie.py +0 -0
  121. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/rate_heterogeneity.py +0 -0
  122. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/relative_rate_test.py +0 -0
  123. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/rename_tree_tips.py +0 -0
  124. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/rf_distance.py +0 -0
  125. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/root_tree.py +0 -0
  126. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/saturation.py +0 -0
  127. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/simmap_summary.py +0 -0
  128. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/spectral_discordance.py +0 -0
  129. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/spr.py +0 -0
  130. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/spurious_sequence.py +0 -0
  131. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/stochastic_character_map.py +0 -0
  132. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/terminal_branch_stats.py +0 -0
  133. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/threshold_model.py +0 -0
  134. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/tip_labels.py +0 -0
  135. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/tip_to_tip_distance.py +0 -0
  136. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/tip_to_tip_node_distance.py +0 -0
  137. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/total_tree_length.py +0 -0
  138. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/trait_correlation.py +0 -0
  139. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/trait_rate_map.py +0 -0
  140. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/transfer_annotations.py +0 -0
  141. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/tree_space.py +0 -0
  142. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/treeness.py +0 -0
  143. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/treeness_over_rcv.py +0 -0
  144. {phykit-2.1.87 → phykit-2.1.89}/phykit/services/tree/vcv_utils.py +0 -0
  145. {phykit-2.1.87 → phykit-2.1.89}/phykit.egg-info/dependency_links.txt +0 -0
  146. {phykit-2.1.87 → phykit-2.1.89}/phykit.egg-info/requires.txt +0 -0
  147. {phykit-2.1.87 → phykit-2.1.89}/phykit.egg-info/top_level.txt +0 -0
  148. {phykit-2.1.87 → phykit-2.1.89}/setup.cfg +0 -0
  149. {phykit-2.1.87 → phykit-2.1.89}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: phykit
3
- Version: 2.1.87
3
+ Version: 2.1.89
4
4
  Home-page: https://github.com/jlsteenwyk/phykit
5
5
  Author: Jacob L. Steenwyk
6
6
  Author-email: jlsteenwyk@gmail.com
@@ -21,6 +21,8 @@ ALIAS_TO_HANDLER: Dict[str, str] = {
21
21
  "recode": "alignment_recoding",
22
22
  "outlier_taxa": "alignment_outlier_taxa",
23
23
  "aot": "alignment_outlier_taxa",
24
+ "dtt": "dtt",
25
+ "disparity_through_time": "dtt",
24
26
  "dstat": "dstatistic",
25
27
  "abba_baba": "dstatistic",
26
28
  "dfoil": "dfoil",
@@ -70,6 +72,9 @@ ALIAS_TO_HANDLER: Dict[str, str] = {
70
72
  "blm": "branch_length_multiplier",
71
73
  "collapse": "collapse_branches",
72
74
  "cb": "collapse_branches",
75
+ "chronogram": "chronogram",
76
+ "chrono": "chronogram",
77
+ "time_tree": "chronogram",
73
78
  "cover": "covarying_evolutionary_rates",
74
79
  "consnet": "consensus_network",
75
80
  "splitnet": "consensus_network",
@@ -0,0 +1,119 @@
1
+ """
2
+ Geological timescale data (ICS 2024).
3
+
4
+ Provides era, period, and epoch boundaries for plotting chronograms
5
+ with geological time bands.
6
+ """
7
+
8
+ # Muted pastel color palette for geological epochs
9
+ # Inspired by ICS colors but softened for publication figures
10
+ EPOCH_COLORS = {
11
+ "Holocene": "#FEF2CC",
12
+ "Pleistocene": "#FFF2AE",
13
+ "Pliocene": "#FFFF99",
14
+ "Miocene": "#FFFF00",
15
+ "Oligocene": "#FDC07A",
16
+ "Eocene": "#FDB46C",
17
+ "Paleocene": "#FDA75F",
18
+ "Late Cretaceous": "#A6D84A",
19
+ "Early Cretaceous": "#8CCD57",
20
+ "Late Jurassic": "#B3E1EB",
21
+ "Middle Jurassic": "#80CFD8",
22
+ "Early Jurassic": "#42B2D0",
23
+ "Late Triassic": "#BD8CC2",
24
+ "Middle Triassic": "#B07FB7",
25
+ "Early Triassic": "#A372AB",
26
+ }
27
+
28
+ PERIOD_COLORS = {
29
+ "Quaternary": "#F9F97F",
30
+ "Neogene": "#FFE619",
31
+ "Paleogene": "#FD9A52",
32
+ "Cretaceous": "#7FC64E",
33
+ "Jurassic": "#34B2C9",
34
+ "Triassic": "#812B92",
35
+ "Permian": "#F04028",
36
+ "Carboniferous": "#67A599",
37
+ "Devonian": "#CB8C37",
38
+ "Silurian": "#B3E1B6",
39
+ "Ordovician": "#009270",
40
+ "Cambrian": "#7FA056",
41
+ }
42
+
43
+ ERA_COLORS = {
44
+ "Cenozoic": "#F2F91D",
45
+ "Mesozoic": "#67C5CA",
46
+ "Paleozoic": "#99C08D",
47
+ }
48
+
49
+ EPOCHS = [
50
+ ("Holocene", 0.0117, 0),
51
+ ("Pleistocene", 2.58, 0.0117),
52
+ ("Pliocene", 5.333, 2.58),
53
+ ("Miocene", 23.03, 5.333),
54
+ ("Oligocene", 33.9, 23.03),
55
+ ("Eocene", 56.0, 33.9),
56
+ ("Paleocene", 66.0, 56.0),
57
+ ("Late Cretaceous", 100.5, 66.0),
58
+ ("Early Cretaceous", 145.0, 100.5),
59
+ ("Late Jurassic", 163.5, 145.0),
60
+ ("Middle Jurassic", 174.7, 163.5),
61
+ ("Early Jurassic", 201.4, 174.7),
62
+ ("Late Triassic", 237.0, 201.4),
63
+ ("Middle Triassic", 247.2, 237.0),
64
+ ("Early Triassic", 251.902, 247.2),
65
+ ]
66
+
67
+ PERIODS = [
68
+ ("Quaternary", 2.58, 0),
69
+ ("Neogene", 23.03, 2.58),
70
+ ("Paleogene", 66.0, 23.03),
71
+ ("Cretaceous", 145.0, 66.0),
72
+ ("Jurassic", 201.4, 145.0),
73
+ ("Triassic", 251.902, 201.4),
74
+ ("Permian", 298.9, 251.902),
75
+ ("Carboniferous", 358.9, 298.9),
76
+ ("Devonian", 419.2, 358.9),
77
+ ("Silurian", 443.8, 419.2),
78
+ ("Ordovician", 485.4, 443.8),
79
+ ("Cambrian", 538.8, 485.4),
80
+ ]
81
+
82
+ ERAS = [
83
+ ("Cenozoic", 66.0, 0),
84
+ ("Mesozoic", 251.902, 66.0),
85
+ ("Paleozoic", 538.8, 251.902),
86
+ ]
87
+
88
+
89
+ def get_timescale_for_range(root_age, level="auto"):
90
+ """Return appropriate timescale intervals for a given root age.
91
+
92
+ Parameters
93
+ ----------
94
+ root_age : float, Ma
95
+ level : "epoch", "period", "era", or "auto"
96
+
97
+ Returns
98
+ -------
99
+ list of (name, start_ma, end_ma), color_dict
100
+ """
101
+ if level == "auto":
102
+ if root_age <= 66:
103
+ level = "epoch"
104
+ elif root_age <= 252:
105
+ level = "period"
106
+ else:
107
+ level = "era"
108
+
109
+ if level == "epoch":
110
+ intervals = [(n, s, e) for n, s, e in EPOCHS if s > 0 and e < root_age * 1.1]
111
+ # Include any epoch that overlaps the tree's range
112
+ intervals = [(n, s, e) for n, s, e in EPOCHS if e < root_age * 1.05]
113
+ return intervals, EPOCH_COLORS
114
+ elif level == "period":
115
+ intervals = [(n, s, e) for n, s, e in PERIODS if e < root_age * 1.05]
116
+ return intervals, PERIOD_COLORS
117
+ else:
118
+ intervals = [(n, s, e) for n, s, e in ERAS if e < root_age * 1.05]
119
+ return intervals, ERA_COLORS
@@ -192,6 +192,10 @@ class Phykit:
192
192
  concordance_asr (alias: conc_asr; casr)
193
193
  - concordance-aware ancestral state reconstruction
194
194
  incorporating gene tree discordance
195
+ chronogram (alias: chrono; time_tree)
196
+ - plot a time-calibrated tree with geological timescale
197
+ dtt (alias: disparity_through_time)
198
+ - disparity through time analysis (Harmon et al. 2003)
195
199
  bipartition_support_stats (alias: bss)
196
200
  - calculates summary statistics for bipartition support
197
201
  branch_length_multiplier (alias: blm)
@@ -2922,6 +2926,143 @@ class Phykit:
2922
2926
  _add_json_argument(parser)
2923
2927
  _run_service(parser, argv, ConcordanceAsr)
2924
2928
 
2929
+ @staticmethod
2930
+ def chronogram(argv):
2931
+ parser = _new_parser(
2932
+ description=textwrap.dedent(
2933
+ f"""\
2934
+ {help_header}
2935
+
2936
+ Plot a chronogram (time-calibrated phylogeny) with
2937
+ geological timescale bands. Requires an ultrametric
2938
+ tree and the root age in millions of years (Ma).
2939
+
2940
+ Geological epoch/period/era bands are drawn behind
2941
+ the tree as colored stripes, with labels along the
2942
+ top. The time axis runs from past (left) to present
2943
+ (right).
2944
+
2945
+ Aliases:
2946
+ chronogram, chrono, time_tree
2947
+ Command line interfaces:
2948
+ pk_chronogram, pk_chrono, pk_time_tree
2949
+
2950
+ Usage:
2951
+ phykit chronogram -t <tree> --root-age <float>
2952
+ --plot-output <file> [--timescale auto|epoch|period|era]
2953
+ [--node-ages] [--circular] [--ladderize]
2954
+ [--color-file <file>] [--json]
2955
+
2956
+ Options
2957
+ =====================================================
2958
+ -t/--tree ultrametric tree file
2959
+ (required)
2960
+
2961
+ --root-age age of the root in Ma
2962
+ (required)
2963
+
2964
+ --plot-output output figure path
2965
+ (required; .png, .pdf, .svg)
2966
+
2967
+ --timescale timescale level: auto
2968
+ (default), epoch, period,
2969
+ or era. Auto selects based
2970
+ on root age.
2971
+
2972
+ --node-ages label internal nodes with
2973
+ divergence times (Ma)
2974
+
2975
+ --circular draw circular chronogram
2976
+
2977
+ --ladderize ladderize the tree
2978
+
2979
+ --color-file color annotation file
2980
+ (iTOL-inspired TSV)
2981
+
2982
+ --json output node ages as JSON
2983
+ """
2984
+ ),
2985
+ )
2986
+ parser.add_argument("-t", "--tree", type=str, required=True, help=SUPPRESS, metavar="")
2987
+ parser.add_argument("--root-age", type=float, required=True, help=SUPPRESS, metavar="")
2988
+ parser.add_argument("--plot-output", type=str, required=True, help=SUPPRESS, metavar="")
2989
+ parser.add_argument("--timescale", type=str, default="auto", choices=["auto", "epoch", "period", "era"], help=SUPPRESS, metavar="")
2990
+ parser.add_argument("--node-ages", action="store_true", help=SUPPRESS)
2991
+ add_plot_arguments(parser)
2992
+ _add_json_argument(parser)
2993
+ _run_service(parser, argv, Chronogram)
2994
+
2995
+ @staticmethod
2996
+ def dtt(argv):
2997
+ parser = _new_parser(
2998
+ description=textwrap.dedent(
2999
+ f"""\
3000
+ {help_header}
3001
+
3002
+ Disparity through time (DTT) analysis. Computes how
3003
+ morphological disparity partitions among subclades
3004
+ through time (Harmon et al. 2003).
3005
+
3006
+ At each branching time, calculates the mean relative
3007
+ subclade disparity. Under Brownian motion, this declines
3008
+ linearly. The MDI (Morphological Disparity Index) is the
3009
+ area between the observed DTT and the BM null median.
3010
+
3011
+ Positive MDI = late disparity accumulation
3012
+ Negative MDI = early disparity accumulation (radiation)
3013
+
3014
+ Aliases:
3015
+ dtt, disparity_through_time
3016
+ Command line interfaces:
3017
+ pk_dtt, pk_disparity_through_time
3018
+
3019
+ Usage:
3020
+ phykit dtt -t <tree> --traits <traits_file>
3021
+ [--trait <column>] [--index avg_sq|avg_manhattan]
3022
+ [--nsim <int>] [--seed <int>]
3023
+ [--plot-output <file>] [--json]
3024
+
3025
+ Options
3026
+ =====================================================
3027
+ -t/--tree ultrametric tree file
3028
+ (required)
3029
+
3030
+ --traits TSV file with trait data
3031
+ (required)
3032
+
3033
+ --trait specific trait column name
3034
+ (default: all traits)
3035
+
3036
+ --index disparity index: avg_sq
3037
+ (average squared Euclidean
3038
+ distance, default) or
3039
+ avg_manhattan
3040
+
3041
+ --nsim number of BM simulations
3042
+ for null DTT envelope and
3043
+ MDI p-value (default: 0,
3044
+ no simulations)
3045
+
3046
+ --seed random seed for
3047
+ reproducibility
3048
+
3049
+ --plot-output output figure path
3050
+
3051
+ --json output results as JSON
3052
+ """
3053
+ ),
3054
+ )
3055
+ parser.add_argument("-t", "--tree", type=str, required=True, help=SUPPRESS, metavar="")
3056
+ parser.add_argument("--traits", type=str, required=True, help=SUPPRESS, metavar="")
3057
+ parser.add_argument("--trait", type=str, default=None, help=SUPPRESS, metavar="")
3058
+ parser.add_argument("--index", type=str, default="avg_sq", choices=["avg_sq", "avg_manhattan"], help=SUPPRESS, metavar="")
3059
+ parser.add_argument("--nsim", type=int, default=0, help=SUPPRESS, metavar="")
3060
+ parser.add_argument("--seed", type=int, default=None, help=SUPPRESS, metavar="")
3061
+ parser.add_argument("--plot-output", type=str, default=None, help=SUPPRESS, metavar="")
3062
+ add_plot_arguments(parser)
3063
+ _add_json_argument(parser)
3064
+ _run_service(parser, argv, Dtt)
3065
+
2925
3066
  @staticmethod
2926
3067
  def bipartition_support_stats(argv):
2927
3068
  parser = _new_parser(
@@ -9069,6 +9210,14 @@ def concordance_asr(argv=None):
9069
9210
  Phykit.concordance_asr(sys.argv[1:])
9070
9211
 
9071
9212
 
9213
+ def chronogram(argv=None):
9214
+ Phykit.chronogram(sys.argv[1:])
9215
+
9216
+
9217
+ def dtt(argv=None):
9218
+ Phykit.dtt(sys.argv[1:])
9219
+
9220
+
9072
9221
  def bipartition_support_stats(argv=None):
9073
9222
  Phykit.bipartition_support_stats(sys.argv[1:])
9074
9223
 
@@ -29,6 +29,7 @@ CompositionalBiasPerSite = _LazyServiceFactory("phykit.services.alignment.compos
29
29
  CompositionPerTaxon = _LazyServiceFactory("phykit.services.alignment.composition_per_taxon", "CompositionPerTaxon")
30
30
  CreateConcatenationMatrix = _LazyServiceFactory("phykit.services.alignment.create_concatenation_matrix", "CreateConcatenationMatrix")
31
31
  DNAThreader = _LazyServiceFactory("phykit.services.alignment.dna_threader", "DNAThreader")
32
+ Dtt = _LazyServiceFactory("phykit.services.tree.dtt", "Dtt")
32
33
  Dstatistic = _LazyServiceFactory("phykit.services.alignment.dstatistic", "Dstatistic")
33
34
  Dfoil = _LazyServiceFactory("phykit.services.alignment.dfoil", "Dfoil")
34
35
  EvolutionaryRatePerSite = _LazyServiceFactory("phykit.services.alignment.evolutionary_rate_per_site", "EvolutionaryRatePerSite")
@@ -59,6 +60,7 @@ BipartitionSupportStats = _LazyServiceFactory("phykit.services.tree.bipartition_
59
60
  BranchLengthMultiplier = _LazyServiceFactory("phykit.services.tree.branch_length_multiplier", "BranchLengthMultiplier")
60
61
  CollapseBranches = _LazyServiceFactory("phykit.services.tree.collapse_branches", "CollapseBranches")
61
62
  CovaryingEvolutionaryRates = _LazyServiceFactory("phykit.services.tree.covarying_evolutionary_rates", "CovaryingEvolutionaryRates")
63
+ Chronogram = _LazyServiceFactory("phykit.services.tree.chronogram", "Chronogram")
62
64
  ConsensusNetwork = _LazyServiceFactory("phykit.services.tree.consensus_network", "ConsensusNetwork")
63
65
  NeighborNet = _LazyServiceFactory("phykit.services.tree.neighbor_net", "NeighborNet")
64
66
  ConsensusTree = _LazyServiceFactory("phykit.services.tree.consensus_tree", "ConsensusTree")
@@ -5,11 +5,13 @@ _EXPORTS = {
5
5
  "BipartitionSupportStats": "bipartition_support_stats",
6
6
  "BranchLengthMultiplier": "branch_length_multiplier",
7
7
  "CollapseBranches": "collapse_branches",
8
+ "Chronogram": "chronogram",
8
9
  "CovaryingEvolutionaryRates": "covarying_evolutionary_rates",
9
10
  "ConsensusNetwork": "consensus_network",
10
11
  "NeighborNet": "neighbor_net",
11
12
  "ConsensusTree": "consensus_tree",
12
13
  "DVMC": "dvmc",
14
+ "Dtt": "dtt",
13
15
  "DiscordanceAsymmetry": "discordance_asymmetry",
14
16
  "EvolutionaryRate": "evolutionary_rate",
15
17
  "EvoTempoMap": "evo_tempo_map",