library(CyFj11)
library(flowWorkspace)
#> As part of improvements to flowWorkspace, some behavior of
#> GatingSet objects has changed. For details, please read the section
#> titled "The cytoframe and cytoset classes" in the package vignette:
#>
#> vignette("flowWorkspace-Introduction", "flowWorkspace")
library(flowCore)
library(CytoML)
library(ggcyto)
#> Loading required package: ggplot2
#> Loading required package: ncdfFlow
#> Loading required package: BHCyFj11 is an R package designed to bridge the gap between FlowJo v11 workspaces and the R flowWorkspace ecosystem. It provides asymmetric format conversion capabilities:
This asymmetric design reflects the technical realities of developing against proprietary formats without vendor documentation. While FlowJo v11 uses a complex JSON schema with extensive UUID cross-referencing that makes direct export impractical, the v10 XML format follows the documented Gating-ML 2.0 standard, making it feasible to generate valid workspaces.
Several Bioconductor packages can import FlowJo workspaces. Here’s how CyFj11 compares:
| Package | FlowJo Version | Input Format | Output | Key Features |
|---|---|---|---|---|
| CyFj11 | v11 | .flowjo (ZIP) |
GatingSet |
v11 native, comp |
| CytoML | v10 | .wsp (XML) |
GatingSet |
Gating-ML 2.0 standard |
| flowWorkspace | v10 | .wsp (XML) |
GatingSet |
Basic v10 import |
| flowWorkspaceData | v10 | .wsp (XML) |
GatingSet |
Example datasets |
Key advantages of CyFj11:
.flowjo format (ZIP archive with JSON)GatingSet objects back to FlowJo v10 .wsp
format for round-trip workflowsWhen to use CyFj11: - You have FlowJo v11 workspace
files (.flowjo extension) - You want to import workspaces
without manually exporting to v10 format in FlowJo - You need to
preserve v11-specific compensation and transformation information
When to use CytoML: - You have FlowJo v10 workspace
files (.wsp XML format) - You need Gating-ML 2.0 standard
compliance - You’re working with legacy v10 workflows
To install CyFj11, ensure you have the required dependencies:
The typical workflow with CyFj11 involves three steps:
Let’s walk through this process with a simple example.
First, we’ll read a FlowJo v11 workspace file:
# Set the path to your FlowJo workspace file
workspace_path <- system.file("extdata", "test.data.flowjo", package = "CyFj11")
# Read the FlowJo v11 workspace
ws <- read_flowjo11_workspace(workspace_path)
# Examine the workspace structure
print(names(ws))
#> [1] "path" "manifest" "json"
#> [4] "groups" "dataSources" "populationDefinitions"
#> [7] "populations" "schemaVersion" "analysisUUID"
#> [10] "uri" "reports" "compoundParameterSets"
#> [13] "compoundPopulations" "paramsetDefinitions" "platforms"
#> [16] "cytometers" "analysisRoot" "timestamp"The read_flowjo11_workspace() function parses the
ZIP-based FlowJo v11 format and extracts the JSON data structures. The
returned object contains all the information from the workspace,
including groups, samples, gates, and populations.
Next, we convert the parsed workspace to a GatingSet object, which is the standard format for flow cytometry data in R:
workspace_fcs <- system.file("extdata", "", package = "CyFj11")
# fj11_to_gatingset() returns a named list of GatingSet objects, one per sample
gsList <- fj11_to_gatingset(
fj11_workspace = ws,
group_name = 1,
path = workspace_fcs
)
#> ===========================================
#> Resolving FCS File Paths
#> ===========================================
#>
#> Resolving1sample paths...
#> OK sample04.fcs
#>
#> ===========================================
#> Resolution Summary
#> ===========================================
#> Total samples: 1
#> Found: 1 (100.0%)
#> Missing: 0 (0.0%)
#> Multiple: 0 (0.0%)
#> ===========================================
#> Created mapping for1samples
#> Warning in comp_platform_or_legacy(sample_uuid, custom_comp, sample_comp_map, :
#> No compensation found for sample: 9d31a6f4-342d-4b66-a508-1f7d470e6bd9
#> Warning in pop_warn_failed_add(e, node_name, parent, gate_obj_adjusted, : Failed to add population 'ch8High': Expected string vector of length 1
#> Parent: cells/Singlets/Single_cells/cd8Pos/Q4:PE-A :: CD4-,PerCP-Cy5-5-A :: CD14- : cells/Singlets/Single_cells/cd8Pos/Q3:PE-A :: CD4+,PerCP-Cy5-5-A :: CD14- : cells/Singlets/Single_cells/cd8Pos/Q1:PE-A :: CD4-,PerCP-Cy5-5-A :: CD14+ : cells/Singlets/Single_cells/cd8Pos/Q2:PE-A :: CD4+,PerCP-Cy5-5-A :: CD14+
#> Gate type: rectangleGate
#> Warning in pop_warn_failed_add(e, node_name, parent, gate_obj_adjusted, : Failed to add population 'cd8HighLow': Expected string vector of length 1
#> Parent: cells/Singlets/Single_cells/cd8Pos/Q4:PE-A :: CD4-,PerCP-Cy5-5-A :: CD14- : cells/Singlets/Single_cells/cd8Pos/Q3:PE-A :: CD4+,PerCP-Cy5-5-A :: CD14- : cells/Singlets/Single_cells/cd8Pos/Q1:PE-A :: CD4-,PerCP-Cy5-5-A :: CD14+ : cells/Singlets/Single_cells/cd8Pos/Q2:PE-A :: CD4+,PerCP-Cy5-5-A :: CD14+
#> Gate type: rectangleGate
#> Warning in pop_warn_unresolved(deferred, gh): Could not resolve all component paths for logical gate: Not gate of ch8High
#> Missing components: ch8High
#> Warning in pop_warn_unresolved(deferred, gh): Could not resolve all component paths for logical gate: And gate of ch8High, cd8HighLow
#> Missing components: ch8High, cd8HighLow
#> Warning in pop_warn_unresolved(deferred, gh): Could not resolve all component paths for logical gate: Or gate of ch8High, cd8HighLow
#> Missing components: cd8HighLow, ch8High
#> Warning in pop_warn_unresolved(deferred, gh): Could not resolve all component paths for logical gate: Not gate of cd8HighLow
#> Missing components: cd8HighLow
#> done!
pretty_print_flowjo(workspace_path)
#> Pretty printed JSON saved to:test.data_pretty.json
#> [1] "test.data_pretty.json"
# Check the number of samples
length(gsList)
#> [1] 1
# View sample names for the first sample
flowCore::sampleNames(gsList[[1]])
#> [1] "sample04.fcs"
# Work with a single sample directly
gs <- gsList[[1]]
# Or merge all samples into one GatingSet
# (may overwrite per-sample transform settings)
# gs_merged <- flowWorkspace::merge_list_to_gs(gsList)Note: If you don’t know the group name you can leave it empty and you will be prompted to choose:
1: Acquired Data
2: Compensation Data
3: Experiment Data
Note: fj11_to_gatingset() returns a
list of GatingSet objects, one per sample.
This preserves per-sample gate variations (tailored gates) from FlowJo
v11. To work with all samples in a single GatingSet, use
flowWorkspace::merge_list_to_gs(gsList), but be aware this
may discard sample-specific transformation settings.
Note that you’ll need to provide the path to your FCS files. The
fj11_to_gatingset() function will attempt to locate the FCS
files referenced in the workspace.
Once we have a GatingSet, we can examine its structure:
# View the population hierarchy
gs_get_pop_paths(gs)
#> [1] "root"
#> [2] "/Lymphocytes-test"
#> [3] "/cells"
#> [4] "/cells/Singlets"
#> [5] "/cells/Singlets/Single_cells"
#> [6] "/cells/Singlets/Single_cells/cd8Pos"
#> [7] "/cells/Singlets/Single_cells/cd8Pos/Q1:PE-A :: CD4-,PerCP-Cy5-5-A :: CD14+"
#> [8] "/cells/Singlets/Single_cells/cd8Pos/Q2:PE-A :: CD4+,PerCP-Cy5-5-A :: CD14+"
#> [9] "/cells/Singlets/Single_cells/cd8Pos/Q3:PE-A :: CD4+,PerCP-Cy5-5-A :: CD14-"
#> [10] "/cells/Singlets/Single_cells/cd8Pos/Q4:PE-A :: CD4-,PerCP-Cy5-5-A :: CD14-"
# Get population statistics
stats <- gs_pop_get_stats(gs)
head(stats)
#> sample pop count
#> <char> <char> <num>
#> 1: sample04.fcs root 8000
#> 2: sample04.fcs /Lymphocytes-test 4816
#> 3: sample04.fcs /cells 7417
#> 4: sample04.fcs /cells/Singlets 7344
#> 5: sample04.fcs /cells/Singlets/Single_cells 7296
#> 6: sample04.fcs /cells/Singlets/Single_cells/cd8Pos 0
plot(gs)
gate <- gh_pop_get_gate(gs, "cd8Pos")
gh = gs[[1]][[1]]
gh_get_transformations(gh,channel = "APC-A")
#> function (x, deriv = 0)
#> {
#> deriv <- as.integer(deriv)
#> if (deriv < 0 || deriv > 3)
#> stop("'deriv' must be between 0 and 3")
#> if (deriv > 0) {
#> z0 <- double(z$n)
#> z[c("y", "b", "c")] <- switch(deriv, list(y = z$b, b = 2 *
#> z$c, c = 3 * z$d), list(y = 2 * z$c, b = 6 * z$d,
#> c = z0), list(y = 6 * z$d, b = z0, c = z0))
#> z[["d"]] <- z0
#> }
#> res <- stats:::.splinefun(x, z)
#> if (deriv > 0 && z$method == 2 && any(ind <- x <= z$x[1L]))
#> res[ind] <- ifelse(deriv == 1, z$y[1L], 0)
#> res
#> }
#> <bytecode: 0x60fbbbf2e228>
#> <environment: 0x60fbe54c0228>
#> attr(,"type")
#> [1] "biexp"
#> attr(,"parameters")
#> attr(,"parameters")$channelRange
#> [1] 256
#>
#> attr(,"parameters")$maxValue
#> [1] 262144
#>
#> attr(,"parameters")$neg
#> [1] 0
#>
#> attr(,"parameters")$pos
#> [1] 4
#>
#> attr(,"parameters")$widthBasis
#> [1] -10After performing analysis in R, you can export your results back to FlowJo v10 format:
# Export to FlowJo v10 format
export_path <- tempfile(fileext = ".wsp")
export_flowjo10_workspace(gs[[1]], export_path)
#> Successfully exported FlowJo v10 workspace to: /tmp/RtmpL7KFxg/file1c6c4461176b.wsp
#> [1] TRUE
# Verify the export was successful
file.exists(export_path)
#> [1] TRUENote:
warnings about invalid transformations are normal, as we don’t have any transformations set in FlowJo 11 and the gatingset.
The exported workspace can be opened in FlowJo v11, which maintains backward compatibility with v10 workspaces.
For more detailed information about compensation handling, see
vignette("compensation-workflow").
For exporting GatingSet objects back to FlowJo v10 format, see
?export_flowjo10_workspace.
For function-specific documentation, use the help system:
library(CytoML)
ws <- CytoML::open_flowjo_xml(export_path)
gs_back <- CytoML::flowjo_to_gatingset(ws, name = "All Samples",
path = workspace_fcs)
# Get population statistics
stats <- gs_pop_get_stats(gs_back)
head(stats)
plot(gs_back)
ggcyto::autoplot(gs_back, "cells", bins = 80)
autoplot(gs_back, "cd8Pos", bins = 80)sessionInfo()
#> R version 4.6.1 (2026-06-24)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 26.04.1 LTS
#>
#> Matrix products: default
#> BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
#> LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.32.so; LAPACK version 3.12.0
#>
#> locale:
#> [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
#> [3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
#> [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
#> [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
#> [9] LC_ADDRESS=C LC_TELEPHONE=C
#> [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
#>
#> time zone: Etc/UTC
#> tzcode source: system (glibc)
#>
#> attached base packages:
#> [1] stats graphics grDevices utils datasets methods base
#>
#> other attached packages:
#> [1] ggcyto_1.41.1 ncdfFlow_2.59.1 BH_1.90.0-1
#> [4] ggplot2_4.0.3 CytoML_2.25.0 flowCore_2.25.1
#> [7] flowWorkspace_4.25.1 CyFj11_0.99.113 BiocStyle_2.41.0
#>
#> loaded via a namespace (and not attached):
#> [1] gtable_0.3.6 xfun_0.61 bslib_0.12.0
#> [4] Biobase_2.73.2 lattice_0.23-1 tzdb_0.5.0
#> [7] vctrs_0.7.3 tools_4.6.1 generics_0.1.4
#> [10] stats4_4.6.1 tibble_3.3.1 pkgconfig_2.0.3
#> [13] Matrix_1.7-6 data.table_1.18.6.1 RColorBrewer_1.1-3
#> [16] S7_0.2.2 S4Vectors_0.51.10 graph_1.91.0
#> [19] lifecycle_1.0.5 compiler_4.6.1 farver_2.1.2
#> [22] htmltools_0.5.9 sys_3.4.3 buildtools_1.0.0
#> [25] sass_0.4.10 yaml_2.3.12 pillar_1.11.1
#> [28] hexbin_1.28.6 jquerylib_0.1.4 tidyr_1.3.2
#> [31] DelayedArray_0.39.8 cachem_1.1.0 abind_1.4-8
#> [34] RProtoBufLib_2.25.0 tidyselect_1.2.1 digest_0.6.39
#> [37] dplyr_1.2.1 purrr_1.2.2 labeling_0.4.3
#> [40] maketools_1.3.2 fastmap_1.2.0 grid_4.6.1
#> [43] SparseArray_1.13.4 cli_3.6.6 magrittr_2.0.5
#> [46] S4Arrays_1.13.2 RBGL_1.89.0 XML_3.99-0.25
#> [49] readr_2.2.0 withr_3.0.3 scales_1.4.0
#> [52] XVector_0.53.0 rmarkdown_2.32 matrixStats_1.5.0
#> [55] otel_0.2.0 gridExtra_2.3.1 cytolib_2.25.0
#> [58] hms_1.1.4 evaluate_1.0.5 knitr_1.52
#> [61] IRanges_2.47.5 rlang_1.3.0 Rcpp_1.1.2
#> [64] glue_1.8.1 Rgraphviz_2.57.2 BiocManager_1.30.27
#> [67] xml2_1.6.0 BiocGenerics_0.59.12 jsonlite_2.0.0
#> [70] R6_2.6.1 plyr_1.8.9 MatrixGenerics_1.25.0