Flowkit
K-DenseCommunityCreated by K-Dense Inc.
- Skill
- Tool integration
- Science and research
- MIT
- Low risk
- Has scripts
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
FlowKit is a Python-based tool for processing and analyzing flow cytometry data. It enables users to perform spillover compensation, apply logicle and biexponential transforms, and execute hierarchical gating strategies. The tool supports GatingML 2.0 and provides compatibility with a subset of FlowJo 10 workspaces, allowing for reproducible analysis of FCS files. It is designed for researchers needing to automate gating, generate population statistics, or validate cytometry results programmatically.
What it does
- Applies spillover compensation to flow cytometry data.
- Executes hierarchical gating strategies using GatingML or FlowJo workspaces.
- Performs logicle and biexponential data transformations.
- Generates reproducible gate counts and population percentage reports.
- Exports analysis results including provenance and population paths.
- Provides a Python API for building custom gating strategies.
When to use it
- When you need to reproduce FlowJo 10 workspace analyses in a Python environment.
- When automating the gating of large batches of FCS files.
- When you require programmatic control over cytometry data processing and compensation.
- When generating standardized reports for scientific studies.
Advantages
- Enables reproducible, scriptable cytometry analysis.
- Supports standard GatingML 2.0 formats for interoperability.
- Provides detailed provenance and reporting for scientific audit trails.
- Allows for custom, complex gating logic beyond standard GUI tools.
Usage examples
- “Analyze a FlowJo workspace study using FlowKit.”
- The tool processes the specified FCS files against the workspace definitions, outputting a gate_report.csv and provenance.json with population statistics.
- “Build a custom gating strategy in Python for FCS data.”
- A GatingStrategy object is created, compensation matrices and transforms are registered, and gates are applied to generate a report and exportable GatingML file.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/k-dense-ai/flowkit/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip flowkit.zip -d .agents/skills
Documentation by K-Dense Inc.
FlowKit
When to use
Use FlowKit to apply or build cytometry gating strategies, analyze batches of FCS samples, or reproduce supported FlowJo workspace analyses. It supports GatingML 2.0 and a subset of FlowJo 10 features. Import success alone does not establish agreement with FlowJo.
The examples and bundled helper target FlowKit 1.3.2 on Python 3.13. Upstream supports additional Python versions; those were not exercised here. The helper and examples were tested on synthetic FCS data, including a public FlowJo 10.7.1 synthetic workspace fixture. They are not biological validation.
Install
Use a separate environment; FlowKit 1.3.2 requires NumPy >2 and pandas <3:
uv venv --python 3.13 .venv-flowkit
uv pip install --python .venv-flowkit/bin/python "flowkit==1.3.2"
.venv-flowkit/bin/python -c "import flowkit; print(flowkit.__version__)"The scientific package is BSD-3-Clause licensed; this skill is MIT licensed.
Workflow
Identify the analysis definition. Use
Sessionfor a programmatic or GatingML strategy; useWorkspacefor FlowJo sample-specific gates, compensation, and transforms. Request the actual strategy or controls when biological thresholds have not been supplied.Inspect samples and channel identities. Match detector/PnN labels to compensation matrices and gate dimensions; PnS marker names may be empty or repeated. Verify sample IDs: the default is FCS
$FIL, which can differ from the current filename. Reject ID collisions before loading a batch.Establish the coordinate system. Determine whether the supplied events are already compensated. Apply compensation before nonlinear transforms; match gate thresholds to the same transformed or untransformed coordinates. See compensation and gating.
Check the hierarchy. Preserve parent gates and full gate paths, including
root. For a study, review acquisition/time stability, debris exclusion, singlets, viability, and phenotype gates as appropriate to its panel. Use single-stain controls for compensation and suitable negative/FMO controls for positivity; demonstration thresholds are not transferable biology.Analyze and inspect. Run on all events, then check gate overlays and sample-level QC. A plot's subsample is not the population denominator. Review warnings and compare representative imported results to FlowJo.
Export counts with denominators and provenance. Keep gate paths, sample IDs, total event counts, input hashes, package versions, and the analysis definition. Keep biological replicates identifiable; events from one specimen are not independent experimental replicates.
Apply an existing strategy
Set FLOWKIT_SKILL_DIR to this skill's installed directory. From the repository
root it is skills/flowkit. Paths below represent the user's local inputs.
FLOWKIT_SKILL_DIR="skills/flowkit"
uv run --no-project --python 3.13 --with "flowkit==1.3.2" \
python "$FLOWKIT_SKILL_DIR/scripts/analyze_gates.py" \
--gatingml gates.xml --fcs sample.fcs --output-dir results-gatingmlFor a FlowJo workspace, supply every FCS file in the selected group:
uv run --no-project --python 3.13 --with "flowkit==1.3.2" \
python "$FLOWKIT_SKILL_DIR/scripts/analyze_gates.py" \
--workspace study.wsp --group "Study" \
--fcs sample-a.fcs sample-b.fcs --output-dir results-workspaceThe helper writes gate_report.csv and provenance.json to a new directory.
Each row includes sample_event_count, parent_event_count, and a full
population_path; empty-parent percentages are blank and flagged with
relative_percent_defined=False.
It rejects duplicate sample IDs, missing/extra workspace-group samples,
zero-event samples, and strategies without gates. It uses explicit input files,
does not follow paths embedded in the workspace, and runs without
multiprocessing or transformed-event caching. It still loads each sample into
memory; use manageable batches via the Python API for large studies.
--filename-as-id deliberately switches from $FIL to file basenames. Use it
only when those names match the analysis definition. See
workspace analysis for partial-group
analysis, result interpretation, and fluorescence summaries.
Build a strategy in Python
This runnable example uses sample.fcs with FSC-A, FL1-A, and FL2-A.
The matrix, thresholds, and transform parameters are synthetic teaching
values. Replace them with the study's validated settings.
import flowkit as fk
import numpy as np
sample = fk.Sample("sample.fcs")
strategy = fk.GatingStrategy()
strategy.add_comp_matrix(
"spill", fk.Matrix(
np.array([[1.0, 0.1], [0.2, 1.0]]), ["FL1-A", "FL2-A"],
fluorochromes=["FITC", "PE"],
)
)
logicle = fk.transforms.LogicleTransform(
param_t=262144, param_w=0.5, param_m=4.5, param_a=0
)
strategy.add_transform("logicle", logicle)
strategy.add_gate(
fk.gates.RectangleGate("Cells", [
fk.Dimension("FSC-A", range_min=50, range_max=300)
]),
gate_path=("root",),
)
thresholds = logicle.apply(np.array([50.0, 600.0]))
strategy.add_gate(
fk.gates.RectangleGate("Positive", [
fk.Dimension(
"FL1-A", compensation_ref="spill", transformation_ref="logicle",
range_min=float(thresholds[0]), range_max=float(thresholds[1]),
)
]),
gate_path=("root", "Cells"),
)
session = fk.Session(gating_strategy=strategy, fcs_samples=[sample])
session.analyze_samples(use_mp=False)
report = session.get_analysis_report()
print(report[["sample_id", "gate_path", "gate_name", "count",
"absolute_percent", "relative_percent"]])
with open("gates.xml", "xb") as handle:
session.export_gml(handle)GatingML exports a template by default. When custom per-sample gates exist,
use session.export_gml(handle, sample_id=sample.id) for that sample's strategy.
A single template export does not preserve every sample-specific override.
Interpretation checks
countis the number of events passing the gate and its ancestors.absolute_percentis percent of all sample events;relative_percentis percent of the immediate parent. These are percentages, not fractions.Gate names can repeat under different parents. In the helper's output use
(sample_id, population_path)as the identifier. Paths are JSON arrays inside CSV cells. FlowKit's native report stores a quadrant's owner separately inquadrant_parent; itsgate_pathalone omits that owner.A zero-event parent makes a child percentage biologically undefined; FlowKit 1.3.2 reports zero for ordinary children and NaN for quadrants. The helper exports both as blank with an explicit false flag. This differs from a defined 0% for an empty gate whose parent contains events.
Compensated negative fluorescence is legitimate. Do not clip it to zero or discard those events merely to permit a logarithmic transform.
Define whether “MFI” means mean or median and name the event source. A transformed display value is not an intensity on the original scale.
References
Compensation and gating: event sources, detector order, transform semantics, gate paths, and plotting.
Workspaces and results: sample matching, missing FCS files, FlowJo limits, and gated fluorescence summaries.
FlowKit API and versioned source: consult signatures when moving beyond the tested release.
FlowKit publication: cite the software and version in scientific methods when used for analysis.
Files
- ATTRIBUTION.mdAdded by AutoGineer907 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md8.5 KiB
- references/compensation-and-gating.md4.8 KiB
- references/workspaces-and-results.md6.4 KiB
- scripts/analyze_gates.pyScript8.6 KiB
scripts/analyze_gates.py
#!/usr/bin/env python3
"""Apply a supplied GatingML strategy or FlowJo workspace to explicit FCS files."""
from __future__ import annotations
import argparse
import hashlib
import json
from importlib.metadata import version
from pathlib import Path
def file_record(path: Path) -> dict:
with path.open("rb") as handle:
digest = hashlib.file_digest(handle, "sha256").hexdigest()
return {"path": str(path.resolve()), "sha256": digest}
def add_denominators(report, event_counts: dict[str, int]):
"""Attach population identities and denominators, including quadrant children."""
import pandas as pd
report = report.copy()
population_paths = []
counts = {}
for row in report.itertuples(index=False):
# FlowKit reports a quadrant at its owning QuadrantGate's path, with
# that gate stored separately in quadrant_parent. Its children use
# the full strategy path, so include the owner when building the LUT.
path = tuple(row.gate_path)
if pd.notna(row.quadrant_parent):
path += (row.quadrant_parent,)
path += (row.gate_name,)
key = (row.sample_id, path)
if key in counts:
raise ValueError(f"Duplicate population in analysis report: {key!r}")
counts[key] = int(row.count)
population_paths.append(path)
parent_counts = []
for row in report.itertuples(index=False):
path = tuple(row.gate_path)
if path == ("root",):
parent_count = event_counts[row.sample_id]
else:
key = (row.sample_id, path)
if key not in counts:
raise ValueError(f"Missing parent population in analysis report: {key!r}")
parent_count = counts[key]
if not 0 <= row.count <= parent_count <= event_counts[row.sample_id]:
raise ValueError(f"Inconsistent gate/parent/sample counts for {row.gate_name!r}")
parent_counts.append(parent_count)
report["population_path"] = population_paths
report["sample_event_count"] = report["sample_id"].map(event_counts)
report["parent_event_count"] = parent_counts
report["relative_percent_defined"] = report["parent_event_count"] > 0
# FlowKit 1.3.2 returns zero for ordinary gates below an empty parent and
# NaN for quadrants. Neither is a defined percentage; CSV writes blank.
report.loc[~report["relative_percent_defined"], "relative_percent"] = float("nan")
return report
def analyze(
*,
fcs_paths: list[Path],
definition: Path,
mode: str,
output_dir: Path,
group: str | None = None,
filename_as_id: bool = False,
) -> dict:
"""Analyze all supplied samples; reject incomplete workspace groups and ID collisions."""
import flowkit as fk
if mode not in {"gatingml", "workspace"}:
raise ValueError("mode must be gatingml or workspace")
if (mode == "workspace") != (group is not None):
raise ValueError("A group is required for workspace mode and only valid there")
if output_dir.exists():
raise FileExistsError(f"Output directory already exists: {output_dir}")
if not fcs_paths:
raise ValueError("Supply at least one FCS file")
for path in [definition, *fcs_paths]:
if not path.is_file():
raise ValueError(f"Expected a local file: {path}")
# Hash the actual inputs, not paths or metadata supplied by a workspace.
definition_record = file_record(definition)
samples = []
records = []
seen_ids = set()
for path in fcs_paths:
record = file_record(path)
sample = fk.Sample(
path,
filename_as_id=filename_as_id,
use_flowjo_labels=(mode == "workspace"),
)
if sample.id in seen_ids:
raise ValueError(f"Duplicate sample ID: {sample.id!r}; check FCS $FIL values")
if sample.event_count == 0:
raise ValueError(f"Sample has no events: {sample.id!r}")
seen_ids.add(sample.id)
samples.append(sample)
record.update(sample_id=sample.id, event_count=sample.event_count)
records.append(record)
if mode == "gatingml":
strategy = fk.parse_gating_xml(str(definition))
if not strategy.get_gate_ids():
raise ValueError("GatingML strategy has no gates")
analysis = fk.Session(strategy, fcs_samples=samples)
analysis.analyze_samples(use_mp=False, cache_events=False)
report = analysis.get_analysis_report()
else:
analysis = fk.Workspace(
str(definition),
fcs_samples=samples,
# Retain missing-sample gate metadata so completeness can be checked.
load_missing_file_data=True,
find_fcs_files_from_wsp=False,
)
expected = set(analysis.get_sample_ids(group_name=group, loaded_only=False))
if not expected:
raise ValueError(f"Workspace group is empty: {group!r}")
if expected != seen_ids:
raise ValueError(
"FCS inputs must exactly match the selected workspace group; "
f"missing IDs={sorted(expected - seen_ids)}, "
f"unexpected IDs={sorted(seen_ids - expected)}"
)
for sample_id in sorted(expected):
if not analysis.get_gate_ids(sample_id):
raise ValueError(f"Workspace sample has no gates: {sample_id!r}")
analysis.analyze_samples(group_name=group, use_mp=False, cache_events=False)
report = analysis.get_analysis_report(group_name=group)
if report.empty or set(report["sample_id"]) != seen_ids:
raise ValueError("Analysis did not produce gate results for every input sample")
report = add_denominators(report, {sample.id: sample.event_count for sample in samples})
# Preserve path components without inventing an additional separator.
for column in ("gate_path", "population_path"):
report[column] = report[column].map(lambda path: json.dumps(list(path)))
manifest = {
"mode": mode,
"group": group,
"filename_as_id": filename_as_id,
"definition": definition_record,
"samples": records,
"versions": {
package: version(package)
for package in ("flowkit", "flowio", "flowutils", "numpy", "pandas")
},
"report": "gate_report.csv",
"percentages": {
"absolute_percent": "100 * gate count / total sample events",
"relative_percent": "100 * gate count / parent population count",
"empty_parent": "relative_percent is blank; relative_percent_defined is false",
},
"population_identity": "sample_id plus population_path (JSON array including gate name and quadrant owner)",
"event_processing": "FlowKit FCS preprocessing; compensation and transforms from gate definitions",
"use_mp": False,
}
output_dir.mkdir(parents=True, exist_ok=False)
report.to_csv(output_dir / "gate_report.csv", index=False)
(output_dir / "provenance.json").write_text(
json.dumps(manifest, indent=2, allow_nan=False) + "\n", encoding="utf-8"
)
return manifest
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__)
definitions = parser.add_mutually_exclusive_group(required=True)
definitions.add_argument("--gatingml", type=Path, help="GatingML 2.0 XML strategy")
definitions.add_argument("--workspace", type=Path, help="FlowJo 10 .wsp file")
parser.add_argument("--fcs", nargs="+", type=Path, required=True, help="Explicit local FCS files")
parser.add_argument("--group", help="Workspace group; all its FCS files must be supplied")
parser.add_argument("--output-dir", type=Path, required=True, help="New results directory")
parser.add_argument(
"--filename-as-id", action="store_true",
help="Use current file basenames instead of FCS $FIL metadata for sample IDs",
)
args = parser.parse_args(argv)
if bool(args.workspace) != (args.group is not None):
parser.error("--workspace requires --group; --group is not valid with --gatingml")
try:
manifest = analyze(
fcs_paths=args.fcs,
definition=args.workspace or args.gatingml,
mode="workspace" if args.workspace else "gatingml",
output_dir=args.output_dir,
group=args.group,
filename_as_id=args.filename_as_id,
)
except (ValueError, KeyError, OSError) as exc:
parser.exit(2, f"Analysis failed: {exc}\n")
print(f"Analyzed {len(manifest['samples'])} samples: {args.output_dir / 'gate_report.csv'}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs Python
The author says: Requires Python 3.13 with flowkit==1.3.2 for the tested environment. Dependencies include FlowIO, FlowUtils, NumPy, pandas, SciPy, lxml, and Bokeh. Installation needs network access; analysis uses local FCS/XML/WSP files without credentials. FlowUtils needs a C compiler if a compatible wheel is unavailable.
Security
What our scanner noticed· Low risk
- Instructions that use the network
- Scripts that your agent may run
This skill includes scripts that your agent may run. Read them before you use it.
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- K-Dense Inc.
- Publisher
- K-Dense (Community)
- Source
- K-Dense-AI/scientific-agent-skills · skills/flowkit · commit a25cf9e
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE.md)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by K-Dense Inc. or K-Dense.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download flowkit.zip