flow-cytometry/differential-analysis/SKILL.md
Differential abundance (DA) and differential state (DS) analysis for flow and mass cytometry - tests which cell populations change in frequency or marker expression between conditions using diffcyt (edgeR/voom/GLMM for DA, limma/LMM for DS), with cydar, CITRUS, and compositional methods (sccomp, scCODA, DCATS) as alternatives. Covers the sample-is-the-experimental-unit principle, design/contrast and mixed-model formulas, compositionality of cluster proportions, and FDR across clusters. Use when comparing populations between groups, choosing a DA method, handling paired/batch designs, or deciding whether compositional correction is needed.
npx skillsauth add GPTomics/bioSkills bio-flow-cytometry-differential-analysisInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Reference examples tested with: diffcyt 1.22+, CATALYST 1.26+, edgeR 4.0+, limma 3.58+.
Before using code patterns, verify installed versions match. If versions differ:
packageVersion('<pkg>') then ?function_name to verify parameterstestDA_edgeR/testDS_limma are diffcyt functions operating on count/median objects from calcCounts/calcMedians; the CATALYST-integrated path is the diffcyt() wrapper on the SCE. Confirm the signature with ?diffcyt before relying on it.
"Compare cell populations between my conditions" -> Test cluster frequencies (DA) and within-cluster marker expression (DS) between groups, with the sample (not the cell) as the unit.
diffcyt::diffcyt(sce, analysis_type='DA', method_DA='diffcyt-DA-edgeR', design, contrast)diffcyt(sce, analysis_type='DS', method_DS='diffcyt-DS-limma', ...)Tens of thousands of cells from one donor are technical PSEUDOREPLICATES, not independent observations. A per-cell test (Wilcoxon across all cells) treats them as n = cells and produces astronomically significant p-values from two mice - it is the single most common statistical sin in modern cytometry (Hurlbert 1984 Ecol Monogr 54:187; the cytometry mirror of the scRNA-seq pseudobulk lesson). The correct unit is the SAMPLE/subject: diffcyt aggregates cells to PER-SAMPLE-PER-CLUSTER counts (DA) and PER-SAMPLE-PER-CLUSTER arcsinh-MEDIANS (DS), then tests across samples with edgeR/limma/GLMM (Weber 2019 Commun Biol 2:183). Biological replication is mandatory (>= 2-3 per group); DA from a single sample per condition has no valid test. Paired with this: cluster proportions are COMPOSITIONAL (they sum to 1), so a real increase in one population mechanically forces apparent depletion in others - a source of false DA in "unchanged" clusters.
| Method | Citation | Mechanism | When to use | |--------|----------|-----------|-------------| | diffcyt-DA-edgeR / voom | Weber 2019 Commun Biol 2:183 | edgeR/voom empirical-Bayes on per-sample counts; optional TMM | standard 2+ group with replicates (DEFAULT) | | diffcyt-DA-GLMM / DS-LMM | Weber 2019 | random effects in the formula | paired/repeated-measures/nested (subject random effect) | | cydar | Lun 2017 Nat Methods 14:707 | overlapping hyperspheres + edgeR + spatial FDR | continuum, avoid hard clusters | | CITRUS | Bruggner 2014 PNAS 111:E2770 | hierarchical clustering + LASSO | predictive signature, LARGE n; correlated-not-causal; largely superseded | | sccomp / scCODA / DCATS | Mangiola 2023 PNAS 120:e2203828120 / Buttner 2021 Nat Commun 12:6876 / Lin 2023 Genome Biol 24:151 | simplex-aware compositional models | strong compositional shift (one pop dominates); DCATS for assignment uncertainty |
Goal: Test abundance and state on a CATALYST-clustered SCE.
Approach: Build design + contrast from ei(sce); the diffcyt() wrapper uses the stored clustering. State markers are tested in DS, type markers define DA clusters.
library(CATALYST); library(diffcyt)
sce <- readRDS('sce_clustered.rds')
design <- createDesignMatrix(ei(sce), cols_design = 'condition')
contrast <- createContrast(c(0, 1)) # Treatment vs Control
res_DA <- diffcyt(sce, clustering_to_use = 'meta20',
analysis_type = 'DA', method_DA = 'diffcyt-DA-edgeR',
design = design, contrast = contrast)
res_DS <- diffcyt(sce, clustering_to_use = 'meta20',
analysis_type = 'DS', method_DS = 'diffcyt-DS-limma',
design = design, contrast = contrast)
library(SummarizedExperiment)
rowData(res_DA$res) # cluster_id, logFC, p_val, p_adj (BH across clusters)
Goal: Account for within-subject correlation (e.g. pre/post on the same donor).
Approach: Use a GLMM/LMM method with a random effect for subject via a formula.
formula <- createFormula(ei(sce), cols_fixed = 'condition', cols_random = 'patient_id')
res_DA <- diffcyt(sce, clustering_to_use = 'meta20',
analysis_type = 'DA', method_DA = 'diffcyt-DA-GLMM',
formula = formula, contrast = createContrast(c(0, 1)))
Goal: Confirm a headline single-population shift is not inducing artifactual reciprocal depletion.
Approach: Re-test with a simplex-aware model when one cluster changes a lot or total yield differs by group.
# If a dominant population expands, the apparent depletion of others may be a simplex artifact.
# Re-test with sccomp / scCODA (reference cell type) / DCATS (assignment uncertainty)
# before reporting reciprocal depletion as independent biology.
Trigger: Wilcoxon/t-test across all cells. Mechanism: cells aren't independent. Symptom: p ~ 1e-40 from few subjects. Fix: aggregate to per-sample summaries (diffcyt).
Trigger: one population expands strongly. Mechanism: proportions sum to 1. Symptom: significant "depletion" of unrelated clusters. Fix: TMM only when total cell abundance is NOT itself the biological signal (else it removes real signal), or a compositional method (sccomp/scCODA/DCATS); report total-yield differences.
Trigger: normalizing batch out then testing naively. Mechanism: over-correction removes real signal. Symptom: attenuated effects. Fix: include batch in the design; if batch == condition, no rescue - design it out.
Trigger: 1 sample per condition. Mechanism: no error term. Symptom: uninterpretable p. Fix: require >= 2-3 biological replicates per group.
| Threshold | Source | Rationale | |-----------|--------|-----------| | >= 2-3 biological replicates per group | Weber 2019 | minimum for a valid DA/DS error term | | BH FDR across clusters (and clusters x markers for DS) | diffcyt | high-resolution grids have many tests | | arcsinh median as DS statistic | Nowicka 2017 | robust per-cluster per-sample summary |
| Error / symptom | Cause | Solution |
|-----------------|-------|----------|
| testDA_edgeR(sce, ...) fails | wrong signature | use the diffcyt() wrapper on the SCE, or calcCounts first |
| results empty | wrong clustering_to_use name | match the stored clustering id (e.g. meta20) |
| no DS results | state markers not flagged | set marker_class='state' in the panel |
| paired design ignored | used fixed-effect method | use diffcyt-DA-GLMM with a random effect |
development
Installs 425 bioinformatics skills covering sequence analysis, RNA-seq, single-cell, variant calling, metagenomics, structural biology, and 56 more categories. Use when setting up bioinformatics capabilities or when a bioinformatics task requires specialized skills not yet installed.
testing
Chains a somatic (tumor-normal) SNV/indel and structural-variant pipeline end to end with GATK Mutect2 (or Strelka2), wiring the somatic-specific machinery - panel-of-normals and gnomAD germline-resource priors, GetPileupSummaries/CalculateContamination, and LearnReadOrientationModel FFPE/oxoG orientation-bias filtering fed into FilterMutectCalls. Use when calling somatic mutations from a tumor-normal pair (or tumor-only with PoN caveats), deciding which artifact filter removes which class of false positive, reasoning about VAF/purity/ploidy and clonal-vs-subclonal detection, adding somatic SV/CNV or TMB/MSI/signatures, or routing variants to AMP/ASCO/CAP tier and oncogenicity interpretation (never germline ACMG).
development
End-to-end pooled and single-cell CRISPR screen analysis from FASTQ to hit genes. Orchestrates library design QC, guide counting, six-stage screen QC (plasmid Gini, replicate Pearson, CEGv2 PR-AUC, copy-number artifact), method-appropriate hit calling across MAGeCK RRA/MLE, BAGEL2, drugZ, JACKS, and Chronos, cancer-cell-line copy-number correction (CRISPRcleanR / Chronos), batch correction for multi-batch screens, and the specialized branches for combinatorial paralog screens, single-cell Perturb-seq, base-editor variant-function screens, prime-editor screens, and in vivo bottleneck-aware screens. Use when analyzing any pooled CRISPR screen end-to-end, matching the hit-calling method to the experimental design, integrating copy-number correction into the pipeline, or branching the workflow for single-cell, combinatorial, base-editor, prime-editor, or in vivo variants.
development
Transcribe DNA to RNA and translate to protein using Biopython, with NCBI codon-table selection, CDS validation, and six-frame ORF finding. Use when converting a CDS or ORF to its amino-acid sequence, selecting a non-standard (mitochondrial, bacterial, ciliate) genetic code, validating a coding sequence, or scanning all reading frames.