Skip to contents

Every caveat in one place, and the methods left out, with the reason for each.

Read this before quoting a number from a run. Nothing here is hypothetical: each limitation is something the pipeline can do to a real result, and each exclusion is a decision that has been argued rather than an oversight.

Known limitations

Every caveat below exists somewhere else in this repository already, in a code comment, a NEWS entry or a function’s documentation. Scattered like that, a new user has no way to see the aggregate risk before deciding whether to trust a result. This page is the single place that collects them.

The same page also records what was considered and not added, and why. That half exists so the same arguments do not get relitigated every six months.

Part 1. Limitations

A control used to be believed too easily

staining_verdict() read an absent is_control column as “might be a control”, because only is_control = FALSE positively asserts that a file is a biological sample. On a sheet without that column, any sample whose CD45 gate found no density minimum was promoted to the unstained reference for its whole panel, and every threshold there became the 99.5th percentile of that one sample. Where it was really a stained sample, every population collapsed, with no error raised and no warning printed.

How to check whether a result you already have was affected. Open thresholds_used.csv. If source reads control_q995 on most markers, those thresholds came from a reference. Find out which sample supplied it before reading a single frequency. A cohort that declares no control should show valley, tailgate and quantile_fallback only.

Fixed. --discover-controls restores the old behaviour for anyone who needs it.

The version number will not tell you whether you are affected, so check the file. A 1.0.0 image was published before this fix and replaced afterwards under the same tag, so two pulls of bhagesh/cyraven:1.0.0 can differ. The source column in thresholds_used.csv is the reliable test, and it costs a few seconds.

Automated gates have not been benchmarked against independent expert gating

learn_convex_gate() and explain_cluster() are validated on held-out cells, and, where donor count allows, leave-one-donor-out. Both of those are internal: the only cohort they have been measured on is the demonstration one. Nothing here establishes that the defaults generalise to a different panel, a different instrument, or a different laboratory’s staining.

Two consequences. The hardcoded constants in density_valley(), min_rel_depth among them, may have been tuned to one cohort without anyone intending to tune them. And a learned gate is descriptive: it carries no p-value and is not evidence that the population is real.

Read the minimum, not the median. On a 21-donor cohort, three clusters gave median F1 of 0.987, 0.731 and 0.189 with worst-donor F1 of 0.953, 0.111 and 0.008. Cells held out from a fit come from the same donors, acquired in the same tubes on the same day, so they flatter the model. Only the per-donor minimum tells you whether a gate survives contact with the next patient.

No mixed-effects differential abundance

The tests are rank-based: Wilcoxon rank-sum, Kruskal-Wallis, Wilcoxon signed-rank and Friedman when paired, with Benjamini-Hochberg within families. Parametric equivalents sit beside them with their assumptions attached.

None of those can model donor and batch as crossed random effects. They can stratify or pair, which is not the same thing. A GLMM with a count offset is the standard remedy, and statistical_methods.csv names it in the catalogue without computing it. That gap is deliberate for now rather than overlooked, and it is recorded in the backlog.

A single convex gate can misrepresent a multimodal population

learn_convex_gate() fits one convex polygon per population. Nothing currently checks whether the label being fitted is a single coherent region in the chosen marker pair before fitting it. A hull drawn around two disjoint clusters of the same nominal population reports a large, low-precision polygon that “explains” the label without describing the geometry.

If a population is known to be phenotypically heterogeneous, check spec_gaps.csv from explore mode, which reports how many effective clusters a declared label spans.

FMO disagreement is computed but not enforced

fmo_agreement.csv reports the distance between a derived cut and its FMO-anchored equivalent in units of that threshold’s own uncertainty. Beyond about three units, one of them is wrong. Nothing acts on that: the file is written and the run proceeds. An unattended pipeline will not stop on it, unlike --fail-on-drift for baseline conformance.

Spectral panels can fragment a cohort silently

The panel fingerprint is the set of resolved marker names, so a channel present in some files and absent in others makes them separate panels, each with its own cofactor, thresholds and embedding. Spectral unmixing writes extracted autofluorescence back as extra channels and how many appear varies per acquisition, so twelve comparable files can become seven panels of one to three files each.

Nothing fails when this happens. Every file loads and every table is written. --list-channels now names the offending channels and prints the exact --ignore-channels pattern that merges the cohort, so run it before a long run.

There is no unmixing-specific QC. spreading.R diagnoses conventional spillover spreading well, but poor reference spectra and autofluorescence misattribution are distinct failure modes it does not capture.

Everything is documented on one cohort

The README, the worked example and the container smoke test all run on the same demonstration cohort: one panel, one biological question. Behaviour on a larger marker panel, on spectral channels, or on a design with three or more groups is exercised by tests but not by a documented worked example.

Batch correction changes intensities, not conclusions, and can be wrong

--correct-batch aligns marker distributions and refuses above a Cramér’s V threshold between batch and group, because at that overlap removing the batch and removing the finding are the same operation. --batch-method cluster fits one map per cell type rather than one per file, which is the better choice when a detector shift moves bright and dim populations by different amounts.

Two things to know. The clustering it fits is derived from the data, so a cohort with batch-specific biology will have that biology partly absorbed into the correction. And no correction is applied to the gate hierarchy, which is derived per sample and is already batch-local; correction is descriptive of intensity only.

Part 2. Deliberately not included

Nothing below is missing by accident. Each was considered against what the package already does and rejected for the stated reason. If circumstances change, the reason is what has to change first.

Packages

Package Overlaps Why not
AutoSpill compensation Installs only from GitHub, which breaks the dated-snapshot guarantee the Docker image rests on. For spectral data, unmixing has already happened on the instrument
CytoNorm batch normalisation Same GitHub-only problem, and its API is file-based: QuantileNorm.train() takes FCS paths and QuantileNorm.normalize() writes new FCS to disk, while correction here happens on an in-memory matrix already read, transformed and gated. Its method, one map per cell type, is implemented natively as --batch-method cluster
fdaNorm, gaussNorm normalisation No longer supported, and fdaNorm is gone from recent flowStats versions
flowTrans transformation The cofactor is derived per panel from the data, so there is nothing to fit. The package also carries a multi-year unfixed biexponential bug
ggcyto, flowViz plotting One figure system and one theme already. A second plotting idiom costs consistency and adds nothing a reader sees
Rtsne dimensionality reduction UMAP only, deliberately. Two embeddings of the same cells invite comparison between two things that are not comparable
diffcyt, CATALYST differential abundance They test clusters defined on the subsample, so the subsample changes what is tested. That is the failure the donor-level design exists to avoid. Note this rejects the paradigm, not the mathematics: diffcyt-DA-edgeR on our own per-population counts is available with --edger; a GLMM remains open
FastPG, Rphenograph clustering FlowSOM is a cross-check here, not the primary method, so clustering speed is not the binding constraint
openCyto gating CSV templates applied uniformly across samples. The premise of this package is that one gate copied across samples is the problem
flowAI, flowClean acquisition QC They remove anomalous events. Acquisition QC here reports what removing them would change and deletes nothing unless asked. PeacoQC fills that role instead, opt-in with --peacoqc
PICAFlow, CytoPipeline, CytoExploreR whole workflow Alternatives to this package, not components of it
CytoML, flowWorkspace FlowJo import, Gating-ML Not rejected, deferred. Reading a .wsp into --external-labels would let a hand-drawn strategy be scored leave-one-donor-out. Held back on dependency weight: it pulls flowWorkspace and ncdfFlow, which compile C++. Both now come into the image with PeacoQC (cytolib and RProtoBufLib already came with flowCore), so that reason is weaker than it was

Capabilities

Capability Why not
Trajectory inference / pseudotime Ordering cells along a trajectory assumes the population is a continuum that was sampled densely enough to reconstruct. A gated cytometry panel with a dozen markers usually is not, and a trajectory will still be returned
Batch correction without a confounding check Available in several other tools. Where batch and group overlap far enough, correcting removes the finding, so the check comes first and the refusal is the feature
Automatic removal of anomalous events The impact of removal is reported first. --drop-unstable-events (the in-house detector) or --peacoqc (PeacoQC) performs it only when asked
A p-value on anything that cannot be collapsed to one value per sample Testing over events treats one deeply acquired donor as many independent observations. Those quantities get an effect size and no p-value
Trained classifiers for label transfer to a new cohort A classifier transfers a labelling; it does not transfer the reason for it. What moves between cohorts here is a gating strategy, which is inspectable, exportable to the instrument, and can be scored per withheld donor. A model that cannot be read is a worse artefact even when it is more accurate
Cross-panel benchmarking against public expert gates Wanted, and blocked on a rule that is not worth breaking: third-party FCS does not go into this repository. It needs an external harness, not a vignette
A GUI The point of the package is that a run is one command with a recorded invocation. A GUI makes the choices unrecorded again, which is the problem it was built to solve

Where to look when something seems wrong

Symptom File to open first
Frequencies near zero across the board thresholds_used.csv, source column
A population is missing entirely staining_qc.csv, then gating_qc.png
More panels than expected --list-channels, which now names the fix
A comparison you expected is absent design_feasibility.csv
A difference that looks too good threshold_drift_stats.csv, then difference_over_gate_u
Correction ran or did not batch_correction.csv

Scope: what cyRAVEN does not do

Nine methods available in comparable toolboxes are absent from cyRAVEN. Each section states the omission, the reasoning, and the condition under which it should be revisited. Where a method is required now, the Using cyRAVEN with cyCONDOR covers combined use with cyCONDOR.

1. Harmony and CytoNorm

Batch correction is implemented. --correct-batch performs monotone quantile alignment and refuses above a configurable Cramér’s V between batch and group.

These two implementations specifically are not wrapped. Both introduce substantial dependency weight, and neither declines to execute when batch and biological effect are collinear. That refusal is the behaviour cyRAVEN treats as the purpose of the feature.

Revisit when a study design requires reference-based normalisation across instruments rather than distributional alignment within a batch.

2. Phenograph

FlowSOM addresses the same requirement at lower cost at these event counts. Phenograph determines cluster count from the data rather than accepting it as a parameter, which is informative, but requires Rphenograph or Rphenoannoy. Neither is distributed through CRAN or Bioconductor, and a GitHub-only dependency is incompatible with the reproducibility guarantee the container provides.

Revisit when cluster count is the quantity under investigation rather than a setting.

--explore-k is likewise a setting rather than a finding, and explore_provenance.csv records it as one, because a cluster count chosen from the data is one more thing the number of significant results depends on.

3. diffcyt

The rank tests address the same hypotheses without distributional assumptions, which is the correct trade at single-digit n per group.

The edgeR form is available opt-in with --edger. It is applied to the gated populations’ counts per sample, not to clusters defined on a subsample, so it avoids the objection in the table above, and it is written beside the rank tests, never in place of them. limma-voom and the GLMM are not implemented.

Revisit at approximately fifteen samples per group, where the empirical Bayes prior becomes informative and mixed models can accommodate repeated measures.

4. Per-marker cofactors

derive_cofactor() computes per-marker candidates and returns them in attr(cf, "per_marker"). Application is not implemented.

Applying them alters the arcsinh scale per channel, displacing every threshold, marker median and embedding distance. Validation requires re-derivation and re-inspection of every gating QC figure in the affected dataset.

Revisit when gating QC demonstrates channels whose background distributions differ in scale. The data required for that judgement is already collected.

5. Trajectory inference

Pseudotime methods assume a continuous differentiation process sampled across its extent. Peripheral blood immunophenotyping panels predominantly measure terminally differentiated populations, where a fitted trajectory reflects the fitting procedure rather than a developmental process.

Revisit when the panel resolves a developmental axis, for example thymic emigrants or plasmablast maturation. cyCONDOR provides diffusion maps and Slingshot.

6. Label transfer

Classifier-based cell type assignment is standard elsewhere. Here the training labels originate from a gating specification, so a classifier trained on them reproduces that specification including its errors.

Unsupervised cluster concordance is the stronger check for the same purpose, since it does not consult the labels.

Revisit when an externally annotated reference dataset is available for training.

7. FlowJo ingestion

Gates travel outward. --export-gates writes learned strategies as ISAC Gating-ML 2.0, and --flowjo-export writes FCS with embedding coordinates as additional parameters, so a result can be opened in FlowJo, Cytobank or FlowRepository.

The inverse is absent: cyRAVEN does not read gates from a .wsp workspace, which requires CytoML and flowWorkspace. The export needs neither, because a Gating-ML document is written directly, whereas parsing a proprietary workspace is not something to reimplement.

Revisit when manual FlowJo gates are required as input rather than output.

8. Cell-level testing

Absent for the reason in section 1 of the Statistics: event counts are set by acquisition duration, so a test over pooled events derives its degrees of freedom from instrument time. Toolboxes providing a cell-level Wilcoxon distribute it with a warning against its use.

The cell-level view is available descriptively in subcluster_marker_shifts.csv, which reports effect sizes without p-values.

9. Compensation matrices

cyRAVEN applies the spillover matrix stored in the FCS keyword block and reports its absence. It does not compute one from single-stain controls, which belongs in acquisition software where the controls can be inspected.

10. The 2025-2026 methods survey

A survey of cytometry method releases and papers to August 2026, recorded here so the same candidates are not re-evaluated from scratch. Adopting nothing is a decision too, and this is where it is written down.

Worth adopting, ranked.

Method What it adds that cyRAVEN lacks Licence
staRgate Gates a marker against a unimodal background population rather than a density minimum. That is the case cyRAVEN gives a tail gate, a fixed 3.5 robust standard deviations above the dominant mode and flagged for review, and on a 29-marker panel across 266 subpopulations it matched manual gating at 0.97 concordance MIT
CompensAID Flags, in the stained sample, marker pairs whose source-positive population is skewed in the receiver, the mark of a wrong reference control: it segments that population and flags the pair when the last segment’s Secondary Stain Index is below -1, a rule validated against specialists. cyRAVEN’s trend_rho screens the same region with an unvalidated cut GPL-3
CytoScan Between-file anomaly detection, including novelty against a locked reference set. cyRAVEN’s acquisition QC is within-file, so a whole tube that is unlike its cohort passes preprint
CytoMDS Attributes a batch separation back to the channels that caused it. cyRAVEN’s EMD says a batch differs; this says which markers made it differ GPL-3
Rajwa & Roederer 2025 Effective rank, information efficiency and the Cramer-Rao lower bound matrix, all computable from the spillover matrix already parsed. Not software – three formulas and a vocabulary that makes the existing QC legible to a reviewer paper

Considered and declined, with the reason.

sccomp tests differential variability and can transfer priors from an earlier cohort, neither of which the CLR re-test does, but it pulls Stan into the image for a question users have not asked. propeller borrows variance across populations, which is the right instinct at n = 5-10, and is worth offering beside the rank tests rather than instead of them, because it assumes a variance structure the rank tests do not. CytoBatchFlagR is CC BY-NC-ND, which forbids redistribution in a packaged pipeline, and its MSI/frequency/EMD triad largely duplicates what is here. flowMagic reaches 65% accuracy on rare populations, which is where automated gating has to earn its keep, and a trained model is not auditable the way a declared threshold is. GPCT is a Python transformer answering a different question, sample-level outcome prediction rather than population quantification.

What did not change, which is why nothing here chases it. MIFlowCyt has had no revision. Gating-ML is still 2.0 and FCS still 3.2, so the export target is stable. The Cossarizza EJI guidelines are still the third edition. EuroFlow’s 2025 updates are wet-lab only. SOULCAP intends to standardise population naming against Cell Ontology identifiers but has shipped no ontology, so the whole of the forward-compatible work is an optional cl_id: field per population.

And on small cohorts specifically: there is no consensus to follow. No ISAC position paper, no journal guidance, no methods paper aimed at the n < 20 immunophenotyping case. The field is quietly importing propeller and sccomp from single-cell RNA sequencing. The approach here (rank tests, Cliff’s delta, Benjamini-Hochberg within a family, bootstrap intervals, and the attainable-p floor drawn on the figure) is behind no published standard, because there is not one.