# Steady-state analysis of flow cytometry data

#### R Clay Wright

#### 2025-04-15

This vignette will guide you through analysis of an example flow cytometry data set from an experiment examining the fluorescent reporter levels of a synthetic biological circuit in liquid cultures of budding yeast. Here, we analyze a circuit in which a florescent reporter is fused to a protein that is degraded over time after addition of an inducer molecule. At some time post-induction (as optimized by the experimenter) fluorescence of these cultures is analyzed by flow cytometry. Here we demonstrate how to import the resulting .fcs files into R, annotate this data with experimental metadata (e.g. the `strain` and `treatment` for each sample), and compile the relevant events and measurements.

#Importing and annotating data Import your flow cytometry data using `read.flowset`. Here, we will import an example flowSet.

    plate1 <- read.flowSet(path = system.file("extdata", "ss_example", package = "flowTime"),
        alter.names = TRUE)
    # add plate numbers to the sampleNames
    sampleNames(plate1) <- paste("1_", sampleNames(plate1), sep = "")
    dat <- plate1

If you have several plates this code can be repeated and each plate can be combined to assemble the full data set.

    plate2 <- read.flowSet(path = paste(experiment, "_2/", sep = ""), alter.names = TRUE)
    sampleNames(plate2) <- paste("2_", sampleNames(plate2), sep = "")
    dat <- rbind2(plate1, plate2)

For this example, we will import the table of metadata. The `sampleNames` of the assembled `flowSet` (`dat` in this example) must match that of a unique identifier column of `annotation`.

    annotation <- read.csv(system.file("extdata", "ss_example.csv", package = "flowTime"))
    head(annotation)
    #>           X      name  AFB   IAA treatment repl
    #> 1 1_A01.fcs 1_A01.fcs TIR1  IAA1      0.00    1
    #> 2 1_A02.fcs 1_A02.fcs TIR1 IAA17      0.00    1
    #> 3 1_A03.fcs 1_A03.fcs AFB2  IAA1      0.00    1
    #> 4 1_A04.fcs 1_A04.fcs AFB2 IAA17      0.00    1
    #> 5 1_B01.fcs 1_B01.fcs TIR1  IAA1      0.05    1
    #> 6 1_B02.fcs 1_B02.fcs TIR1 IAA17      0.05    1
    sampleNames(dat)
    #>  [1] "1_A01.fcs" "1_A02.fcs" "1_A03.fcs" "1_A04.fcs" "1_B01.fcs" "1_B02.fcs"
    #>  [7] "1_B03.fcs" "1_B04.fcs" "1_C01.fcs" "1_C02.fcs" "1_C03.fcs" "1_C04.fcs"
    #> [13] "1_D01.fcs" "1_D02.fcs" "1_D03.fcs" "1_D04.fcs" "1_E01.fcs" "1_E02.fcs"
    #> [19] "1_E03.fcs" "1_E04.fcs" "1_F01.fcs" "1_F02.fcs" "1_F03.fcs" "1_F04.fcs"
    #> [25] "1_G01.fcs" "1_G02.fcs" "1_G03.fcs" "1_G04.fcs" "1_H01.fcs" "1_H02.fcs"
    #> [31] "1_H03.fcs" "1_H04.fcs"
    sampleNames(dat) == annotation$name
    #>  [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
    #> [16] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
    #> [31] TRUE TRUE

We can also create this column from our data set and attach the annotation columns. Alternatively one can use the `createAnnotation` function to create a data frame with the appropriate `name` column that can then be filled in via R-code or saved as a csv file and filled via a spreadsheet editor. The order of the entries in `annotation` does not matter, so long as each entry in `sampleNames(dat)` is represented. The `annotateFlowSet` function will match entries by the `mergeBy` column

    annotation <- cbind(annotation, name = sampleNames(dat))
    # or
    annotation <- createAnnotation(yourFlowSet = dat)
    write.csv(annotation, file = "path/to/yourAnnotation.csv")

Finally we can attach this metadata to the flowSet using the `annotateFlowSet` function.

    adat <- annotateFlowSet(yourFlowSet = dat, annotation_df = annotation, mergeBy = "name")
    head(rownames(pData(adat)))
    #> [1] "1_A01.fcs" "1_A02.fcs" "1_A03.fcs" "1_A04.fcs" "1_B01.fcs" "1_B02.fcs"
    head(pData(adat))
    #>                name         X  AFB   IAA treatment repl
    #> 1_A01.fcs 1_A01.fcs 1_A01.fcs TIR1  IAA1      0.00    1
    #> 1_A02.fcs 1_A02.fcs 1_A02.fcs TIR1 IAA17      0.00    1
    #> 1_A03.fcs 1_A03.fcs 1_A03.fcs AFB2  IAA1      0.00    1
    #> 1_A04.fcs 1_A04.fcs 1_A04.fcs AFB2 IAA17      0.00    1
    #> 1_B01.fcs 1_B01.fcs 1_B01.fcs TIR1  IAA1      0.05    1
    #> 1_B02.fcs 1_B02.fcs 1_B02.fcs TIR1 IAA17      0.05    1

Now we can save this flowSet and anyone in perpetuity can load and analyze this annotated flowSet with ease!

    write.flowSet(adat, outdir = "your/favorite/directory")

    # Read the flowSet with the saved experimental meta data
    read.flowSet("flowSet folder", path = "your/flow/directory", phenoData = "annotation.txt",
        alter.names = TRUE)

#Compiling and plotting data Now we are ready to analyze the raw data in this `flowSet`. First we load the set of gates that will be used to subset our data. To analyze this steady-state or single time point experiment we will use the `steadyState` function. This function will gate each `flowFrame` in the `flowSet` and compile and return a `dataframe` of the relevant data and metadata for each event. This `dataframe` can then be used to visualize the full data set.

    loadGates()  # use the default included gateSet
    dat.SS <- steadyState(flowset = adat, ploidy = "diploid", only = "singlets")
    #> [1] "Gating with diploid singlet gates..."
    #> [1] "Converting events..."

    p <- ggplot(dat.SS, aes(x = as.factor(treatment), y = FL2.A, fill = AFB)) + geom_boxplot(outlier.shape = NA) +
        facet_grid(IAA ~ AFB) + theme_classic(base_family = "Arial", base_size = 16) +
        ylim(c(-1000, 10000)) + xlab(expression(paste("Auxin (", mu, "M)", sep = ""))) +
        ylab("Fluorescence (AU)") + theme(legend.position = "none")
    p
    #> Warning: Removed 85 rows containing non-finite outside the scale range
    #> (`stat_boxplot()`).
    #> Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)): font family
    #> not found in Windows font database
    #> Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)): font family
    #> not found in Windows font database
    #> Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
    #> family not found in Windows font database
    #> Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
    #> family not found in Windows font database
    #> Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
    #> family not found in Windows font database
    #> Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
    #> family not found in Windows font database
    #> Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
    #> font family not found in Windows font database
    #> Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
    #> font family not found in Windows font database
    #> Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
    #> font family not found in Windows font database
    #> Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
    #> font family not found in Windows font database
    #> Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
    #> font family not found in Windows font database
    #> Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
    #> font family not found in Windows font database
    #> Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
    #> font family not found in Windows font database
    #> Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
    #> font family not found in Windows font database
    #> Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
    #> font family not found in Windows font database
    #> Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
    #> font family not found in Windows font database
    #> Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
    #> font family not found in Windows font database