Parse and create FCS (Flow Cytometry Standard) files v2.0-3.1. Read event data as NumPy arrays, extract channel metadata, handle multi-dataset files, export to CSV/FCS. For advanced gating and compensation use FlowKit.
FlowIO is a lightweight Python library for reading and writing Flow Cytometry Standard (FCS) files. It parses FCS metadata, extracts event data as NumPy arrays, and creates new FCS files. Supports FCS versions 2.0, 3.0, and 3.1. Minimal dependencies — ideal for data pipelines and preprocessing before advanced analysis.
pip install flowio numpy pandas
Requires Python 3.9+. No compiled dependencies — installs on any platform.
from flowio import FlowData
flow = FlowData("experiment.fcs")
print(f"Events: {flow.event_count}, Channels: {flow.channel_count}")
print(f"Channels: {flow.pnn_labels}")
events = flow.as_array() # Shape: (n_events, n_channels)
print(f"Data shape: {events.shape}")
The FlowData class is the primary interface for reading FCS files.
from flowio import FlowData
# Standard reading
flow = FlowData("sample.fcs")
print(f"Version: {flow.version}") # '3.0', '3.1', etc.
print(f"Events: {flow.event_count}")
print(f"Channels: {flow.channel_count}")
# Event data
events = flow.as_array() # Preprocessed (gain, log scaling)
raw = flow.as_array(preprocess=False) # Raw values
print(f"Shape: {events.shape}") # (n_events, n_channels)
# Memory-efficient: metadata only (skip DATA segment)
flow_meta = FlowData("sample.fcs", only_text=True)
print(f"Instrument: {flow_meta.text.get('$CYT', 'Unknown')}")
# Handle problematic files
flow = FlowData("bad.fcs", ignore_offset_discrepancy=True)
flow = FlowData("bad.fcs", use_header_offsets=True)
# Exclude null channels
flow = FlowData("sample.fcs", null_channel_list=["Time", "Null"])
Extract channel names, types, and ranges from FCS files.
flow = FlowData("sample.fcs")
# Channel names
pnn = flow.pnn_labels # Short names: ['FSC-A', 'SSC-A', 'FL1-A', ...]
pns = flow.pns_labels # Descriptive: ['Forward Scatter', 'Side Scatter', 'FITC', ...]
pnr = flow.pnr_values # Range/max values per channel
# Channel type indices
scatter_idx = flow.scatter_indices # [0, 1] — FSC, SSC
fluoro_idx = flow.fluoro_indices # [2, 3, 4] — fluorescence channels
time_idx = flow.time_index # Time channel index (or None)
# Access by type
events = flow.as_array()
scatter_data = events[:, scatter_idx]
fluoro_data = events[:, fluoro_idx]
# Full metadata (TEXT segment dictionary)
text = flow.text
print(f"Date: {text.get('$DATE', 'N/A')}")
print(f"Instrument: {text.get('$CYT', 'N/A')}")
Generate new FCS files from NumPy arrays.
import numpy as np
from flowio import create_fcs
# Basic creation
events = np.random.rand(10000, 5) * 1000
channels = ["FSC-A", "SSC-A", "FL1-A", "FL2-A", "Time"]
create_fcs("output.fcs", events, channels)
# With descriptive names and metadata
create_fcs(
"output.fcs",
events,
channels,
opt_channel_names=["Forward Scatter", "Side Scatter", "FITC", "PE", "Time"],
metadata={"$SRC": "Python pipeline", "$DATE": "17-FEB-2026", "$CYT": "Synthetic"},
)
# Output: FCS 3.1, single-precision float
Handle FCS files containing multiple datasets.
from flowio import FlowData, read_multiple_data_sets, MultipleDataSetsError
# Detect multi-dataset files