| Previous changeset 39:106a316b5313 (2026-02-25) |
|
Commit message:
planemo upload for repository https://github.com/galaxyproject/tools-iuc/tree/main/tools/hyphy commit cee1ce4bd7d82088b9bf62403bc175c13223e020 |
|
modified:
macros.xml |
|
added:
scripts/infer_stasis_clusters.py test-data/bstill-in1.json |
|
removed:
scripts/hyphy_summary.py |
| b |
| diff -r 106a316b5313 -r 3f8261f0a826 macros.xml --- a/macros.xml Wed Feb 25 20:57:09 2026 +0000 +++ b/macros.xml Wed Mar 11 11:15:12 2026 +0000 |
| b |
| @@ -1,7 +1,7 @@ <?xml version="1.0"?> <macros> - <token name="@TOOL_VERSION@">2.5.93</token> - <token name="@VERSION_SUFFIX@">3</token> + <token name="@TOOL_VERSION@">2.5.96</token> + <token name="@VERSION_SUFFIX@">0</token> <token name="@PROFILE@">24.0</token> <token name="@SHELL_OPTIONS@">export TERM="vt100"; </token> |
| b |
| diff -r 106a316b5313 -r 3f8261f0a826 scripts/hyphy_summary.py --- a/scripts/hyphy_summary.py Wed Feb 25 20:57:09 2026 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 |
| [ |
| b"@@ -1,679 +0,0 @@\n-import argparse\n-import json\n-import re\n-from collections import defaultdict\n-\n-import BioExt\n-from Bio import SeqIO\n-from Bio.Seq import Seq\n-from Bio.SeqRecord import SeqRecord\n-from BioExt.uds import _align_par\n-\n-\n-class HyPhySummary(object):\n-\n- def __init__(self, arguments, summary_json=None, annotation_json=None):\n- self.arguments = arguments\n- self.ref_map = ''\n- self.summary_json = {}\n- self.annotation_json = {}\n- self.include_in_annotation = {}\n- self.test_map = {}\n- self.site_reports = {}\n- self.labels = {}\n- self.ref_seq_map = []\n- self.cfel = {}\n- self.relax = {}\n- self.busted = {}\n- self.slac = {}\n- self.fel = {}\n- self.meme = {}\n- self.meme_full = {}\n- self.prime = {}\n- self.fade = {}\n- self.bgm = {}\n- self.ref_genes = [\n- ['genome', 'ATTAAAGGTTTATACCTTCCCAGGTAACAAACCAACCAACTTTCGATCTCTTGTAGATCTGTTCTCTAAACGAACTTTAAAATCTGTGTGGCTGTCACTCGGCTGCATGCTTAGTGCACTCACGCAGTATAATTAATAACTAATTACTGTCGTTGACAGGACACGAGTAACTCGTCTATCTTCTGCAGGCTGCTTACGGTTTCGTCCGTGTTGCAGCCGATCATCAGCACATCTAGGTTTCGTCCGGGTGTGACCGAAAGGTAAGATGGAGAGCCTTGTCCCTGGTTTCAACGAGAAAACACACGTCCAACTCAGTTTGCCTGTTTTACAGGTTCGCGACGTGCTCGTACGTGGCTTTGGAGACTCCGTGGAGGAGGTCTTATCAGAGGCACGTCAACATCTTAAAGATGGCACTTGTGGCTTAGTAGAAGTTGAAAAAGGCGTTTTGCCTCAACTTGAACAGCCCTATGTGTTCATCAAACGTTCGGATGCTCGAACTGCACCTCATGGTCATGTTATGGTTGAGCTGGTAGCAGAACTCGAAGGCATTCAGTACGGTCGTAGTGGTGAGACACTTGGTGTCCTTGTCCCTCATGTGGGCGAAATACCAGTGGCTTACCGCAAGGTTCTTCTTCGTAAGAACGGTAATAAAGGAGCTGGTGGCCATAGTTACGGCGCCGATCTAAAGTCATTTGACTTAGGCGACGAGCTTGGCACTGATCCTTATGAAGATTTTCAAGAAAACTGGAACACTAAACATAGCAGTGGTGTTACCCGTGAACTCATGCGTGAGCTTAACGGAGGGGCATACACTCGCTATGTCGATAACAACTTCTGTGGCCCTGATGGCTACCCTCTTGAGTGCATTAAAGACCTTCTAGCACGTGCTGGTAAAGCTTCATGCACTTTGTCCGAACAACTGGACTTTATTGACACTAAGAGGGGTGTATACTGCTGCCGTGAACATGAGCATGAAATTGCTTGGTACACGGAACGTTCTGAAAAGAGCTATGAATTGCAGACACCTTTTGAAATTAAATTGGCAAAGAAATTTGACACCTTCAATGGGGAATGTCCAAATTTTGTATTTCCCTTAAATTCCATAATCAAGACTATTCAACCAAGGGTTGAAAAGAAAAAGCTTGATGGCTTTATGGGTAGAATTCGATCTGTCTATCCAGTTGCGTCACCAAATGAATGCAACCAAATGTGCCTTTCAACTCTCATGAAGTGTGATCATTGTGGTGAAACTTCATGGCAGACGGGCGATTTTGTTAAAGCCACTTGCGAATTTTGTGGCACTGAGAATTTGACTAAAGAAGGTGCCACTACTTGTGGTTACTTACCCCAAAATGCTGTTGTTAAAATTTATTGTCCAGCATGTCACAATTCAGAAGTAGGACCTGAGCATAGTCTTGCCGAATACCATAATGAATCTGGCTTGAAAACCATTCTTCGTAAGGGTGGTCGCACTATTGCCTTTGGAGGCTGTGTGTTCTCTTATGTTGGTTGCCATAACAAGTGTGCCTATTGGGTTCCACGTGCTAGCGCTAACATAGGTTGTAACCATACAGGTGTTGTTGGAGAAGGTTCCGAAGGTCTTAATGACAACCTTCTTGAAATACTCCAAAAAGAGAAAGTCAACATCAATATTGTTGGTGACTTTAAACTTAATGAAGAGATCGCCATTATTTTGGCATCTTTTTCTGCTTCCACAAGTGCTTTTGTGGAAACTGTGAAAGGTTTGGATTATAAAGCATTCAAACAAATTGTTGAATCCTGTGGTAATTTTAAAGTTACAAAAGGAAAAGCTAAAAAAGGTGCCTGGAATATTGGTGAACAGAAATCAATACTGAGTCCTCTTTATGCATTTGCATCAGAGGCTGCTCGTGTTGTACGATCAATTTTCTCCCGCACTCTTGAAACTGCTCAAAATTCTGTGCGTGTTTTACAGAAGGCCGCTATAACAATACTAGATGGAATTTCACAGTATTCACTGAGACTCATTGATGCTATGATGTTCACATCTGATTTGGCTACTAACAATCTAGTTGTAATGGCCTACATTACAGGTGGTGTTGTTCAGTTGACTTCGCAGTGGCTAACTAACATCTTTGGCACTGTTTATGAAAAACTCAAACCCGTCCTTGATTGGCTTGAAGAGAAGTTTAAGGAAGGTGTAGAGTTTCTTAGAGACGGTTGGGAAATTGTTAAATTTATCTCAACCTGTGCTTGTGAAATTGTCGGTGGACAAATTGTCACCTGTGCAAAGGAAATTAAGGAGAGTGTTCAGACATTCTTTAAGCTTGTAAATAAATTTTTGGCTTTGTGTGCTGACTCTATCATTATTGGTGGAGCTAAACTTAAAGCCTTGAATTTAGGTGAAACATTTGTCACGCACTCAAAGGGATTGTACAGAAAGTGTGTTAAATCCAGAGAAGAAACTGGCCTACTCATGCCTCTAAAAGCCCCAAAAGAAATTATCTTCTTAGAGGGAGAAACACTTCCCACAGAAGTGTTAACAGAGGAAGTTGTCTTGAAAACTGGTGATTTACAACCATTAGAACAACCTACTAGTGAAGCTGTTGAAGCTCCATTGGTTGGTACACCAGTTTGTATTAACGGGCTTATGTTGCTCGAAATCAAAGACACAGAAAAGTACTGTGCCCTTGCACCTAATATGATGGTAACAAACAATACCTTCACACTCAAAGGCGGTGCACCAACAAAGGTTACTTTTGGTGATGACACTGTGATAGAAGTGCAAGGTTACAAGAGTGTGAATATCACTTTTGAACTTGATGAAAGGATTGATAAAGTACTTAATGAGAAGTGCTCTGCCTATACAGTTGAACTCGGTACAGAAGTAAATGAGTTCGCCTGTGTTGTGGCAGATGCTGTCATAAAAACTTTGCAACCAGTATCTGAATTACTTACACCACTGGGCATTGATTTAGATGAGTGGAGTATGGCTACATACTACTTATTTGATGAGTCTGGTGAGTTTAAATTGGCTTCACATATGTATTGTTCTTTCTACCCTCCAGATGAGGATGAAGAAGAAGG"..b" return generate_error(char_index)\n- current_node_annotation += current_char\n- except Exception as e:\n- return generate_error(char_index)\n- print(e)\n-\n- if (len(clade_stack) != 1):\n- return generate_error(len(nwk_str) - 1)\n-\n- if (len(current_node_name)):\n- tree_json['name'] = current_node_name\n-\n- return {\n- 'json': tree_json,\n- 'error': None\n- }\n-\n-\n-if __name__ == '__main__':\n- parser = argparse.ArgumentParser(description='Summarize selection analysis results.')\n- parser.add_argument('--combined', help='Combined reference and query alignment from TN-93', required=False, type=str)\n- parser.add_argument('--pvalue', help='p-value to use', required=False, type=float, default=0.05)\n- parser.add_argument('--gene', help='Name of the gene or sequence being analyzed', required=False, type=str)\n- parser.add_argument('--labels', help='JSON file with labels', required=False, type=str)\n- parser.add_argument('--annotation-output', help='Write a JSON file with site annotations', required=True, type=str)\n- parser.add_argument('--summary-output', help='Write a JSON file here segment annotations', required=True, type=str)\n- parser.add_argument('--annotation-inputs', help='Comma-separated list of site annotation files to merge', required=False, type=str)\n- parser.add_argument('--summary-inputs', help='Comma-separated list of segment annotation files to merge', required=False, type=str)\n- parser.add_argument('--default-tag', help='Default name for sequences that have no explicit label', required=False, type=str, default='Reference')\n- parser.add_argument('--name', help='The sequence ID to highlight', required=False, default='MN908947')\n- parser.add_argument('--mode', help='Operation mode, generate a summary or merge multiple summaries', type=str, choices=['summary', 'merge'], default='summary')\n- parser.add_argument('--relax', help='Path to RELAX.json file', required=False, type=str)\n- parser.add_argument('--busted', help='Path to BUSTED.json file', required=False, type=str)\n- parser.add_argument('--slac', help='Path to SLAC.json file', required=False, type=str)\n- parser.add_argument('--fel', help='Path to FEL.json file', required=False, type=str)\n- parser.add_argument('--cfel', help='Path to CFEL.json file', required=False, type=str)\n- parser.add_argument('--meme', help='Path to MEME.json file', required=False, type=str)\n- parser.add_argument('--meme-full', help='Path to MEME-full.json file', dest='meme_full', required=False, type=str)\n- parser.add_argument('--prime', help='Path to PRIME.json file', required=False, type=str)\n- parser.add_argument('--fade', help='Path to FADE.json file', required=False, type=str)\n- parser.add_argument('--bgm', help='Path to BGM.json file', required=False, type=str)\n- arguments = parser.parse_args()\n- if arguments.mode == 'summary':\n- analyzer = HyPhySummary(arguments)\n- annotation_json, summary_json = analyzer.summary()\n- if annotation_json is not None:\n- with open(arguments.annotation_output, 'w') as fh:\n- json.dump(annotation_json, fh, indent=1)\n- if summary_json is not None:\n- with open(arguments.summary_output, 'w') as fh:\n- json.dump(summary_json, fh, indent=1)\n- else:\n- summary = {}\n- annotation = {}\n- for filename in arguments.annotation_inputs.split(','):\n- with open(filename, 'r') as fh:\n- annotation.update(json.load(fh))\n- for filename in arguments.summary_inputs.split(','):\n- with open(filename, 'r') as fh:\n- summary.update(json.load(fh))\n- with open(arguments.annotation_output, 'w') as fh:\n- json.dump(annotation, fh, indent=1)\n- with open(arguments.summary_output, 'w') as fh:\n- json.dump(summary, fh, indent=1)\n- exit(0)\n" |
| b |
| diff -r 106a316b5313 -r 3f8261f0a826 scripts/infer_stasis_clusters.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/infer_stasis_clusters.py Wed Mar 11 11:15:12 2026 +0000 |
| [ |
| @@ -0,0 +1,171 @@ +#!/usr/bin/env python3 +""" +B-STILL Stasis Cluster Inference Tool +==================================== +Identifies regional footprints of extreme purifying selection (stasis) in B-STILL +JSON results using a FWER-controlled Hypergeometric Scan Statistic. + +Usage: + python3 infer_stasis_clusters.py input.json --ebf 10 --permutations 10000 --output results.json +""" + +import argparse +import json +import sys +import time + +import numpy as np +from scipy.stats import hypergeom + + +def get_sf_optimized(n, d, L, K, cache): + """Retrieves or computes Hypergeometric Survival Function value.""" + key = (n, d) + if key not in cache: + cache[key] = hypergeom.sf(n - 1, L, K, d) + return cache[key] + + +def scan_intervals(indices, L, K, max_size, sf_cache, threshold=None): + """ + Scans all possible intervals [i, j] anchored by stasis events. + Returns the minimum p-value if threshold is None, else returns all significant segments. + """ + best_p = 1.0 + segments = [] + num_events = len(indices) + + for n in range(3, min(max_size + 1, num_events + 1)): + for i in range(num_events - n + 1): + d = indices[i + n - 1] - indices[i] + 1 + p = get_sf_optimized(n, d, L, K, sf_cache) + + if threshold is None: + if p < best_p: + best_p = p + else: + if p <= threshold: + segments.append({ + "start": int(indices[i] + 1), + "end": int(indices[i + n - 1] + 1), + "p_value": p, + "k": n, + "d": int(d) + }) + + return best_p if threshold is None else segments + + +def merge_segments(segments, merge_dist=15): + """Merges overlapping or nearby significant segments.""" + if not segments: + return [] + segments.sort(key=lambda x: x['start']) + + merged = [] + curr = segments[0] + for next_s in segments[1:]: + if next_s['start'] <= curr['end'] + merge_dist: + curr['end'] = max(curr['end'], next_s['end']) + curr['p_value'] = min(curr['p_value'], next_s['p_value']) + curr['d'] = curr['end'] - curr['start'] + 1 + else: + merged.append(curr) + curr = next_s + merged.append(curr) + return merged + + +def main(): + parser = argparse.ArgumentParser(description="Infer stasis clusters from B-STILL JSON.") + parser.add_argument("input", help="Path to B-STILL JSON result file") + parser.add_argument("--ebf", type=float, default=10.0, help="EBF threshold for defining stasis sites (default: 10.0)") + parser.add_argument("--permutations", type=int, default=10000, help="Number of permutations for FWER control (default: 10000)") + parser.add_argument("--alpha", type=float, default=0.05, help="Family-wise error rate threshold (default: 0.05)") + parser.add_argument("--max-cluster", type=int, default=30, help="Maximum number of stasis sites per interval scan (default: 30)") + parser.add_argument("--merge", type=int, default=15, help="Distance in codons to merge adjacent clusters (default: 15)") + parser.add_argument("--output", help="Path to save results in JSON format") + + args = parser.parse_args() + + try: + with open(args.input, "r") as f: + data = json.load(f) + except Exception as e: + print("Error loading JSON: {0}".format(e)) + sys.exit(1) + + sites = data.get("MLE", {}).get("content", {}).get("0", []) + ebfs = [s[12] if (len(s) > 12 and isinstance(s[12], (int, float))) else 0 for s in sites] + L = len(ebfs) + + if L < 10: + print("Alignment too short for cluster analysis.") + sys.exit(0) + + stasis_indices = np.array([i for i, val in enumerate(ebfs) if val >= args.ebf]) + K = len(stasis_indices) + + print("--- B-STILL Cluster Inference ---") + print("Input: {0}".format(args.input)) + print("Gene Length (L): {0} codons".format(L)) + print("Stasis Sites (K): {0} (EBF >= {1})".format(K, args.ebf)) + + if K < 3: + print("Insufficient stasis sites to form clusters (minimum 3 required).") + sys.exit(0) + + print("Running {0} permutations for FWER control...".format(args.permutations)) + null_min_ps = [] + all_positions = np.arange(L) + sf_cache = {} + + start_time = time.time() + for i in range(args.permutations): + if i > 0 and i % 1000 == 0: + elapsed = time.time() - start_time + print(" Processed {0} permutations... ({1:.1f} per sec)".format(i, i / elapsed)) + shuffled = sorted(np.random.choice(all_positions, K, replace=False)) + min_p = scan_intervals(shuffled, L, K, args.max_cluster, sf_cache) + null_min_ps.append(min_p) + + crit_p = np.percentile(null_min_ps, args.alpha * 100) + print("Gene-specific Critical P-value (FWER {0}): {1:.2e}".format(args.alpha, crit_p)) + + print("Scanning observed sequence for significant clusters...") + raw_segments = scan_intervals(stasis_indices, L, K, args.max_cluster, sf_cache, threshold=crit_p) + + final_clusters = merge_segments(raw_segments, merge_dist=args.merge) + + for c in final_clusters: + c['k'] = sum(1 for idx in stasis_indices if c['start'] <= idx + 1 <= c['end']) + + print("\nFound {0} significant stasis clusters:".format(len(final_clusters))) + if final_clusters: + print("\nLegend:") + print(" k : Number of high-confidence stasis sites within the cluster") + print(" d : Total span of the cluster in codons") + print("\n{:<8} | {:<8} | {:<5} | {:<5} | {:<10}".format("Start", "End", "k", "d", "P-value")) + print("-" * 45) + for c in final_clusters: + print("{:<8} | {:<8} | {:<5} | {:<5} | {:.2e}".format(c['start'], c['end'], c['k'], c['d'], c['p_value'])) + + if args.output: + output_data = { + "input_file": args.input, + "parameters": vars(args), + "summary": { + "gene_length": L, + "total_stasis_sites": K, + "critical_p_value": float(crit_p), + "num_clusters": len(final_clusters) + }, + "clusters": final_clusters + } + with open(args.output, "w") as f: + json.dump(output_data, f, indent=4) + print("\nDetailed results saved to {0}".format(args.output)) + + +if __name__ == "__main__": + main() |
| b |
| diff -r 106a316b5313 -r 3f8261f0a826 test-data/bstill-in1.json --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-data/bstill-in1.json Wed Mar 11 11:15:12 2026 +0000 |
| [ |
| b'@@ -0,0 +1,1880 @@\n+{\n+ "MLE": {\n+ "content": {\n+ "0": [\n+ [\n+ 2.37190144157317,\n+ 0.1886402906758332,\n+ 0.003967400746549269,\n+ 0.03170401941783556,\n+ 0.1405070629863407,\n+ 0.2478544806315437,\n+ 0.155938987532467,\n+ 0,\n+ 0,\n+ 1.668673069222721,\n+ 0.9606238475229101,\n+ 1.774898950889347,\n+ 1.224638397608354\n+ ],\n+ [\n+ 1.562666730096422,\n+ 0.7176422560937019,\n+ 0,\n+ 9.089104346594827e-07,\n+ 0,\n+ 0.01450154962095004,\n+ 0.2139088787662019,\n+ 0,\n+ 0,\n+ 0,\n+ 2.666666171615278e-05,\n+ 0,\n+ 0.05468540563699053\n+ ],\n+ [\n+ 0.6841064654452979,\n+ 0.6942845364725866,\n+ 0,\n+ 0.05269358336084695,\n+ 0,\n+ 0.07725223791333098,\n+ 0.5503080822201034,\n+ 0,\n+ 0,\n+ 0,\n+ 1.631978457912503,\n+ 0,\n+ 0.311129391337898\n+ ],\n+ [\n+ 1.603530309019734,\n+ 0.7640979882187913,\n+ 0,\n+ 9.480689290490966e-08,\n+ 0,\n+ 0.0129282189776549,\n+ 0.2196957351152485,\n+ 0,\n+ 0,\n+ 0,\n+ 2.781551610416608e-06,\n+ 0,\n+ 0.0486746608276061\n+ ],\n+ [\n+ 0.7745431936738028,\n+ 0.2481102774238383,\n+ 0.005522827981575777,\n+ 0.05017493786864266,\n+ 0.1167934725144055,\n+ 0.3301340073712549,\n+ 0.2680133904625786,\n+ 0,\n+ 0,\n+ 2.326512750426494,\n+ 1.549852564474649,\n+ 1.435734346731007,\n+ 1.831535344411821\n+ ],\n+ [\n+ 0.7291297549930137,\n+ 0.2232251727035806,\n+ 0.006100143264891383,\n+ 0.05237454314416388,\n+ 0.1226528460847873,\n+ 0.3565428751858024,\n+ 0.2618162231862851,\n+ 0,\n+ 0,\n+ 2.57120169474055,\n+ 1.62155131319508,\n+ 1.51783284915089,\n+ 2.059231243604498\n+ ],\n+ [\n+ 0.5299477744525759,\n+ 0.3199604238306135,\n+ 0.006259406053599633,\n+ 0.06427776426846346,\n+ 0.09952410556612061,\n+ 0.3591418826984177,\n+ 0.3634880853401729,\n+ 0,\n+ 0,\n+ 2.638753567868953,\n+ 2.015398729009674,\n+ 1.199979925906953,\n+ 2.08265405394555\n+ ],\n+ [\n+ 0.7850622182406316,\n+ 0.208593049315937,\n+ 0.006006859903783282,\n+ 0.04987146289298329,\n+ 0.1278017467009314,\n+ 0.3521879280501232,\n+ 0.2426664534291802,\n+ 0,\n+ 0,\n+ 2.531645281107513,\n+ 1.53998649735195,\n+ 1.590887120603373,\n+ 2.020404836198384\n+ ],\n+ [\n+ 0.7353234573836737,\n+ 0.7975030154821481,\n+ 0,\n+ 0.05027743242304463,\n+ 0,\n+ 0.06341157982977386,\n+ 0.5594116332078475,\n+ 0,\n+ 0,\n+ 0,\n+ 1.55318611910382,\n+ 0,\n+ 0.2516128169853178\n+ ],\n+ [\n+ 0.7745431936738028,\n+ 0.2481102774238383,\n+ 0.005522827981575777,\n+ 0.05017493786864266,\n+ 0.1167934725144055,\n+ 0.3301340073712549,\n+ 0.2680133904625786,\n+ 0,\n+ 0,\n+ 2.326512750426494,\n+ 1.549852564474649,\n+ 1.435734346731007,\n+ 1.831535344411821\n+ ],\n+ [\n+ 1.368503706104576,\n+ 1.018443324506961,\n+ 0,\n+ 0.0001254498320607177,\n+ 0,\n+ 0.01193786625873712,\n+ 0.3202018713208712,\n+ 0,\n+ 0,\n+ 0,\n+ 0.003681050177071567,\n+ 0,\n+ '..b':1e-10)Node123:0.000622,(CY173095:0.001245,(((CY173191:1e-10,CY037703:0.001247)Node132:0.000622,CY035062:0.00249)Node131:1e-10,((CY173255:0.001871,(CY044748:1e-10,GQ385846:0.001247)Node139:0.000623)Node137:1e-10,((CY027075:0.000623,(EU199367:1e-10,CY172823:0.000623)Node145:1e-10)Node143:0.000623,(((CY172847:0.000623,CY025643:1e-10)Node150:0.000623,CY172839:0.000623)Node149:0.001247,(((CY026251:0.002497,CY092241:0.00125)Node156:0.000624,CY026019:0.000623)Node155:1e-10,((CY025341:0.002502,(CY172775:0.001247,CY172903:0.000623)Node163:0.001248)Node161:0.000622,(EU199255:0.003134,(CY172431:0.000621,((CY172223:0.003127,(CY172191:0.001254,CY020069:0.000628)Node173:0.000621)Node171:1e-10,((CY092217:0.003119,(CY025485:0.001283,EU516019:0.00059)Node179:0.008843)Node177:0.001868,(CY002080:0.000622,((CY002064:0.001873,(CY002456:0.000626,CY002048:0.002491)Node187:0.001243)Node185:0.003763,(AB434109:0.006307,(((CY088198:0.001245,CY088475:0.000621)Node194:1e-10,CY000257:0.000621)Node193:0.002494,((CY112957:0.003135,CY006859:0.001253)Node199:0.000616,((CY000721:0.001243,(CY114493:1e-10,CY090885:0.002483)Node205:0.002488)Node203:1e-10,((CY001792:0.000621,(CY001600:0.00062,CY002368:0.001865)Node211:1e-10)Node209:1e-10,((CY002304:0.002489,(CY006163:0.001242,CY003632:0.00062)Node217:1e-10)Node215:0.00062,(CY001920:0.003739,(CY001912:0.002498,((CY001504:1e-10,CY006060:0.001241)Node225:0.00062,((CY001744:1e-10,CY002136:0.00124)Node229:0.00062,((CY114309:0.003746,(CY006899:0.001874,CY112901:0.001863)Node235:0.002496)Node233:0.000618,(((CY006579:0.001245,CY006283:0.000621)Node240:0.001244,CY007979:0.001864)Node239:1e-10,((CY006499:0.001241,(CY006491:0.001863,CY006635:0.00124)Node247:1e-10)Node245:0.00062,(((CY036847:0.002495,CY010004:0.001867)Node252:0.000621,CY012200:0.000621)Node251:1e-10,(((CY010028:0.001242,CY009732:0.001242)Node258:1e-10,CY010012:0.002484)Node257:0.001875,(((CY010020:0.002497,CY011416:0.001237)Node264:0.001252,CY009484:0.00062)Node263:0.005658,((CY010036:1e-10,(CY039879:0.002489,CY039880:0.000621)Node271:0.002487)Node269:0.003109,((CY010628:0.000619,(CY010716:0.002485,CY010516:0.001239)Node277:1e-10)Node275:0.000619,((CY012728:1e-10,CY013701:0.000621)Node281:0.001243,(((CY012760:0.001239,CY013200:0.001239)Node286:1e-10,CY011888:0.005614)Node285:1e-10,((CY013693:0.000619,(CY012184:0.002484,CY013669:0.00124)Node293:0.00124)Node291:1e-10,((CY112669:0.004353,(CY112556:0.001861,CY011896:0.000619)Node299:0.00186)Node297:1e-10,(CY112605:0.001243,(CY012224:0.006953,((CY012512:0.000619,(CY012896:0.00062,CY012232:0.00062)Node309:0.000621)Node307:1e-10,(CY011848:1e-10,(((CY011328:1e-10,CY114221:0.000619)Node316:0.001241,CY011560:0.00124)Node315:1e-10,((CY012456:0.000619,CY011824:0.00124)Node321:1e-10,CY017283:0.000619)Node320:1e-10)Node314:1e-10)Node312:1e-10)Node306:0.002439)Node304:0.008263)Node302:0.000618)Node296:1e-10)Node290:1e-10)Node284:0.000617)Node280:0.012609)Node274:0.000619)Node268:0.001215)Node262:0.003782)Node256:0.000613)Node250:0.006878)Node244:1e-10)Node238:0.001862)Node232:1e-10)Node228:1e-10)Node224:0.001863)Node222:0.001862)Node220:0.001245)Node214:1e-10)Node208:0.001241)Node202:0.021084)Node198:0.000623)Node192:0.00065)Node190:0.003713)Node184:0.002495)Node182:1e-10)Node176:0.005637)Node170:0.00313)Node168:0.002515)Node166:0.001876)Node160:1e-10)Node154:0.000623)Node148:1e-10)Node142:0.003749)Node136:1e-10)Node130:1e-10)Node128:0.000623)Node122:0.000623)Node116:0.001872)Node114:0.002503)Node110:1e-10)Node104:1e-10)Node102:0.000623)Node96:0.001468)Node90:0.00189)Node84:0.002721)Node78:0.002524)Node76:1e-10)Node74:0.000624)Node72:1e-10)Node66:1e-10)Node60:1e-10)Node58:0.000623)Node54:0.001248)Node48:0.001255)Node46:0.000619)Node40:1e-10)Node34:1e-10)Node28:1e-10)Node22:0.000623)Node16:1e-10)Node10:1e-10)Node4:1e-10)"\n+ }\n+ },\n+ "_comment": "Trimmed HyPhy B-STILL output (first 120 codon sites, first 13 metrics, removed posterior/grid/etc) to keep infer_stasis_clusters test input under planemo\'s 1MB lint limit."\n+}\n' |