Our analysis starts with data import. While some flow cytometry acquisition software export data from different samples as separate .fcs files, others export a single, merged .fcs file containing data from multiple samples. The $NXTDATA keyword can be used to retrieve data from individual samples.
In flowFate, users can upload a merged file or individual files. If individual files are uploaded, they must be grouped inside a folder.
Note: Imported files must be FCS3.0.
A - Merged file upload
1) Fetch the number of datasets
We first fetch the number of datasets contained in the merged .fcs file (code adapted from RGLab).
n_datasets <- function(filename) {
# Adapted code from https://github.com/RGLab/flowCore/blob/ba3b6ffed5310c1c0618487ab163c0142d8cab8f/R/IO.R
# the keyword $NEXTDATA contains either a zero when there are no next data
# or a positive integer for the next dataset
counter <- 0
nextdata <- 1
while (nextdata != 0) {
counter <- counter + 1
nextdata <- read.FCSheader(filename, keyword = "$NEXTDATA", emptyValue = FALSE, dataset = counter)
nextdata <- as.integer(nextdata[[1]]["$NEXTDATA"])
}
counter
}
2) Write individual .fcs files
We write the individual datasets inside the merged .fcs file as
individual .fcs files to a temporary folder using the custom
split_1_fcs( )
function. We append the $WELLID keyword to
the filename.
split_1_fcs <- function(nb, input_file) {
# create a folder named fcs_input in a temporary location
if (!dir_exists(path(path_dir(input_file), "fcs_input"))) dir_create(path(path_dir(input_file), "fcs_input"))
# walk along every dataset and read it as a flowFrame (data structure from flowCore)
walk(seq_len(nb_ds), \(x) {
fr <- read.FCS(filename, dataset = x,
transformation = FALSE,
truncate_max_range = FALSE,
alter.names = TRUE,
emptyValue = FALSE)
message(paste("Write file #", x, "well", fr@description$`$WELLID`))
# write flowframes to individual FCS files inside the fcs_input folder
write.FCS(fr, fs::path("fcs_input", paste0("dataset_", fr@description$`$WELLID`, ".fcs")))
})
# return the complete path of the fcs_input folder
path(path_dir(input_file), "fcs_input")
}
# create a reactive expression that, when called, executes the split_1_fcs() function and returns a directory
individual_fcs <- reactive(split_1_fcs(nb_ds(), input$filename$datapath))
3) Create a flowSet
We read the individual .fcs files (stored in the fcs_input temporary
folder) into a flowSet, a data structure from the flowCore package). The
reactive expression individual_fcs()
returns the directory
of fcs_input temporary folder.
fs <- reactive(read.flowSet(fs::dir_ls(individual_fcs(), glob = "*.fcs"),
truncate_max_range = FALSE,
alter.names = TRUE,
transformation = FALSE))
B - Individual file upload
Note: Individual files (all FCS3.0) have to be grouped inside a folder. Steps 1) and 2) from above are now dispensable and we can start by creating a flowSet.
C - Create a GatingSet
The flowFrame and flowSet data structures from flowCore are very useful for handling flow cytometry data in R. The GatingSet data structure from flowWorkspace is, however, better suited for storing and manipulating gated data.
1) Create a GatingSet from a flowSet
We use the GatingSet()
function from flowWorkspace
to create a GatingSet from the previous flowSet.
gs <- reactive({GatingSet(fs())})
D - Sharing reactive values across modules
The {golem}
framework promotes the use of modules to split a Shiny app into
separate, smaller parts. Different techniques
are available in {golem} to allow communication between modules. We
opted for the “stratégie
du petit r”. Briefly, a list of reactive values - called
r
- is passed along modules.
1) Completing the reactive list r
We add the GatingSet, flowSet and nb_ds (number of datasets) reactive
expression to the reactive list. These variables can now be used in
other modules by calling r$gs
, r$fs
or
r$nb_ds
. To read reactive expressions in Shiny, they have
to be wrapped in inside observe( )
.
observe({
r$gs <- GatingSet(fs())
r$fs <- fs()
r$nb_ds <- nb_ds()
})