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.
