phykit 2.1.67__tar.gz → 2.1.68__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 (132) hide show
  1. {phykit-2.1.67 → phykit-2.1.68}/PKG-INFO +1 -1
  2. {phykit-2.1.67 → phykit-2.1.68}/phykit/phykit.py +18 -0
  3. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/ancestral_reconstruction.py +63 -2
  4. phykit-2.1.68/phykit/version.py +1 -0
  5. {phykit-2.1.67 → phykit-2.1.68}/phykit.egg-info/PKG-INFO +1 -1
  6. phykit-2.1.67/phykit/version.py +0 -1
  7. {phykit-2.1.67 → phykit-2.1.68}/LICENSE.md +0 -0
  8. {phykit-2.1.67 → phykit-2.1.68}/README.md +0 -0
  9. {phykit-2.1.67 → phykit-2.1.68}/phykit/__init__.py +0 -0
  10. {phykit-2.1.67 → phykit-2.1.68}/phykit/__main__.py +0 -0
  11. {phykit-2.1.67 → phykit-2.1.68}/phykit/cli_registry.py +0 -0
  12. {phykit-2.1.67 → phykit-2.1.68}/phykit/errors.py +0 -0
  13. {phykit-2.1.67 → phykit-2.1.68}/phykit/helpers/__init__.py +0 -0
  14. {phykit-2.1.67 → phykit-2.1.68}/phykit/helpers/boolean_argument_parsing.py +0 -0
  15. {phykit-2.1.67 → phykit-2.1.68}/phykit/helpers/caching.py +0 -0
  16. {phykit-2.1.67 → phykit-2.1.68}/phykit/helpers/circular_layout.py +0 -0
  17. {phykit-2.1.67 → phykit-2.1.68}/phykit/helpers/color_annotations.py +0 -0
  18. {phykit-2.1.67 → phykit-2.1.68}/phykit/helpers/discrete_models.py +0 -0
  19. {phykit-2.1.67 → phykit-2.1.68}/phykit/helpers/files.py +0 -0
  20. {phykit-2.1.67 → phykit-2.1.68}/phykit/helpers/json_output.py +0 -0
  21. {phykit-2.1.67 → phykit-2.1.68}/phykit/helpers/parallel.py +0 -0
  22. {phykit-2.1.67 → phykit-2.1.68}/phykit/helpers/parsimony_utils.py +0 -0
  23. {phykit-2.1.67 → phykit-2.1.68}/phykit/helpers/plot_config.py +0 -0
  24. {phykit-2.1.67 → phykit-2.1.68}/phykit/helpers/quartet_utils.py +0 -0
  25. {phykit-2.1.67 → phykit-2.1.68}/phykit/helpers/stats_summary.py +0 -0
  26. {phykit-2.1.67 → phykit-2.1.68}/phykit/helpers/streaming.py +0 -0
  27. {phykit-2.1.67 → phykit-2.1.68}/phykit/service_factories.py +0 -0
  28. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/__init__.py +0 -0
  29. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/__init__.py +0 -0
  30. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/alignment_entropy.py +0 -0
  31. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/alignment_length.py +0 -0
  32. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/alignment_length_no_gaps.py +0 -0
  33. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/alignment_outlier_taxa.py +0 -0
  34. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/alignment_recoding.py +0 -0
  35. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/alignment_subsample.py +0 -0
  36. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/base.py +0 -0
  37. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/column_score.py +0 -0
  38. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/composition_per_taxon.py +0 -0
  39. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/compositional_bias_per_site.py +0 -0
  40. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/create_concatenation_matrix.py +0 -0
  41. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/dfoil.py +0 -0
  42. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/dna_threader.py +0 -0
  43. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/dstatistic.py +0 -0
  44. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/evolutionary_rate_per_site.py +0 -0
  45. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/faidx.py +0 -0
  46. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/gc_content.py +0 -0
  47. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/identity_matrix.py +0 -0
  48. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/mask_alignment.py +0 -0
  49. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/occupancy_per_taxon.py +0 -0
  50. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/pairwise_identity.py +0 -0
  51. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/parsimony_informative_sites.py +0 -0
  52. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/plot_alignment_qc.py +0 -0
  53. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/rcv.py +0 -0
  54. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/rcvt.py +0 -0
  55. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/rename_fasta_entries.py +0 -0
  56. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/sum_of_pairs_score.py +0 -0
  57. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/alignment/variable_sites.py +0 -0
  58. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/base.py +0 -0
  59. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/__init__.py +0 -0
  60. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/base.py +0 -0
  61. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/bipartition_support_stats.py +0 -0
  62. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/branch_length_multiplier.py +0 -0
  63. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/character_map.py +0 -0
  64. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/collapse_branches.py +0 -0
  65. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/concordance_asr.py +0 -0
  66. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/consensus_network.py +0 -0
  67. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/consensus_tree.py +0 -0
  68. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/cont_map.py +0 -0
  69. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/cophylo.py +0 -0
  70. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/covarying_evolutionary_rates.py +0 -0
  71. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/density_map.py +0 -0
  72. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/discordance_asymmetry.py +0 -0
  73. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/dvmc.py +0 -0
  74. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/evo_tempo_map.py +0 -0
  75. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/evolutionary_rate.py +0 -0
  76. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/fit_continuous.py +0 -0
  77. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/fit_discrete.py +0 -0
  78. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/hidden_paralogy_check.py +0 -0
  79. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/independent_contrasts.py +0 -0
  80. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/internal_branch_stats.py +0 -0
  81. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/internode_labeler.py +0 -0
  82. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/kf_distance.py +0 -0
  83. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/last_common_ancestor_subtree.py +0 -0
  84. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/lb_score.py +0 -0
  85. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/ltt.py +0 -0
  86. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/monophyly_check.py +0 -0
  87. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/nearest_neighbor_interchange.py +0 -0
  88. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/network_signal.py +0 -0
  89. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/ou_shift_detection.py +0 -0
  90. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/ouwie.py +0 -0
  91. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/parsimony_score.py +0 -0
  92. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/patristic_distances.py +0 -0
  93. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/phenogram.py +0 -0
  94. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/phylo_heatmap.py +0 -0
  95. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/phylogenetic_glm.py +0 -0
  96. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/phylogenetic_ordination.py +0 -0
  97. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/phylogenetic_regression.py +0 -0
  98. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/phylogenetic_signal.py +0 -0
  99. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/phylomorphospace.py +0 -0
  100. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/polytomy_test.py +0 -0
  101. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/print_tree.py +0 -0
  102. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/prune_tree.py +0 -0
  103. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/quartet_network.py +0 -0
  104. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/quartet_pie.py +0 -0
  105. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/rate_heterogeneity.py +0 -0
  106. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/relative_rate_test.py +0 -0
  107. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/rename_tree_tips.py +0 -0
  108. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/rf_distance.py +0 -0
  109. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/root_tree.py +0 -0
  110. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/saturation.py +0 -0
  111. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/spectral_discordance.py +0 -0
  112. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/spurious_sequence.py +0 -0
  113. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/stochastic_character_map.py +0 -0
  114. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/terminal_branch_stats.py +0 -0
  115. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/threshold_model.py +0 -0
  116. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/tip_labels.py +0 -0
  117. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/tip_to_tip_distance.py +0 -0
  118. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/tip_to_tip_node_distance.py +0 -0
  119. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/total_tree_length.py +0 -0
  120. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/trait_correlation.py +0 -0
  121. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/trait_rate_map.py +0 -0
  122. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/tree_space.py +0 -0
  123. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/treeness.py +0 -0
  124. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/treeness_over_rcv.py +0 -0
  125. {phykit-2.1.67 → phykit-2.1.68}/phykit/services/tree/vcv_utils.py +0 -0
  126. {phykit-2.1.67 → phykit-2.1.68}/phykit.egg-info/SOURCES.txt +0 -0
  127. {phykit-2.1.67 → phykit-2.1.68}/phykit.egg-info/dependency_links.txt +0 -0
  128. {phykit-2.1.67 → phykit-2.1.68}/phykit.egg-info/entry_points.txt +0 -0
  129. {phykit-2.1.67 → phykit-2.1.68}/phykit.egg-info/requires.txt +0 -0
  130. {phykit-2.1.67 → phykit-2.1.68}/phykit.egg-info/top_level.txt +0 -0
  131. {phykit-2.1.67 → phykit-2.1.68}/setup.cfg +0 -0
  132. {phykit-2.1.67 → phykit-2.1.68}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: phykit
3
- Version: 2.1.67
3
+ Version: 2.1.68
4
4
  Home-page: https://github.com/jlsteenwyk/phykit
5
5
  Author: Jacob L. Steenwyk
6
6
  Author-email: jlsteenwyk@gmail.com
@@ -2431,6 +2431,16 @@ class Phykit:
2431
2431
  and branch colors (iTOL-
2432
2432
  inspired TSV format)
2433
2433
 
2434
+ --plot-ci draw confidence interval bars
2435
+ at internal nodes on the
2436
+ contMap plot (requires --ci
2437
+ and --plot)
2438
+
2439
+ --ci-size scale factor for CI bar
2440
+ size (default: 1.0; use
2441
+ 2.0 for larger, 0.5 for
2442
+ smaller)
2443
+
2434
2444
  --json output results as JSON
2435
2445
  """
2436
2446
  ),
@@ -2464,6 +2474,14 @@ class Phykit:
2464
2474
  "--plot", type=str, required=False, default=None,
2465
2475
  help=SUPPRESS, metavar=""
2466
2476
  )
2477
+ parser.add_argument(
2478
+ "--plot-ci", action="store_true", required=False, default=False,
2479
+ help=SUPPRESS,
2480
+ )
2481
+ parser.add_argument(
2482
+ "--ci-size", type=float, required=False, default=1.0,
2483
+ help=SUPPRESS, metavar=""
2484
+ )
2467
2485
  add_plot_arguments(parser)
2468
2486
  _add_json_argument(parser)
2469
2487
  _run_service(parser, argv, AncestralReconstruction)
@@ -48,6 +48,8 @@ class AncestralReconstruction(Tree):
48
48
  self.json_output = parsed["json_output"]
49
49
  self.trait_type = parsed["trait_type"]
50
50
  self.model = parsed["model"]
51
+ self.plot_ci = parsed["plot_ci"]
52
+ self.ci_size = parsed["ci_size"]
51
53
  self.plot_config = parsed["plot_config"]
52
54
 
53
55
  def run(self) -> None:
@@ -112,9 +114,11 @@ class AncestralReconstruction(Tree):
112
114
  )
113
115
 
114
116
  if self.plot_output:
117
+ ci_data = node_cis if (self.plot_ci and self.ci and node_cis) else None
115
118
  self._plot_contmap(
116
119
  tree_copy, node_estimates, node_labels,
117
- trait_values, trait_name, self.plot_output
120
+ trait_values, trait_name, self.plot_output,
121
+ node_cis=ci_data,
118
122
  )
119
123
  result["plot_output"] = self.plot_output
120
124
 
@@ -144,6 +148,8 @@ class AncestralReconstruction(Tree):
144
148
  json_output=getattr(args, "json", False),
145
149
  trait_type=getattr(args, "type", "continuous"),
146
150
  model=getattr(args, "model", "ER"),
151
+ plot_ci=getattr(args, "plot_ci", False),
152
+ ci_size=getattr(args, "ci_size", 1.0),
147
153
  plot_config=PlotConfig.from_args(args),
148
154
  )
149
155
 
@@ -809,7 +815,7 @@ class AncestralReconstruction(Tree):
809
815
 
810
816
  def _plot_contmap(
811
817
  self, tree, node_estimates, node_labels, trait_values,
812
- trait_name, output_path,
818
+ trait_name, output_path, node_cis=None,
813
819
  ) -> None:
814
820
  try:
815
821
  import matplotlib
@@ -827,6 +833,7 @@ class AncestralReconstruction(Tree):
827
833
 
828
834
  # Build estimates dict keyed by id(clade) for all nodes
829
835
  all_estimates = {}
836
+ node_labels_map = {} # id(clade) → label string (for CI lookup)
830
837
  for clade in tree.find_clades(order="preorder"):
831
838
  if clade.is_terminal():
832
839
  if clade.name in trait_values:
@@ -834,6 +841,7 @@ class AncestralReconstruction(Tree):
834
841
  else:
835
842
  if id(clade) in node_labels:
836
843
  label = node_labels[id(clade)]
844
+ node_labels_map[id(clade)] = label
837
845
  if label in node_estimates:
838
846
  all_estimates[id(clade)] = node_estimates[label]
839
847
 
@@ -1043,6 +1051,59 @@ class AncestralReconstruction(Tree):
1043
1051
  fontsize=config.title_fontsize,
1044
1052
  )
1045
1053
 
1054
+ # Draw CI bars at internal nodes if requested
1055
+ if node_cis:
1056
+ ci_scale = self.ci_size
1057
+ # Build label→id mapping for looking up CI values
1058
+ label_to_id = {}
1059
+ for clade in tree.find_clades(order="preorder"):
1060
+ if not clade.is_terminal() and id(clade) in node_labels_map:
1061
+ label_to_id[node_labels_map[id(clade)]] = id(clade)
1062
+
1063
+ if config.circular:
1064
+ # Circular: use data coordinates from coords dict
1065
+ for label, (ci_lo, ci_hi) in node_cis.items():
1066
+ cid = label_to_id.get(label)
1067
+ if cid is None or cid not in coords:
1068
+ continue
1069
+ cx = coords[cid]["x"]
1070
+ cy = coords[cid]["y"]
1071
+ angle = coords[cid]["angle"]
1072
+ # CI bar perpendicular to the radius (tangential)
1073
+ bar_len = (ci_hi - ci_lo) * ci_scale * 0.3
1074
+ dx = -np.sin(angle) * bar_len / 2
1075
+ dy = np.cos(angle) * bar_len / 2
1076
+ # Main bar
1077
+ ax.plot([cx - dx, cx + dx], [cy - dy, cy + dy],
1078
+ color="black", lw=1.5 * ci_scale, zorder=8)
1079
+ # Point estimate dot
1080
+ ax.scatter(cx, cy, s=15 * ci_scale, c="black", zorder=9)
1081
+ else:
1082
+ # Rectangular: vertical bars at node positions
1083
+ max_x_val = max(node_x.values()) if node_x else 1.0
1084
+ for label, (ci_lo, ci_hi) in node_cis.items():
1085
+ cid = label_to_id.get(label)
1086
+ if cid is None or cid not in node_x or cid not in node_y:
1087
+ continue
1088
+ cx = node_x[cid]
1089
+ cy = node_y[cid]
1090
+ est = all_estimates.get(cid, (ci_lo + ci_hi) / 2)
1091
+ # Scale CI width relative to y-axis range
1092
+ bar_half = (ci_hi - ci_lo) / (vmax - vmin) * 0.4 * ci_scale if vmax != vmin else 0.2 * ci_scale
1093
+ cap_width = max_x_val * 0.008 * ci_scale
1094
+ # Vertical bar
1095
+ ax.plot([cx, cx], [cy - bar_half, cy + bar_half],
1096
+ color="black", lw=1.2 * ci_scale, zorder=8)
1097
+ # Caps
1098
+ ax.plot([cx - cap_width, cx + cap_width],
1099
+ [cy - bar_half, cy - bar_half],
1100
+ color="black", lw=1.0 * ci_scale, zorder=8)
1101
+ ax.plot([cx - cap_width, cx + cap_width],
1102
+ [cy + bar_half, cy + bar_half],
1103
+ color="black", lw=1.0 * ci_scale, zorder=8)
1104
+ # Point estimate dot
1105
+ ax.scatter(cx, cy, s=12 * ci_scale, c="black", zorder=9)
1106
+
1046
1107
  fig.tight_layout()
1047
1108
  fig.savefig(output_path, dpi=config.dpi, bbox_inches="tight")
1048
1109
  plt.close(fig)
@@ -0,0 +1 @@
1
+ __version__ = "2.1.68"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: phykit
3
- Version: 2.1.67
3
+ Version: 2.1.68
4
4
  Home-page: https://github.com/jlsteenwyk/phykit
5
5
  Author: Jacob L. Steenwyk
6
6
  Author-email: jlsteenwyk@gmail.com
@@ -1 +0,0 @@
1
- __version__ = "2.1.67"
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