Skip to content

Analysis Tools

Most flow cytometry labs use GUI software like FlowJo or FCS Express. These tools are excellent for small panels and routine immunophenotyping. But as panels grow beyond 20 parameters, scripted analysis in R or Python becomes essential for reproducibility and scalability.

FlowJo is the most widely used flow cytometry software. It supports manual gating, compensation, basic statistics, and figure export. Most published flow cytometry figures come from FlowJo. It runs on Mac and Windows with a commercial license.

FCS Express offers similar functionality with stronger integration into PowerPoint and Excel workflows. Some labs prefer it for its layout and reporting features.

These tools work well for conventional panels with fewer than 10 markers. Their main limitation is manual gating. Every analysis requires an operator to draw gates by hand, which introduces the reproducibility problems discussed in the previous page.

R has the most mature ecosystem for computational flow cytometry. The Bioconductor project hosts most of these packages.

flowCore is the foundation package. It reads FCS files into R, stores events as a flowFrame object, and provides basic operations like compensation, transformation, and simple gating. Almost every other flow cytometry package in R depends on flowCore.

library(flowCore)
fs <- read.flowSet(path = "fcs_files/")

flowWorkspace manages gating hierarchies. It stores a tree of gates applied to a flowSet, making it easy to extract populations at any level. This is the R equivalent of the gating tree you see in FlowJo.

ggcyto extends ggplot2 for flow cytometry data. It understands flowFrame and GatingSet objects, so you can create publication quality dot plots, density plots, and histograms with familiar ggplot2 syntax.

library(ggcyto)
autoplot(gs[[1]], "CD4", "CD8")

CytoExploreR provides interactive exploration of flow cytometry data. It is useful for quickly inspecting gating strategies and comparing samples during early analysis.

openCyto implements template based automated gating. You define your gating hierarchy in a CSV file specifying which algorithm to use at each step. The package then applies those gates consistently across all samples.

A typical template might look like this:

alias pop parent dims gating_method
singlets + root FSC-A,FSC-H singletGate
lymph + singlets FSC-A,SSC-A flowClust
cd3pos + lymph CD3 mindensity
cd4pos + cd3pos CD4,CD8 quadrantGate

This approach is fully reproducible. The same template produces the same gates on every run.

FlowSOM uses self-organizing maps to cluster cells across all markers simultaneously. It is one of the most popular unsupervised methods for high-dimensional flow and mass cytometry data. FlowSOM first builds a grid of nodes, then metaclusters those nodes into cell populations.

library(FlowSOM)
fsom <- FlowSOM(ff, colsToUse = markers, nClus = 20)

FlowSOM is fast and scales well to millions of events. It works best when you have a reasonable estimate of the number of expected populations.

CATALYST provides a complete workflow for differential discovery and differential abundance analysis. It is designed for mass cytometry but works equally well with spectral flow cytometry data. CATALYST handles clustering, dimensionality reduction, and statistical testing for differences between conditions.

The Python ecosystem for flow cytometry is less mature than R but growing steadily.

FlowIO and fcsparser both read FCS files into Python. FlowIO is a pure Python implementation. fcsparser is simpler and returns data as a pandas DataFrame.

import fcsparser
meta, data = fcsparser.parse("sample.fcs")

FlowCal provides calibration routines for converting arbitrary fluorescence units to standardized units using calibration beads. This is important for quantitative flow cytometry where you need to compare fluorescence intensities across experiments.

Some researchers use scanpy and anndata for flow cytometry analysis. These tools were built for single-cell RNA-seq, but the data structure is similar: cells as rows, features as columns. Loading flow cytometry data into an AnnData object gives you access to UMAP, Leiden clustering, and the rest of the scanpy ecosystem.

import anndata as ad
import scanpy as sc
adata = ad.AnnData(X=data[markers].values)
sc.pp.neighbors(adata)
sc.tl.umap(adata)
sc.tl.leiden(adata)

This approach works but lacks flow-specific features like compensation and FCS file handling. You need to preprocess the data separately before loading it into anndata.

Panels with 20 or more markers cannot be fully explored with manual gating on bivariate plots. Unsupervised clustering algorithms examine all dimensions at once and find populations that would be invisible in any single 2D plot.

Flow and mass cytometry data spans several orders of magnitude. Raw fluorescence values are not suitable for clustering or dimensionality reduction. The standard transformation is the inverse hyperbolic sine (arcsinh) with a cofactor:

transformed = arcsinh(raw / cofactor)

The cofactor differs by instrument type:

Instrument Cofactor
Mass cytometry (CyTOF) 5
Fluorescence flow cytometry 150

This transform compresses high values while preserving differences near zero. It is similar to a log transform but handles zero and negative values gracefully.

UMAP and tSNE reduce high-dimensional cytometry data to two dimensions for visualization. These are the same algorithms used in single-cell RNA-seq analysis. They show the overall structure of cell populations but should not be used for quantitative analysis. Distances on a UMAP plot do not represent real distances in marker space.

FlowSOM, PhenoGraph, and Leiden are the most common clustering methods:

  • FlowSOM: Fast, deterministic with a fixed seed, good default choice.
  • PhenoGraph: Builds a k-nearest-neighbor graph, then applies Louvain community detection. Good for discovering rare populations.
  • Leiden: An improved version of Louvain with better-connected communities. Available in scanpy.
Feature R Python
FCS file reading flowCore (robust, full spec support) FlowIO, fcsparser (basic)
Compensation flowCore (built-in) Manual implementation needed
Automated gating openCyto (mature, template-based) No equivalent
Unsupervised clustering FlowSOM, CATALYST scanpy (adapted from scRNA-seq)
Visualization ggcyto (publication quality) matplotlib (manual)
Community and publications Large, established Growing
Best for Standard and high-dimensional analysis Integration with ML pipelines

For most flow cytometry analysis, R is the stronger choice. The Bioconductor ecosystem was built for this data type. Python is a good option when you need to integrate flow cytometry data with machine learning workflows or when your lab already works primarily in Python.

If you are starting a new flow cytometry analysis project, here is a practical guide:

  1. Small panels (fewer than 10 markers), routine work: FlowJo or FCS Express. Manual gating is fine.
  2. Small panels, need reproducibility: openCyto in R with a gating template.
  3. Large panels (20+ markers): FlowSOM or CATALYST in R. Manual gating cannot cover all dimensions.
  4. Integration with Python ML pipelines: Load preprocessed data into anndata and use scanpy.