kr-colab/mkado: v0.6.0 - Bug-fix release
Abstract
Bug-fix release. No options were added or removed. The thirty fixes below came out of a coverage push that took the test suite from 311 to 718 tests and line coverage from 68 to 95 percent, and out of the bug reports filed along the way. Several fixes change reported numbers: a multi-allelic codon now counts each of its mutations, VCF divergence follows the FASTA rules, pooled polymorphisms filter on the combined minor-allele frequency, and singleton filtering reaches every path. Two fixes remove run-to-run drift: polarized fixed differences no longer follow the hash seed, and separate-files batch pairing no longer follows the directory listing order. Lint and the test suite now run on every pull request. Fixed In separate-files mode, batch paired an ingroup file with a second outgroup, or with an outgroup found through --outgroup-pattern, by the first _-separated token of its name, taking the first match from an unsorted glob. Gene g1 could take g10's file, and Adh_dmel could take Adh_dsim's, depending on the order the filesystem listed the directory. A partner now has to carry the whole gene name, which is the ingroup stem without its _ingroup or _in marker, followed by _, and candidates are tried in sorted order (closes #117). polarized_mk_test read the second outgroup's codon at a fixed difference with next(iter(set)), so when outgroup2 was polymorphic there the lineage followed Python's hash seed: the same alignment reported the difference on the ingroup lineage, on the outgroup lineage, or as unpolarized from one run to the next. The second outgroup is now read by membership. It names the ancestral codon when it carries exactly one of the two codons, and the difference is unpolarized when it carries both or neither (closes #114). g_test computed its p-value as 1 - cdf, which rounds to exactly zero once the G statistic passes about 75. It now uses the chi-square survival function, so a strongly significant table reports its real p-value instead of zero (closes #35). classify_polymorphism/classify_polymorphism_pooled dropped an entire codon when it had exactly two alleles and the only comparison was stop-blocked, but only dropped the blocked allele when three or more alleles were present. Both now drop just the blocked allele, so classification no longer depends on how many alleles happen to segregate at a codon (closes #94). A codon pair where every mutational ordering passed through a stop codon was dropped from Dn/Ds/Pn/Ps with no signal, indistinguishable from an identical-codon pair. Under the vertebrate mitochondrial code this silently dropped four ordinary Lys/Trp pairs. The pair still drops from the counts, but each drop is now counted and surfaces as a warning, the same way skipped indels and multi-allelic sites already do (closes #90). Asymptotic MK frequency bin edges came from np.linspace, which does not always return the double nearest k / num_bins -- with the default 20 bins, edge 3 came back as 0.15000000000000002 instead of 0.15, misclassifying a polymorphism at exactly that frequency. Bin edges are now computed exactly (closes #84). Pooled-polymorphism frequency filtering was unreliable: polarized_mk_test's pooled branch applied no filter at all, and mk_test's pooled branch filtered on ingroup-only derived counts, missing outgroup-private singletons. Pooled mode now filters on the minor-allele frequency across both groups (closes #83). alpha_tg_from_gene_data reported a zero-width confidence interval for a single gene, or when every bootstrap replicate failed, indistinguishable from a genuinely tight CI. Both cases now report ci_low/ci_high as undefined (NA in pretty/TSV, null in JSON) instead, with batch and vcf emitting a warning (closes #79). --imputed combined with a polarize flag silently ran an unpolarized imputed test instead of erroring, unlike --asymptotic which already rejected the combination (with wording that had itself drifted between test and batch). --plot-asymptotic's ignored-flag warning was missing its "(ignored)" suffix in test. vcf's volcano-plot code caught a broader exception than batch's identical call, swallowing real bugs. --alpha-tg combined with --per-gene silently ignored --per-gene with no warning. The imputed_cutoff and is_aggregate calculations were duplicated verbatim across test/batch/vcf. All six now go through shared helpers (closes #103). test, batch, and vcf each hand-wrote the same option-conflict checks and the copies had drifted: vcf dropped the explanatory clause from its messages, and checked --alpha-tg/--imputed against --asymptotic in a different order than batch, so the same flag combination could report a different conflict depending on the command. All three now share one validator (closes #74). Batch TSV chose its column layout from the first result's type, then silently dropped every row of a different type. A mixed-type batch now raises a clear error instead (closes #69). --output - (a bare dash for stdout) was rejected by the output-path check (closes #46). A failed VCF open leaked one file descriptor; the htslib capture pipe is now closed in every case (closes #45). --no-singletons did not filter singletons on the VCF path; the frequency check let the singleton itself through (closes #40). VCF divergence counting compared the outgroup codon to the reference instead of to the ingroup, miscounting sites where the ingroup was fixed for ALT (closes #44). --freq-cutoffs was not forwarded to per-gene asymptotic workers in vcf mode (closes #42). Batch TSV had no layout for imputed results and silently fell back to JSON (closes #43). Batch JSON silently dropped a gene when two results shared a name; duplicate names are now rejected up front (closes #70). CLI error messages had drifted: some exited without the "Error: " prefix. All CLI errors now route through one helper (closes #72). batch and vcf used different exit codes for an empty result set. Both now exit 0 when a run simply produces no results, and 1 only when genes errored (closes #75). mkado vcf --gene X with -a, --imputed, or --alpha-tg silently ran the standard MK test instead of the requested mode (closes #41). --no-singletons did not filter singletons on the FASTA --polarize-match or --alpha-tg paths, or wherever sample size did not match the raw sequence count. All FASTA paths now filter by derived-allele count, matching the VCF path (closes #55). Pooled major-codon selection weighted each population's frequency spectrum as a proportion, so a small population could outweigh a large one. Sequences are now counted directly (closes #81). Documented how a multi-position codon difference is resolved; the rule was previously unstated (closes #88). A multi-allelic codon collapsed two different derived bases into one mutation. Each base is now counted separately; polymorphism counts rise on affected genes (closes #87). VCF divergence rules now match the FASTA path: a codon touched by an ingroup polymorphism is excluded from divergence, and a heterozygous outgroup call is treated as missing rather than resolved. Divergence counts drop where this applies (closes #58). --freq-cutoffs had no effect on the FASTA per-gene asymptotic fitter; it always used the full frequency range (closes #61). Imputed batch results showed a placeholder adjusted p-value of 1.0 instead of a real Benjamini-Hochberg correction; asymptotic per-gene batches showed a fabricated adjusted-p-value line despite having no p-value (closes #65). A failed curve fit in the asymptotic fitters fell back to a value outside the requested --freq-cutoffs window instead of the last point inside it. format_batch_results accepted an adjusted_pvalues list of the wrong length: a short list raised an IndexError from inside a format branch and a long list was silently truncated. It now raises a ValueError before formatting anything (#116).