phykit 2.1.35__tar.gz → 2.1.38__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 (112) hide show
  1. {phykit-2.1.35 → phykit-2.1.38}/PKG-INFO +1 -1
  2. {phykit-2.1.35 → phykit-2.1.38}/phykit/cli_registry.py +2 -0
  3. {phykit-2.1.35 → phykit-2.1.38}/phykit/phykit.py +119 -10
  4. {phykit-2.1.35 → phykit-2.1.38}/phykit/service_factories.py +1 -0
  5. phykit-2.1.38/phykit/services/tree/spectral_discordance.py +643 -0
  6. phykit-2.1.38/phykit/version.py +1 -0
  7. {phykit-2.1.35 → phykit-2.1.38}/phykit.egg-info/PKG-INFO +1 -1
  8. {phykit-2.1.35 → phykit-2.1.38}/phykit.egg-info/SOURCES.txt +1 -0
  9. {phykit-2.1.35 → phykit-2.1.38}/phykit.egg-info/entry_points.txt +8 -0
  10. {phykit-2.1.35 → phykit-2.1.38}/setup.py +8 -0
  11. phykit-2.1.35/phykit/version.py +0 -1
  12. {phykit-2.1.35 → phykit-2.1.38}/LICENSE.md +0 -0
  13. {phykit-2.1.35 → phykit-2.1.38}/README.md +0 -0
  14. {phykit-2.1.35 → phykit-2.1.38}/phykit/__init__.py +0 -0
  15. {phykit-2.1.35 → phykit-2.1.38}/phykit/__main__.py +0 -0
  16. {phykit-2.1.35 → phykit-2.1.38}/phykit/errors.py +0 -0
  17. {phykit-2.1.35 → phykit-2.1.38}/phykit/helpers/__init__.py +0 -0
  18. {phykit-2.1.35 → phykit-2.1.38}/phykit/helpers/boolean_argument_parsing.py +0 -0
  19. {phykit-2.1.35 → phykit-2.1.38}/phykit/helpers/caching.py +0 -0
  20. {phykit-2.1.35 → phykit-2.1.38}/phykit/helpers/files.py +0 -0
  21. {phykit-2.1.35 → phykit-2.1.38}/phykit/helpers/json_output.py +0 -0
  22. {phykit-2.1.35 → phykit-2.1.38}/phykit/helpers/parallel.py +0 -0
  23. {phykit-2.1.35 → phykit-2.1.38}/phykit/helpers/stats_summary.py +0 -0
  24. {phykit-2.1.35 → phykit-2.1.38}/phykit/helpers/streaming.py +0 -0
  25. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/__init__.py +0 -0
  26. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/alignment/__init__.py +0 -0
  27. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/alignment/alignment_entropy.py +0 -0
  28. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/alignment/alignment_length.py +0 -0
  29. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/alignment/alignment_length_no_gaps.py +0 -0
  30. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/alignment/alignment_outlier_taxa.py +0 -0
  31. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/alignment/alignment_recoding.py +0 -0
  32. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/alignment/base.py +0 -0
  33. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/alignment/column_score.py +0 -0
  34. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/alignment/composition_per_taxon.py +0 -0
  35. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/alignment/compositional_bias_per_site.py +0 -0
  36. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/alignment/create_concatenation_matrix.py +0 -0
  37. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/alignment/dna_threader.py +0 -0
  38. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/alignment/evolutionary_rate_per_site.py +0 -0
  39. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/alignment/faidx.py +0 -0
  40. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/alignment/gc_content.py +0 -0
  41. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/alignment/mask_alignment.py +0 -0
  42. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/alignment/occupancy_per_taxon.py +0 -0
  43. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/alignment/pairwise_identity.py +0 -0
  44. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/alignment/parsimony_informative_sites.py +0 -0
  45. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/alignment/plot_alignment_qc.py +0 -0
  46. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/alignment/rcv.py +0 -0
  47. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/alignment/rcvt.py +0 -0
  48. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/alignment/rename_fasta_entries.py +0 -0
  49. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/alignment/sum_of_pairs_score.py +0 -0
  50. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/alignment/variable_sites.py +0 -0
  51. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/base.py +0 -0
  52. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/__init__.py +0 -0
  53. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/ancestral_reconstruction.py +0 -0
  54. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/base.py +0 -0
  55. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/bipartition_support_stats.py +0 -0
  56. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/branch_length_multiplier.py +0 -0
  57. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/collapse_branches.py +0 -0
  58. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/concordance_asr.py +0 -0
  59. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/consensus_network.py +0 -0
  60. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/consensus_tree.py +0 -0
  61. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/cont_map.py +0 -0
  62. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/cophylo.py +0 -0
  63. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/covarying_evolutionary_rates.py +0 -0
  64. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/density_map.py +0 -0
  65. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/discordance_asymmetry.py +0 -0
  66. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/dvmc.py +0 -0
  67. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/evo_tempo_map.py +0 -0
  68. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/evolutionary_rate.py +0 -0
  69. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/fit_continuous.py +0 -0
  70. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/hidden_paralogy_check.py +0 -0
  71. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/internal_branch_stats.py +0 -0
  72. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/internode_labeler.py +0 -0
  73. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/last_common_ancestor_subtree.py +0 -0
  74. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/lb_score.py +0 -0
  75. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/ltt.py +0 -0
  76. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/monophyly_check.py +0 -0
  77. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/nearest_neighbor_interchange.py +0 -0
  78. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/network_signal.py +0 -0
  79. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/ou_shift_detection.py +0 -0
  80. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/ouwie.py +0 -0
  81. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/patristic_distances.py +0 -0
  82. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/phenogram.py +0 -0
  83. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/phylogenetic_glm.py +0 -0
  84. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/phylogenetic_ordination.py +0 -0
  85. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/phylogenetic_regression.py +0 -0
  86. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/phylogenetic_signal.py +0 -0
  87. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/phylomorphospace.py +0 -0
  88. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/polytomy_test.py +0 -0
  89. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/print_tree.py +0 -0
  90. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/prune_tree.py +0 -0
  91. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/quartet_network.py +0 -0
  92. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/rate_heterogeneity.py +0 -0
  93. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/relative_rate_test.py +0 -0
  94. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/rename_tree_tips.py +0 -0
  95. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/rf_distance.py +0 -0
  96. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/root_tree.py +0 -0
  97. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/saturation.py +0 -0
  98. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/spurious_sequence.py +0 -0
  99. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/stochastic_character_map.py +0 -0
  100. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/terminal_branch_stats.py +0 -0
  101. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/threshold_model.py +0 -0
  102. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/tip_labels.py +0 -0
  103. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/tip_to_tip_distance.py +0 -0
  104. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/tip_to_tip_node_distance.py +0 -0
  105. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/total_tree_length.py +0 -0
  106. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/treeness.py +0 -0
  107. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/treeness_over_rcv.py +0 -0
  108. {phykit-2.1.35 → phykit-2.1.38}/phykit/services/tree/vcv_utils.py +0 -0
  109. {phykit-2.1.35 → phykit-2.1.38}/phykit.egg-info/dependency_links.txt +0 -0
  110. {phykit-2.1.35 → phykit-2.1.38}/phykit.egg-info/requires.txt +0 -0
  111. {phykit-2.1.35 → phykit-2.1.38}/phykit.egg-info/top_level.txt +0 -0
  112. {phykit-2.1.35 → phykit-2.1.38}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: phykit
3
- Version: 2.1.35
3
+ Version: 2.1.38
4
4
  Home-page: https://github.com/jlsteenwyk/phykit
5
5
  Author: Jacob L. Steenwyk
6
6
  Author-email: jlsteenwyk@gmail.com
@@ -157,6 +157,8 @@ ALIAS_TO_HANDLER: Dict[str, str] = {
157
157
  "etm": "evo_tempo_map",
158
158
  "disc_asym": "discordance_asymmetry",
159
159
  "da": "discordance_asymmetry",
160
+ "spec_disc": "spectral_discordance",
161
+ "sd": "spectral_discordance",
160
162
  # Helper aliases
161
163
  "create_concat": "create_concatenation_matrix",
162
164
  "cc": "create_concatenation_matrix",
@@ -277,7 +277,10 @@ class Phykit:
277
277
  treeness (alias: tness)
278
278
  - reports treeness or stemminess, a measure of signal-to-
279
279
  noise ratio in a phylogeny
280
-
280
+ spectral_discordance (alias: spec_disc; sd)
281
+ - PCA + spectral clustering of gene tree space via
282
+ bipartition decomposition
283
+
281
284
  Alignment- and tree-based commands
282
285
  ==================================
283
286
  saturation (alias: sat)
@@ -288,9 +291,13 @@ class Phykit:
288
291
  """
289
292
  ),
290
293
  )
291
- parser.add_argument("command", help=SUPPRESS)
294
+ parser.add_argument("command", nargs="?", default=None, help=SUPPRESS)
292
295
  args = parser.parse_args(sys.argv[1:2])
293
296
 
297
+ if args.command is None:
298
+ parser.print_help()
299
+ sys.exit(0)
300
+
294
301
  # if command is part of the possible commands (i.e., the long form
295
302
  # commands, run). Otherwise, assume it is an alias and look to the
296
303
  # run_alias function
@@ -302,7 +309,8 @@ class Phykit:
302
309
  except SystemExit:
303
310
  # Re-raise SystemExit as-is to preserve exit code
304
311
  raise
305
- except NameError:
312
+ except NameError as e:
313
+ print(f"Error: {e}", file=sys.stderr)
306
314
  sys.exit(2)
307
315
 
308
316
  ## Aliases
@@ -491,7 +499,7 @@ class Phykit:
491
499
  Aliases:
492
500
  alignment_recoding, aln_recoding, recode
493
501
  Command line interfaces:
494
- bk_alignment_recoding, bk_aln_recoding, bk_recode
502
+ pk_alignment_recoding, pk_aln_recoding, pk_recode
495
503
 
496
504
  Usage:
497
505
  phykit alignment_recoding <fasta> -c/--code <code> [--json]
@@ -1203,7 +1211,7 @@ class Phykit:
1203
1211
  f"""\
1204
1212
  {help_header}
1205
1213
 
1206
- Calculate the number and percentage of parismony
1214
+ Calculate the number and percentage of parsimony
1207
1215
  informative sites in an alignment.
1208
1216
 
1209
1217
  The number of parsimony informative sites in an alignment
@@ -1850,13 +1858,13 @@ class Phykit:
1850
1858
 
1851
1859
  Options
1852
1860
  =====================================================
1853
- <tree_file_zero> first argument after
1861
+ <tree_file_zero> first argument after
1854
1862
  function name should be
1855
- an alignment file
1863
+ a tree file
1856
1864
 
1857
- <tree_file_one> first argument after
1865
+ <tree_file_one> second argument after
1858
1866
  function name should be
1859
- an alignment file
1867
+ a tree file
1860
1868
 
1861
1869
  -r/--reference a tree to correct branch
1862
1870
  lengths by in the two input
@@ -4511,7 +4519,7 @@ class Phykit:
4511
4519
  a tree file
4512
4520
 
4513
4521
  -v/--verbose optional argument to print
4514
- all internal branch lengths
4522
+ all terminal branch lengths
4515
4523
 
4516
4524
  --json optional argument to output
4517
4525
  results as JSON
@@ -5000,6 +5008,95 @@ class Phykit:
5000
5008
  _add_json_argument(parser)
5001
5009
  _run_service(parser, argv, DiscordanceAsymmetry)
5002
5010
 
5011
+ @staticmethod
5012
+ def spectral_discordance(argv):
5013
+ parser = _new_parser(
5014
+ description=textwrap.dedent(
5015
+ f"""\
5016
+ {help_header}
5017
+
5018
+ Spectral discordance decomposition — decompose gene tree
5019
+ space via PCA on a bipartition presence/absence (or
5020
+ branch-length) matrix, with spectral clustering and
5021
+ automatic cluster detection via the eigengap heuristic.
5022
+
5023
+ Each gene tree is encoded as a vector over the union of
5024
+ all bipartitions observed across gene trees. PCA reveals
5025
+ the axes of topological variation, with loading vectors
5026
+ identifying which bipartitions drive each PC. Spectral
5027
+ clustering groups genes sharing similar topologies.
5028
+
5029
+ Two metrics are available:
5030
+ - nrf (default): binary presence/absence (normalized RF)
5031
+ - wrf: branch-length weighted
5032
+
5033
+ Aliases:
5034
+ spectral_discordance, spec_disc, sd
5035
+ Command line interfaces:
5036
+ pk_spectral_discordance, pk_spec_disc, pk_sd
5037
+
5038
+ Usage:
5039
+ phykit spectral_discordance -g <gene_trees> [-t <tree>] [--metric nrf|wrf] [--clusters K] [--n-pcs N] [--top-loadings N] [--plot <prefix>] [--json]
5040
+
5041
+ Options
5042
+ =====================================================
5043
+ -g/--gene-trees file of gene trees (one
5044
+ Newick per line, or file
5045
+ of filenames)
5046
+
5047
+ -t/--tree species tree (optional; flags
5048
+ species-tree bipartitions in
5049
+ loading output)
5050
+
5051
+ --metric distance metric: nrf or wrf
5052
+ (default: nrf)
5053
+
5054
+ --clusters override auto-detected K
5055
+
5056
+ --n-pcs number of PCs to report
5057
+ (default: min(10, G-1))
5058
+
5059
+ --top-loadings top bipartitions per PC
5060
+ (default: 5)
5061
+
5062
+ --plot output prefix for plots
5063
+ (generates _scatter.png and
5064
+ _eigengap.png)
5065
+
5066
+ --json output results as JSON
5067
+ """
5068
+ ),
5069
+ )
5070
+ parser.add_argument(
5071
+ "-g", "--gene-trees", type=str, required=True, help=SUPPRESS, metavar=""
5072
+ )
5073
+ parser.add_argument(
5074
+ "-t", "--tree", type=str, required=False, default=None,
5075
+ help=SUPPRESS, metavar=""
5076
+ )
5077
+ parser.add_argument(
5078
+ "--metric", type=str, required=False, default="nrf",
5079
+ choices=["nrf", "wrf"], help=SUPPRESS, metavar=""
5080
+ )
5081
+ parser.add_argument(
5082
+ "--clusters", type=int, required=False, default=None,
5083
+ help=SUPPRESS, metavar=""
5084
+ )
5085
+ parser.add_argument(
5086
+ "--n-pcs", type=int, required=False, default=None,
5087
+ help=SUPPRESS, metavar=""
5088
+ )
5089
+ parser.add_argument(
5090
+ "--top-loadings", type=int, required=False, default=5,
5091
+ help=SUPPRESS, metavar=""
5092
+ )
5093
+ parser.add_argument(
5094
+ "--plot", type=str, required=False, default=None,
5095
+ help=SUPPRESS, metavar=""
5096
+ )
5097
+ _add_json_argument(parser)
5098
+ _run_service(parser, argv, SpectralDiscordance)
5099
+
5003
5100
  ### Helper commands
5004
5101
  @staticmethod
5005
5102
  def create_concatenation_matrix(argv):
@@ -5453,3 +5550,15 @@ def create_concatenation_matrix(argv=None):
5453
5550
 
5454
5551
  def thread_dna(argv=None):
5455
5552
  Phykit.thread_dna(sys.argv[1:])
5553
+
5554
+
5555
+ def evo_tempo_map(argv=None):
5556
+ Phykit.evo_tempo_map(sys.argv[1:])
5557
+
5558
+
5559
+ def discordance_asymmetry(argv=None):
5560
+ Phykit.discordance_asymmetry(sys.argv[1:])
5561
+
5562
+
5563
+ def spectral_discordance(argv=None):
5564
+ Phykit.spectral_discordance(sys.argv[1:])
@@ -98,6 +98,7 @@ Treeness = _LazyServiceFactory("phykit.services.tree.treeness", "Treeness")
98
98
  TreenessOverRCV = _LazyServiceFactory("phykit.services.tree.treeness_over_rcv", "TreenessOverRCV")
99
99
  EvoTempoMap = _LazyServiceFactory("phykit.services.tree.evo_tempo_map", "EvoTempoMap")
100
100
  DiscordanceAsymmetry = _LazyServiceFactory("phykit.services.tree.discordance_asymmetry", "DiscordanceAsymmetry")
101
+ SpectralDiscordance = _LazyServiceFactory("phykit.services.tree.spectral_discordance", "SpectralDiscordance")
101
102
 
102
103
  SERVICE_FACTORIES: Dict[str, _LazyServiceFactory] = {
103
104
  name: value