OmicVerse Microbiome — Differential-Abundance Method Comparison
Goal
Take a preprocessed microbiome AnnData (output of the 16S amplicon skill — samples × ASVs with 7-rank SINTAX taxonomy in var) and run all three DA methods exposed by ov.micro.DA on the same two-group contrast — Wilcoxon (rank), pyDESeq2 (NB-GLM), ANCOM-BC (compositional). Compare their hit sets at a common FDR cutoff, report consensus / method-specific genera, and surface the biology around the three methods' different statistical assumptions so the user can pick (or report) the right one.
Quick Workflow
- Load the AnnData produced by
omicverse-microbiome-16s-amplicon-dada2. Drop control / non-relevant groups; keep exactly two for the contrast.
- Collapse to a chosen taxonomic rank (typically genus):
ov.micro.collapse_taxa(adata, rank='genus'). DA at species/ASV level is noisy on small cohorts; genus is the canonical reporting rank for 16S.
- Wilcoxon:
ov.micro.DA(adata_genus).wilcoxon(group_key, group_a, group_b, min_prevalence=0.1). Non-parametric; fastest; tests ranks of relative abundance.
- pyDESeq2:
ov.micro.DA(adata_genus).deseq2(group_key, group_a, group_b, min_prevalence=0.1). NB-GLM on raw counts; uses RNA-seq-style shrinkage of small-count fold-changes.
- ANCOM-BC:
ov.micro.DA(adata_genus).ancombc(group_key, min_prevalence=0.1, pseudocount=1.0). Compositional bias-corrected ANCOM; closest to the compositional ground truth.
- Build sets of significant features at a common FDR cutoff (typically
0.05). Watch for column-name differences across methods (fdr_bh for Wilcoxon / DESeq2; q_value or fdr_bh for ANCOM-BC).
- Tabulate the 3-way Venn (Wilcoxon-only / DESeq2-only / ANCOM-BC-only / pairwise overlaps / all-three) and render with
matplotlib_venn.venn3 if installed.
- Report two numbers in the writeup: (a) the consensus hit set (intersection across all three) for the strongest claim, and (b) the Wilcoxon ∪ ANCOM-BC set as a slightly more permissive convention if compositional correctness matters.
Interface Summary
Same ov.micro.DA class, three different methods, all returning a pd.DataFrame indexed by the feature column with method-specific columns:
ov.micro.DA(adata).wilcoxon(
group_key, group_a=None, group_b=None,
rank=None, relative=True, min_prevalence=0.1,
) -> pd.DataFrame
# columns: feature, log2fc, pvalue, fdr_bh, mean_a, mean_b, prevalence_a, prevalence_b
# convention: log2fc > 0 ⇒ higher in group_a
ov.micro.DA(adata).deseq2(
group_key, group_a=None, group_b=None,
rank=None, min_prevalence=0.1, alpha=0.05,
) -> pd.DataFrame
# columns: feature, baseMean, log2fc, lfcSE, stat, pvalue, fdr_bh
# convention: log2fc > 0 ⇒ higher in group_a; uses pyDESeq2's MLE-shrunk LFC.
ov.micro.DA(adata).ancombc(
group_key, rank=None,
min_prevalence=0.1, pseudocount=1.0,
) -> pd.DataFrame
# columns: feature, log2fc, std_error, w_stat, pvalue, q_value (or fdr_bh)
# convention: log2fc > 0 ⇒ higher in group_a (after compositional bias correction).
Boundary
Inside scope:
- Running all three DA methods on the same AnnData and the same two-group contrast.
- Set-arithmetic comparison of hit sets at a common FDR.
- Method-choice guidance based on cohort size / zero inflation / compositional concerns.
- Documenting consensus and method-specific features.
Outside scope:
- Building / preprocessing the input AnnData — see
omicverse-microbiome-16s-amplicon-dada2.
- Cross-cohort meta-analysis (combining DA results from multiple studies) — see
omicverse-microbiome-meta-analysis.
- Three-or-more-group DA —
ov.micro.DA is two-group; use Kruskal-Wallis externally.
- Repeated-measures / paired DA — out of scope for
ov.micro (use mixed models in ov.metabol or external nlme etc.).
- Phylogenetically-aware DA (e.g. PERMANOVA on UniFrac distances) — see phylogeny skill.
Branch Selection
Wilcoxon (default for moderate cohorts)
- Pros: non-parametric (no NB / log-normal assumption); fast (sub-second on a 22-sample × 250-genus cohort); robust to outliers.
- Cons: weak power on small cohorts (n<10/group); doesn't model compositional bias; relative-abundance scaling makes interpretation of log2fc less rigorous than DESeq2.
- Use when: n>=10/group, low-to-moderate sparsity, no strong compositional concern, you need fast / portable.
pyDESeq2 (RNA-seq-style NB-GLM)
- Pros: well-calibrated p-values on raw counts; LFC shrinkage stabilises small-count fold-changes; mature implementation with extensive vignettes.
- Cons: NB assumption fails on highly zero-inflated data (microbiome can be 80%+ zeros); doesn't correct for compositional bias; slower than Wilcoxon.
- Use when: n is moderate, sparsity is moderate, you want NB-style shrinkage, reviewers expect DESeq2-style methodology.
ANCOM-BC (compositional bias-corrected)
- Pros: explicit compositional correction (the only method here that doesn't ignore the simplex); recovers true effect direction on highly biased data; bias-corrected per-feature log-ratio model is closest to the ground-truth.
- Cons: slowest of the three; requires
skbio>=0.7.1; can be conservative on small cohorts; the bias-correction adds a per-feature constant which shifts log2fc relative to DESeq2.
- Use when: compositional correctness matters (always for true microbiome relative-abundance reporting), n is moderate-to-large, downstream analysis uses log-ratio interpretation.
Consensus reporting strategy
- For the strongest claim: intersection across all three (rare but unambiguous; survives method assumptions).
- For a defensible claim: Wilcoxon ∩ ANCOM-BC (combines a model-free test with the compositional-aware test; bypasses DESeq2's NB assumption).
- For exploratory hits: union; flag method-specific hits explicitly so the reader knows the assumption that drove them.
- Always report cohort size + sparsity + chosen FDR — DA results without those numbers are uninterpretable.
Column-name pitfalls
- Wilcoxon / pyDESeq2 use
fdr_bh; ANCOM-BC may use q_value or fdr_bh depending on skbio version. Pattern in the tutorial: sig_col = 'q_value' if 'q_value' in ab.columns else 'fdr_bh'.
- All three methods may also expose a raw
pvalue column. Don't confuse pvalue and FDR when filtering — pvalue<0.05 is not FDR<0.05.
Input Contract
- An
AnnData from the 16S amplicon skill; obs[group_key] is a categorical with at least two values; the two-group slice should have n>=5/group for any of these tests to behave reasonably.
adata.X is integer counts (DESeq2 requires raw counts; Wilcoxon and ANCOM-BC handle either, but raw counts are the canonical input).
- For ANCOM-BC:
pip install skbio>=0.7.1 (function raises ImportError if missing).
- For pyDESeq2:
pip install pydeseq2.
Minimal Execution Patterns
import omicverse as ov
import anndata as ad
import matplotlib.pyplot as plt
ov.plot_set()
# 1) Load AnnData from the 16S amplicon skill, restrict to two groups.
adata = ad.read_h5ad('mothur_sop_16s.h5ad')
adata = adata[adata.obs['group'].isin(['Early', 'Late'])].copy()
print(adata.obs['group'].value_counts().to_dict())
# 2) Collapse to genus.
adata_genus = ov.micro.collapse_taxa(adata, rank='genus')
print('samples × genera:', adata_genus.shape)
# 3) Run all three DA methods on the same contrast.
wx = ov.micro.DA(adata_genus).wilcoxon(
group_key='group', group_a='Early', group_b='Late', min_prevalence=0.1,
)
ds = ov.micro.DA(adata_genus).deseq2(
group_key='group', group_a='Early', group_b='Late', min_prevalence=0.1,
)
ab = ov.micro.DA(adata_genus).ancombc(
group_key='group', min_prevalence=0.1,
)
print(f' Wilcoxon : {(wx["fdr_bh"] < 0.05).sum():3d} / {len(wx):3d} genera at FDR 0.05')
print(f' DESeq2 : {(ds["fdr_bh"] < 0.05).sum():3d} / {len(ds):3d}')
sig_col_ab = 'q_value' if 'q_value' in ab.columns else 'fdr_bh'
print(f' ANCOM-BC : {(ab[sig_col_ab] < 0.05).sum():3d} / {len(ab):3d}')
# 4) Build sets and tabulate the 3-way Venn.
sig_wx = set(wx.loc[wx['fdr_bh'] < 0.05, 'feature'])
sig_ds = set(ds.loc[ds['fdr_bh'] < 0.05, 'feature'])
sig_ab = set(ab.loc[ab[sig_col_ab] < 0.05, 'feature'])
print('Wilcoxon only :', len(sig_wx - sig_ds - sig_ab))
print('DESeq2 only :', len(sig_ds - sig_wx - sig_ab))
print('ANCOM-BC only :', len(sig_ab - sig_wx - sig_ds))
print('All three :', len(sig_wx & sig_ds & sig_ab))
# 5) Render Venn (optional dependency)
try:
from matplotlib_venn import venn3
fig, ax = plt.subplots(figsize=(5, 5))
venn3([sig_wx, sig_ds, sig_ab],
set_labels=('Wilcoxon', 'DESeq2', 'ANCOM-BC'), ax=ax)
ax.set_title('Genera significant at FDR/q < 0.05')
plt.show()
except ImportError:
pass
Validation
- For each method: report
n_tested (pre-min_prevalence filter), n_significant at FDR/q < 0.05, and the percentage. With a 22-sample mothur SOP demo, expect ~5–25 genus-level hits per method.
- Hit-set agreement: at least the top-3 by absolute log2fc per method usually agree; if Wilcoxon and ANCOM-BC have zero overlap, you have a problem (cohort too small, or the contrast doesn't actually exist in the data).
- pyDESeq2 hits that don't appear in Wilcoxon are often spurious driven by NB shrinkage on rare features; check the raw counts for those genera and consider tighter
min_prevalence.
- ANCOM-BC's
log2fc is bias-corrected — it may differ in magnitude from DESeq2's by a small constant offset (per the compositional correction), but the sign must agree on consensus hits. Sign disagreement on a consensus genus is a red flag.
- Reporting: always disclose cohort size, the FDR cutoff, and which
min_prevalence was used. A "significant" feature at min_prevalence=0.0 on n=5/group is uninterpretable.
Resource Map
- See
reference.md for compact copy-paste snippets.
- See
references/source-grounding.md for verified DA.wilcoxon / DA.deseq2 / DA.ancombc signatures and column-name conventions across versions.
- For the AnnData ingest that produces the input, see
omicverse-microbiome-16s-amplicon-dada2.
- For meta-analysis combining DA results across multiple cohorts, see
omicverse-microbiome-meta-analysis.
Examples
- "Run Wilcoxon, pyDESeq2, and ANCOM-BC on Early-vs-Late at the genus level and report the 3-way Venn at FDR 0.05."
- "Pick a single DA method for an n=12/group cohort with high zero-inflation — give the rationale."
- "Build the consensus hit set (intersection) and the Wilcoxon ∩ ANCOM-BC set for a publication report."
- "Diagnose why a DESeq2 hit doesn't appear in Wilcoxon — likely an NB-shrinkage artefact on a low-prevalence feature."
References
- Tutorial notebook:
t_16s_da_comparison.ipynb — three-way DA Venn on the mothur SOP cohort.
- Live API verified — see
references/source-grounding.md.
1---2name: omicverse-microbiome-da-comparison3description: Run all three differential-abundance methods (Wilcoxon, pyDESeq2, ANCOM-BC) on the same microbiome AnnData, compare their hit sets via 3-way Venn / overlap counts, and decide which to trust on a given cohort. Use when the user wants to benchmark DA methods on a 16S study, when picking between methods on a small or zero-inflated cohort, or when reporting consensus features that survive multiple tests.4---56# OmicVerse Microbiome — Differential-Abundance Method Comparison78## Goal910Take a preprocessed microbiome `AnnData` (output of the 16S amplicon skill — samples × ASVs with 7-rank SINTAX taxonomy in `var`) and run **all three DA methods exposed by `ov.micro.DA`** on the same two-group contrast — Wilcoxon (rank), pyDESeq2 (NB-GLM), ANCOM-BC (compositional). Compare their hit sets at a common FDR cutoff, report consensus / method-specific genera, and surface the biology around the three methods' different statistical assumptions so the user can pick (or report) the right one.1112## Quick Workflow13141. Load the AnnData produced by `omicverse-microbiome-16s-amplicon-dada2`. Drop control / non-relevant groups; keep exactly two for the contrast.152. Collapse to a chosen taxonomic rank (typically genus): `ov.micro.collapse_taxa(adata, rank='genus')`. DA at species/ASV level is noisy on small cohorts; genus is the canonical reporting rank for 16S.163. **Wilcoxon**: `ov.micro.DA(adata_genus).wilcoxon(group_key, group_a, group_b, min_prevalence=0.1)`. Non-parametric; fastest; tests ranks of relative abundance.174. **pyDESeq2**: `ov.micro.DA(adata_genus).deseq2(group_key, group_a, group_b, min_prevalence=0.1)`. NB-GLM on **raw counts**; uses RNA-seq-style shrinkage of small-count fold-changes.185. **ANCOM-BC**: `ov.micro.DA(adata_genus).ancombc(group_key, min_prevalence=0.1, pseudocount=1.0)`. Compositional bias-corrected ANCOM; closest to the compositional ground truth.196. Build sets of significant features at a common FDR cutoff (typically `0.05`). Watch for column-name differences across methods (`fdr_bh` for Wilcoxon / DESeq2; `q_value` or `fdr_bh` for ANCOM-BC).207. Tabulate the 3-way Venn (Wilcoxon-only / DESeq2-only / ANCOM-BC-only / pairwise overlaps / all-three) and render with `matplotlib_venn.venn3` if installed.218. Report two numbers in the writeup: (a) the *consensus* hit set (intersection across all three) for the strongest claim, and (b) the *Wilcoxon ∪ ANCOM-BC* set as a slightly more permissive convention if compositional correctness matters.2223## Interface Summary2425Same `ov.micro.DA` class, three different methods, all returning a `pd.DataFrame` indexed by the feature column with method-specific columns:2627```python28ov.micro.DA(adata).wilcoxon(29 group_key, group_a=None, group_b=None,30 rank=None, relative=True, min_prevalence=0.1,31) -> pd.DataFrame32# columns: feature, log2fc, pvalue, fdr_bh, mean_a, mean_b, prevalence_a, prevalence_b33# convention: log2fc > 0 ⇒ higher in group_a3435ov.micro.DA(adata).deseq2(36 group_key, group_a=None, group_b=None,37 rank=None, min_prevalence=0.1, alpha=0.05,38) -> pd.DataFrame39# columns: feature, baseMean, log2fc, lfcSE, stat, pvalue, fdr_bh40# convention: log2fc > 0 ⇒ higher in group_a; uses pyDESeq2's MLE-shrunk LFC.4142ov.micro.DA(adata).ancombc(43 group_key, rank=None,44 min_prevalence=0.1, pseudocount=1.0,45) -> pd.DataFrame46# columns: feature, log2fc, std_error, w_stat, pvalue, q_value (or fdr_bh)47# convention: log2fc > 0 ⇒ higher in group_a (after compositional bias correction).48```4950## Boundary5152**Inside scope:**53- Running all three DA methods on the same AnnData and the same two-group contrast.54- Set-arithmetic comparison of hit sets at a common FDR.55- Method-choice guidance based on cohort size / zero inflation / compositional concerns.56- Documenting consensus and method-specific features.5758**Outside scope:**59- Building / preprocessing the input AnnData — see `omicverse-microbiome-16s-amplicon-dada2`.60- Cross-cohort meta-analysis (combining DA results from multiple studies) — see `omicverse-microbiome-meta-analysis`.61- Three-or-more-group DA — `ov.micro.DA` is two-group; use Kruskal-Wallis externally.62- Repeated-measures / paired DA — out of scope for `ov.micro` (use mixed models in `ov.metabol` or external `nlme` etc.).63- Phylogenetically-aware DA (e.g. PERMANOVA on UniFrac distances) — see phylogeny skill.6465## Branch Selection6667**Wilcoxon (default for moderate cohorts)**68- Pros: non-parametric (no NB / log-normal assumption); fast (sub-second on a 22-sample × 250-genus cohort); robust to outliers.69- Cons: weak power on small cohorts (n<10/group); doesn't model compositional bias; relative-abundance scaling makes interpretation of log2fc less rigorous than DESeq2.70- Use when: n>=10/group, low-to-moderate sparsity, no strong compositional concern, you need fast / portable.7172**pyDESeq2 (RNA-seq-style NB-GLM)**73- Pros: well-calibrated p-values on raw counts; LFC shrinkage stabilises small-count fold-changes; mature implementation with extensive vignettes.74- Cons: NB assumption fails on highly zero-inflated data (microbiome can be 80%+ zeros); doesn't correct for compositional bias; slower than Wilcoxon.75- Use when: n is moderate, sparsity is moderate, you want NB-style shrinkage, reviewers expect DESeq2-style methodology.7677**ANCOM-BC (compositional bias-corrected)**78- Pros: explicit compositional correction (the only method here that doesn't ignore the simplex); recovers true effect direction on highly biased data; bias-corrected per-feature log-ratio model is closest to the ground-truth.79- Cons: slowest of the three; requires `skbio>=0.7.1`; can be conservative on small cohorts; the bias-correction adds a per-feature constant which shifts log2fc relative to DESeq2.80- Use when: compositional correctness matters (always for true microbiome relative-abundance reporting), n is moderate-to-large, downstream analysis uses log-ratio interpretation.8182**Consensus reporting strategy**83- For the *strongest* claim: intersection across all three (rare but unambiguous; survives method assumptions).84- For a *defensible* claim: Wilcoxon ∩ ANCOM-BC (combines a model-free test with the compositional-aware test; bypasses DESeq2's NB assumption).85- For *exploratory* hits: union; flag method-specific hits explicitly so the reader knows the assumption that drove them.86- Always report cohort size + sparsity + chosen FDR — DA results without those numbers are uninterpretable.8788**Column-name pitfalls**89- Wilcoxon / pyDESeq2 use `fdr_bh`; ANCOM-BC may use `q_value` or `fdr_bh` depending on `skbio` version. Pattern in the tutorial: `sig_col = 'q_value' if 'q_value' in ab.columns else 'fdr_bh'`.90- All three methods may also expose a raw `pvalue` column. Don't confuse pvalue and FDR when filtering — pvalue<0.05 is *not* FDR<0.05.9192## Input Contract9394- An `AnnData` from the 16S amplicon skill; `obs[group_key]` is a categorical with at least two values; the two-group slice should have `n>=5/group` for any of these tests to behave reasonably.95- `adata.X` is **integer counts** (DESeq2 requires raw counts; Wilcoxon and ANCOM-BC handle either, but raw counts are the canonical input).96- For ANCOM-BC: `pip install skbio>=0.7.1` (function raises `ImportError` if missing).97- For pyDESeq2: `pip install pydeseq2`.9899## Minimal Execution Patterns100101```python102import omicverse as ov103import anndata as ad104import matplotlib.pyplot as plt105106ov.plot_set()107108# 1) Load AnnData from the 16S amplicon skill, restrict to two groups.109adata = ad.read_h5ad('mothur_sop_16s.h5ad')110adata = adata[adata.obs['group'].isin(['Early', 'Late'])].copy()111print(adata.obs['group'].value_counts().to_dict())112113# 2) Collapse to genus.114adata_genus = ov.micro.collapse_taxa(adata, rank='genus')115print('samples × genera:', adata_genus.shape)116117# 3) Run all three DA methods on the same contrast.118wx = ov.micro.DA(adata_genus).wilcoxon(119 group_key='group', group_a='Early', group_b='Late', min_prevalence=0.1,120)121ds = ov.micro.DA(adata_genus).deseq2(122 group_key='group', group_a='Early', group_b='Late', min_prevalence=0.1,123)124ab = ov.micro.DA(adata_genus).ancombc(125 group_key='group', min_prevalence=0.1,126)127128print(f' Wilcoxon : {(wx["fdr_bh"] < 0.05).sum():3d} / {len(wx):3d} genera at FDR 0.05')129print(f' DESeq2 : {(ds["fdr_bh"] < 0.05).sum():3d} / {len(ds):3d}')130sig_col_ab = 'q_value' if 'q_value' in ab.columns else 'fdr_bh'131print(f' ANCOM-BC : {(ab[sig_col_ab] < 0.05).sum():3d} / {len(ab):3d}')132133# 4) Build sets and tabulate the 3-way Venn.134sig_wx = set(wx.loc[wx['fdr_bh'] < 0.05, 'feature'])135sig_ds = set(ds.loc[ds['fdr_bh'] < 0.05, 'feature'])136sig_ab = set(ab.loc[ab[sig_col_ab] < 0.05, 'feature'])137138print('Wilcoxon only :', len(sig_wx - sig_ds - sig_ab))139print('DESeq2 only :', len(sig_ds - sig_wx - sig_ab))140print('ANCOM-BC only :', len(sig_ab - sig_wx - sig_ds))141print('All three :', len(sig_wx & sig_ds & sig_ab))142143# 5) Render Venn (optional dependency)144try:145 from matplotlib_venn import venn3146 fig, ax = plt.subplots(figsize=(5, 5))147 venn3([sig_wx, sig_ds, sig_ab],148 set_labels=('Wilcoxon', 'DESeq2', 'ANCOM-BC'), ax=ax)149 ax.set_title('Genera significant at FDR/q < 0.05')150 plt.show()151except ImportError:152 pass153```154155## Validation156157- For each method: report `n_tested` (pre-min_prevalence filter), `n_significant` at FDR/q < 0.05, and the percentage. With a 22-sample mothur SOP demo, expect ~5–25 genus-level hits per method.158- Hit-set agreement: at least the top-3 by absolute log2fc per method usually agree; if Wilcoxon and ANCOM-BC have *zero* overlap, you have a problem (cohort too small, or the contrast doesn't actually exist in the data).159- pyDESeq2 hits that don't appear in Wilcoxon are often spurious driven by NB shrinkage on rare features; check the raw counts for those genera and consider tighter `min_prevalence`.160- ANCOM-BC's `log2fc` is bias-corrected — it may differ in *magnitude* from DESeq2's by a small constant offset (per the compositional correction), but the **sign** must agree on consensus hits. Sign disagreement on a consensus genus is a red flag.161- Reporting: always disclose cohort size, the FDR cutoff, and which `min_prevalence` was used. A "significant" feature at `min_prevalence=0.0` on n=5/group is uninterpretable.162163## Resource Map164165- See [`reference.md`](reference.md) for compact copy-paste snippets.166- See [`references/source-grounding.md`](references/source-grounding.md) for verified `DA.wilcoxon` / `DA.deseq2` / `DA.ancombc` signatures and column-name conventions across versions.167- For the AnnData ingest that produces the input, see `omicverse-microbiome-16s-amplicon-dada2`.168- For meta-analysis combining DA results across multiple cohorts, see `omicverse-microbiome-meta-analysis`.169170## Examples171- "Run Wilcoxon, pyDESeq2, and ANCOM-BC on Early-vs-Late at the genus level and report the 3-way Venn at FDR 0.05."172- "Pick a single DA method for an n=12/group cohort with high zero-inflation — give the rationale."173- "Build the consensus hit set (intersection) and the Wilcoxon ∩ ANCOM-BC set for a publication report."174- "Diagnose why a DESeq2 hit doesn't appear in Wilcoxon — likely an NB-shrinkage artefact on a low-prevalence feature."175176## References177- Tutorial notebook: [`t_16s_da_comparison.ipynb`](https://omicverse.readthedocs.io/en/latest/Tutorials-microbiome/t_16s_da_comparison/) — three-way DA Venn on the mothur SOP cohort.178- Live API verified — see [`references/source-grounding.md`](references/source-grounding.md).