Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -400,11 +400,11 @@ jobs:
shell: bash
run: |
set +e
julia --project=. -t2 --compiled-modules=no test/runtests.jl numeric_policy exact_summaries high_precision derived_analyses validated_statistics 2>&1 | tee /tmp/validated-statistics-tests.log
julia --project=. -t2 --compiled-modules=no test/runtests.jl numeric_policy exact_summaries high_precision derived_analyses validated_statistics 2>&1 | tee /tmp/julia-validated-tests.log
status="${PIPESTATUS[0]}"
if [ "$status" -ne 0 ]; then
enc() { head -c 3500 | sed -e 's/%/%25/g' -e 's/\r/%0D/g' | awk '{printf "%s%%0A", $0}'; }
echo "::error title=Validated-statistics tests failed::$(tail -n 120 /tmp/validated-statistics-tests.log | enc)"
echo "::error title=Validated statistics failed (tail)::$(tail -c 3500 /tmp/julia-validated-tests.log | enc)"
fi
exit "$status"

Expand Down
26 changes: 15 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ not exclude samples from this analysis.
calibration caveat in its diagnostics, shown with the result and written
into its CSV export. The caveat changes no p-value; the null-simulation
evidence and full replication protocol live in `bench/null_calibration`.
The glmGamPoi path uses fixed-theta NB Wald fits, not a quasi-likelihood test.
- `zinb`: a zero-inflated negative-binomial model with separate logistic
zero and count components, a count-component library-size offset, a joint
2-df likelihood-ratio test, and BH correction.
Expand All @@ -316,12 +317,15 @@ not exclude samples from this analysis.
on `log(counts + 1)`, propagating bias-estimation variance into two-sided Wald
tests. The effect is a bias-corrected log2 fold change; useful when asymmetric
shifts or load changes would bias standard CLR centering (needs at least 3
tested taxa; assumes fewer than half are differential).
tested taxa). The implementation reports its mixture and bias diagnostics.
Its assumption that fewer than half of tested taxa are differential must be
checked rather than treated as a guarantee.
- `aldex2`: Dirichlet(`count + 0.5`) Monte-Carlo CLR model (Fernandes et al.
2013, 2014). Draws `mc_samples` (default 128, seeded by the study's `seed`)
instances per sample and reports standardized CLR effect sizes with
consistent-sign posterior predictive Welch p-values; suited to sparse or
variable-depth cohorts where deterministic zero imputation is undesirable.
The reported estimate is a standardized CLR effect size, not an absolute fold change.
- `songbird`: compositional baseline-category multinomial regression with
reference-frame invariant CLR-centred differentials summing to zero across
fitted taxa (Morton et al. 2019, *Nat. Commun.* 10:2719; Zhang et al. 2017).
Expand All @@ -336,7 +340,8 @@ not exclude samples from this analysis.
composition (La Rosa et al. 2012), which absorbs between-sample
overdispersion; `dm_overdispersion_method` picks `mle` (profiled) or `mom`.
Both multinomial models bound their Newton fit with `max_iter`, `tolerance`
and `max_seconds`, and a fit that does not converge is refused.
and `max_seconds`, and a fit that does not converge is refused. Dirichlet-
multinomial diagnostics report the concentration, theta, and boundary status.

ZINB and hurdle models can overfit sparse data; do not select ZINB solely
because counts are sparse. Compare against NB diagnostics. The methods report
Expand Down Expand Up @@ -818,23 +823,22 @@ analysis:
normalisation: hellinger # hellinger | none | rarefy | srs
depth: 0 # rarefy/srs reads per sample; 0 = smallest non-empty sample
differential:
method: nb_glm # nb_glm | clr_welch | zinb | hurdle_nb | ancom_bc
# | aldex2 | songbird | multinomial | dirichlet_multinomial
method: nb_glm # nb_glm | clr_welch | multinomial | dirichlet_multinomial
# ancom_bc | aldex2 | songbird | zinb | hurdle_nb
offset: tss # count models: tss | rle
dispersion: ml # nb_glm: ml | glmGamPoi
min_prevalence: 0.0 # fraction of samples a taxon must be seen in
replacement_method: bayesian_multiplicative
# clr_welch: bayesian_multiplicative | multiplicative_replacement
replacement_delta: 0.65 # clr_welch: strictly between 0 and 1
bayesian_alpha: ~ # clr_welch: null (GBM estimate) or a number > 0
mc_samples: 128 # aldex2: Dirichlet Monte-Carlo instances in [16, 4096]
mn_reference_taxon: ~ # multinomial models: null picks the most abundant taxon
mn_penalty: 0.0 # multinomial models: L2 ridge penalty, >= 0
dm_overdispersion_method: mle
# dirichlet_multinomial: mle | mom
bayesian_alpha: ~ # Bayesian replacement: null (GBM estimate) or a number > 0
mn_reference_taxon: ~ # multinomial models: null selects the most abundant fitted taxon
mn_penalty: 0.0 # multinomial models: non-negative L2 penalty
dm_overdispersion_method: mle # dirichlet_multinomial: mle | mom
max_iter: 500 # multinomial models: Newton iteration cap
tolerance: 1.0e-8 # multinomial models: convergence tolerance
tolerance: 0.00000001 # multinomial models: convergence tolerance
max_seconds: 300.0 # multinomial models: wall-clock budget
mc_samples: 128 # aldex2: Dirichlet Monte-Carlo instances in [16, 4096]
nmds:
max_stress: 0.2 # warn if NMDS stress exceeds this value
```
Expand Down
2 changes: 1 addition & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ occupancy work. The GitHub Wiki roadmap still needs a synchronized update; see

## Available now

- The differential-abundance config offers four per-taxon count models
- The differential-abundance config offers four per-taxon methods
(`nb_glm`, `clr_welch`, `zinb`, `hurdle_nb`) and five compositional models
(`ancom_bc`, `aldex2`, `songbird`, `multinomial`, `dirichlet_multinomial`);
see [the differential-abundance section](README.md#differential-abundance).
Expand Down
6 changes: 3 additions & 3 deletions docs/occupancy-models.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ not an AnalysisConfig method or the standard endpoint. The full workflow is
tracked in [issue #205](https://github.com/hyperpolymath/MetaManifold-WebUI/issues/205),
a follow-up to #18.

The differential-abundance configuration supports `zinb` and `hurdle_nb` in
addition to the existing `nb_glm` and `clr_welch` models. The two new methods
are fixed-effect, per-taxon models with a two-part negative-binomial likelihood.
The differential-abundance configuration supports `zinb` and `hurdle_nb`
alongside the count, CLR, and compositional methods. These two methods are
fixed-effect, per-taxon models with a two-part negative-binomial likelihood.
They use the configured `tss` or `rle` library-size offset in the count
component and apply Benjamini–Hochberg correction to the joint tests.

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/api/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ import type {
TreeFileInfo, TreeFile, RunRef, ReadFunnelData, ReportItem, ReportKind,
PlacementDoc, PlacementSummary, PlacementDetail, PlacementQueries, PhyloStep,
ReferenceTreeDoc, ReferenceTreeSummary, ReferenceTreeDetail, AlignmentQC, TrimSettings,
CladisticsCreateRequest, CladisticsSummary, CladisticsDetail,
CladeCumulusSourcesResponse, CladeCumulusRequest, CladeCumulusResponse,
CladeMoveValidationRequest, CladeMoveValidationResponse,
CladisticsSummary, CladisticsDetail, CladisticsCreateRequest,
} from './types'

import type { FigureDoc } from '../figure/types'
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/components/DifferentialPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,16 @@ export function DifferentialPanel({ study, runs, option, aggregate }: {
`replaced values hold ${(100 * z.median_imputed_fraction).toPrecision(2)}% of the median sample ` +
`and at most ${(100 * z.max_imputed_fraction).toPrecision(2)}% of any, and are not measurements.`
})()}
{result.diagnostics.model && (() => {
const m = result.diagnostics.model
return ` Reference taxon: ${m.reference_taxon}; ${m.n_parameters} coefficients, ` +
`penalty ${m.penalty}, ${m.iterations} Newton steps in ${m.runtime_seconds.toPrecision(2)} s.`
})()}
{result.diagnostics.overdispersion && (() => {
const o = result.diagnostics.overdispersion
return ` Concentration ${o.concentration.toPrecision(3)} by ${o.method}, ` +
`theta = ${o.theta.toPrecision(3)}${o.at_bound ? ' (at a bound: see the row notes)' : ''}.`
})()}
{result.diagnostics.ancom_bc && (() => {
const b = result.diagnostics.ancom_bc
const warn = b.warnings.length > 0 ? ` Warning: ${b.warnings.join('; ')}.` : ''
Expand Down
24 changes: 19 additions & 5 deletions frontend/src/components/PipelineStages.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
import { expect, test } from 'bun:test'
import { CONFIG_DESCRIPTIONS, CONFIG_TYPES } from './PipelineStages'

const ALL_DIFFERENTIAL_METHODS = [
'nb_glm', 'clr_welch', 'zinb', 'hurdle_nb', 'ancom_bc', 'aldex2', 'songbird',
'multinomial', 'dirichlet_multinomial',
]

test('dispersion uses the enum editor with both supported models', () => {
expect(CONFIG_TYPES['analysis.differential.dispersion']).toEqual({
kind: 'enum', options: ['ml', 'glmGamPoi'],
Expand All @@ -10,14 +15,23 @@ test('dispersion uses the enum editor with both supported models', () => {

test('differential model editor exposes all nine supported methods', () => {
expect(CONFIG_TYPES['analysis.differential.method']).toEqual({
kind: 'enum',
options: [
'nb_glm', 'clr_welch', 'zinb', 'hurdle_nb', 'ancom_bc', 'aldex2', 'songbird',
'multinomial', 'dirichlet_multinomial',
],
kind: 'enum', options: ALL_DIFFERENTIAL_METHODS,
})
})

test('multinomial settings retain their enums and optimiser budgets', () => {
expect(CONFIG_TYPES['analysis.differential.dm_overdispersion_method']).toEqual({
kind: 'enum', options: ['mle', 'mom'],
})
expect(CONFIG_TYPES['analysis.differential.mn_penalty']).toEqual({
kind: 'float', min: 0, step: 0.05,
})
expect(CONFIG_TYPES['analysis.differential.max_seconds']).toEqual({
kind: 'float', min: 1,
})
expect(CONFIG_TYPES['analysis.differential.max_iter']).toEqual({ kind: 'int' })
})

test('advanced zero handling exposes both methods, bounded delta and nullable alpha', () => {
expect(CONFIG_TYPES['analysis.differential.replacement_method']).toEqual({
kind: 'enum', options: ['bayesian_multiplicative', 'multiplicative_replacement'],
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/PipelineStages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -270,12 +270,12 @@ export const CONFIG_TYPES: Record<string, ConfigType> = {
'analysis.differential.replacement_method': { kind: 'enum', options: ['bayesian_multiplicative', 'multiplicative_replacement'] },
'analysis.differential.replacement_delta': { kind: 'float', min: 0, max: 1, step: 0.05 },
'analysis.differential.bayesian_alpha': { kind: 'float', nullable: true, min: 0, step: 0.1 },
'analysis.differential.mc_samples': { kind: 'int' },
'analysis.differential.mn_penalty': { kind: 'float', min: 0, step: 0.05 },
'analysis.differential.dm_overdispersion_method': { kind: 'enum', options: ['mle', 'mom'] },
'analysis.differential.max_iter': { kind: 'int' },
'analysis.differential.tolerance': { kind: 'float', min: 0 },
'analysis.differential.max_seconds': { kind: 'float', min: 1 },
'analysis.differential.mc_samples': { kind: 'int' },
'analysis.alpha.show_points': { kind: 'boolean' },
'analysis.alpha.annotate_significance': { kind: 'boolean' },
'analysis.alpha.pairwise_brackets': { kind: 'boolean' },
Expand Down
Loading
Loading