An Introduction to the openCyto package

if (!require(flowWorkspaceData)) {
  stop("Cannot build the vignettes without 'flowWorkspaceData'")
}
library(knitr)
opts_chunk$set(out.extra='style="display:block; margin: auto"', fig.align="center", message = FALSE, warning = FALSE)

1. Introduction

The openCyto package is designed to facilitate the application of automated gating methods in a sequential way to mimic the construction of a manual gating scheme.

1.1. Manual gating

Traditionally, scientists have to draw the gates for each individual sample on each 2-D projection (2 channels) within flowJo. Alternatively, they can draw template gates on one sample and replicate them to other samples, then manually inspect the gate on each sample to do the correction if necessary. Either way is time consuming and subjective, thus not suitable for the large data sets generated by high-throughput flow cytometry, CyTOF, or "cross-lab" data analysis.

Here is one xml workspace (manual gating scheme) exported from flowJo.

library(flowWorkspace)
flowDataPath <- system.file("extdata", package = "flowWorkspaceData")
wsfile <- list.files(flowDataPath, pattern="manual.xml",full = TRUE)
wsfile

By using the CytoML package, We can load it into R,

library(CytoML)
ws <- open_flowjo_xml(wsfile)

apply themanual gatesdefined inxmlto the rawFSCfiles,

gs <- flowjo_to_gatingset(ws, name= "T-cell", subset =1)
gs <- load_gs(file.path(flowDataPath,"gs_manual"))

and then visualize theGating Hierarchy

gh <- gs[[1]]
plot(gh)

and thegates:

library(ggcyto)
autoplot(gh)

This is a gating scheme for a T cell panel, which tries to identify T cell sub-populations. We can achieve the same results by using the automated gating pipeline provided by this package.

1.2. Automated Gating

flowCore,flowStats,flowClust and other packages provide many different gating methods to detect cell populations and draw gates automatically.

The flowWorkspace package provides the GatingSet as an efficient data structure to store, query and visualize the hierarchical gated data.

By taking advantage of these tools, the openCyto package can create the automated gating pipeline by a gatingTemplate, which is essentially the same kind of hierarchical gating scheme used by scientists.

2. Create gating templates

2.1. Template format

First of all, we need to describe the gating hierarchy in a spread sheet (a plain text format). This spread sheet must have the following columns:

2.2. Example template

Here is an example of a gating template.

library(openCyto)
library(data.table)
gtFile <- system.file("extdata/gating_template/tcell.csv", package = "openCyto")
dtTemplate <- fread(gtFile)
dtTemplate

Each row is usually corresponding to one cell population and the gating method that is used to get that population. We will try to explain how to create this gating template based on the manual gating scheme row by row.

2.2.1. "nonDebris"

dtTemplate[1,]

2.2.2. "singlets"

dtTemplate[2,]

2.2.3. "lymphocytes"

dtTemplate[3,]

2.2.4. "cd3+" (Tcells)

dtTemplate[4,]

This is similar to the nonDebris gate except that we specify collapseDataForGating as TRUE, which tells the pipeline to collapse all samples into one and apply mindensity to the collapsed data on CD3 dimension. Once the gate is generated, it is replicated across all samples. This is only useful when each individual sample does not have enough events to deduce the gate. Here we do this just for the purpose of proof of concept.

2.2.5. CD4 and CD8

The fifth row specifies pop as cd4+/-cd8+/-, which will be expanded into 6 rows.

dtTemplate[5,]
expanded <- openCyto:::.preprocess_csv(dtTemplate)
rownames(expanded) <- NULL

The first two rows are two 1-D gates that will be generated by gating_method on each dimension (cd4 and cd8) independently:

expanded[5:6,]

Then another 4 rows are 4 rectangleGates that corresponds to the 4 quadrants in the 2-D projection (cd4 vs cd8).

expanded[7:10,]

As we see here, "refGate" in gating_method indicates that they are constructed based on the gate coordinates of the previous two 1-D gates. Those 1-D gates are thus considered as "reference gates" that are referred to by a colon-separated alias string in gating_args: "cd4+:cd8+".

Alternatively, we can expand it into these 6 rows explicitly in the spreadsheet. But this convenient representation is recommended unless the user wants to have finer control on how the gating is done. For instance, sometimes we need to use different gating_methods to generate 1-D gates on cd4 and cd8. Or it could be the case that cd8 gating needs to depend on cd4 gating, i.e. the parent of cd8+ is cd4+(or cd4-) instead of cd3. Sometimes we want to have a customized alias other than the quadrant-like name (x+y+) that gets generated automatically. (e.g. 5th row of the gating template)

3. Load gating template

After the gating template is defined in the spreadsheet, it can be loaded into R:

gt_tcell <- gatingTemplate(gtFile)
gt_tcell

Besides looking at the spreadsheet, we can examine the gating scheme by visualizing it:

plot(gt_tcell)

As we can see, the gating scheme has been expanded as we described above. All the colored arrows source from a parent population and the grey arrows source from a reference population(/gate).

4. Run the gating pipeline

Once we are satisfied with the gating template, we can apply it to the actual flow data.

4.1. Load the raw data

First of all, we load the raw FCS files into R by ncdfFlow::read.ncdfFlowSet (it uses less memory than flowCore::read.flowSet) and create an empty GatingSet object.

fcsFiles <- list.files(pattern = "CytoTrol", flowDataPath, full = TRUE)
cs  <- load_cytoset_from_fcs(fcsFiles)
cf <- realize_view(cs[[1]])
gs <- GatingSet(cs)
gs

4.2. Compensation

Then, we compensate the data. If we have compensation controls (i.e. singly stained samples), we can calculate the compensation matrix by using the flowStats::spillover function. Here we simply use the compensation matrix defined in the flowJo workspace.

compMat <- gh_get_compensations(gh)
compensate(gs, compMat)

Here is one example showing the compensation outcome:

sub_chnl <- c("V545-A","V450-A")
cf <- cf[,sub_chnl]
cf_comp <- realize_view(gh_pop_get_data(gs[[1]])[,sub_chnl])
cs <- cytoset(list(cf = cf, cf_comp = cf_comp))
#transform data to better visualize the compensation effect
transform(cs, estimateLogicle(cf,sub_chnl))
gridExtra::grid.arrange(as.ggplot(autoplot(cs[[1]], "V545", "V450")), as.ggplot(autoplot(cs[[2]], "V545", "V450")), nrow = 1)
# Need to grab the post-compensation, pre-transformation cytoframe before next steps
cf_comp <- realize_view(gh_pop_get_data(gs[[1]])[,sub_chnl])

4.3. Transformation

All of the stained channels need to be transformed properly before the gating. Here we use the flowCore::estimateLogicle method to determine the logicle transformation.

chnls <- parameters(compMat)
trans <- estimateLogicle(gs[[1]], channels = chnls)
gs <- transform(gs, trans)

Here is one example showing the transformation outcome:

cf_trans <- gh_pop_get_data(gs[[1]])[,sub_chnl[1]]
cf_comp <- cf_comp[,sub_chnl[1]]
p1 <- as.ggplot(autoplot(cf_comp, "V545"))
p2 <- as.ggplot(autoplot(cf_trans, "V545"))
plot(gridExtra::arrangeGrob(p1,p2))

4.5. Gating

Now we can apply the gating template to the data:

gt_gating(gt_tcell, gs)

Optionally, we can run the pipeline in parallel to speed up gating. e.g.

gt_gating(gt_tcell, gs, mc.cores=2, parallel_type = "multicore")

4.6. Hide nodes

After gating, there are some extra populations generated automatically by the pipeline (e.g. refGate).

plot(gs[[1]])

We can hide these populations if we are not interested in them:

nodesToHide <- c("cd8+", "cd4+"
                , "cd4-cd8-", "cd4+cd8+"
                , "cd4+cd8-/HLA+", "cd4+cd8-/CD38+"
                , "cd4-cd8+/HLA+", "cd4-cd8+/CD38+"
                , "CD45_neg/CCR7_gate", "cd4+cd8-/CD45_neg"
                , "cd4-cd8+/CCR7+", "cd4-cd8+/CD45RA+"
                )
lapply(nodesToHide, function(thisNode) gs_pop_set_visibility(gs, thisNode, FALSE))

4.7. Rename nodes

And rename the populations:

gs_pop_set_name(gs, "cd4+cd8-", "cd4")
gs_pop_set_name(gs, "cd4-cd8+", "cd8")
plot(gs[[1]])

4.8. Visualize the gates

autoplot(gs[[1]])

4.9. Apply a gating method without csv template

Sometimes it will be helpful (especially when working with data that is already gated) to be able to interact with the GatingSet directly without the need to write the complete csv gating template. We can apply each automated gating method using the same fields as in the gatingTemplate, but provided as arguments to the gs_add_gating_method function. The populations added by each of these calls to gs_add_gating_method can be removed sequentially by gs_remove_gating_method, which will remove all populations added by the prior call to gs_add_gating_method. These two functions allow for interactive stagewise prototyping of a gatingTemplate.

For example, suppose we wanted to add a CD38-/HLA- sub-population to the cd4+cd8- population. We could do this as follows:

gs_add_gating_method(gs, alias = "non-activated cd4",
                         pop = "--",
                         parent = "cd4",
                         dims = "CD38,HLA",
                         gating_method = "tailgate")
plot(gs[[1]])

The addition of this population can then easily be undone by a call to gs_remove_gating_method:

gs_remove_gating_method(gs)
plot(gs[[1]])

5. Conclusion

The openCyto package allows users to specify their gating schemes and gate the data in a data-driven fashion. It frees the scientists from the labor-intensitive manual gating routines and increases the speed as well as the reproducibilty and objectivity of the data analysis work.



Try the openCyto package in your browser

Any scripts or data that you put into this service are public.

openCyto documentation built on Nov. 8, 2020, 5:40 p.m.