flowDashboard Demo

The following document is a guide to how the various objects were generated for the sampleDashboard app from a GatingSet.

The strength of having different display objects instead of the GatingSet are: 1) Each object can be associated with an analysis step, 2) each object handles error handling and display options in a seamless way, populating the modules with very little debugging on the user’s part, 3) exploratory data analysis of the flow data can be done in a seamless way, aggregating on notations.

Creating a GatingSet from GvHD Data

This code was taken from the flowWorkspace documentation. Note that we take the incoming flowSet and convert it to a ncdfFlowSet. In general, we find this file format is more stable, especially when generating plots.

## This code was modified from flowWorkspace examples
library(flowStats)
data(GvHD)
#select raw flow data and cast as ncdfFlowSet
fs<-ncdfFlowSet(GvHD)

pData(fs)$Visit <- ordered(pData(fs)$Visit)
pData(fs)$Days <- ordered(pData(fs)$Days)

#transform the raw data
tf <- transformList(colnames(fs[[1]])[3:6], asinh, transformationId="asinh")
fs_trans<-transform(fs,tf)
#add transformed data to a GatingSet
gs <- GatingSet(fs_trans)
gs
## A GatingSet with 35 samples
getNodes(gs[[1]]) #only contains root node
## [1] "root"
#add one gate
rg <- rectangleGate("FSC-H"=c(200,400), "SSC-H"=c(250, 400),
                    filterId="rectangle")
nodeID<-add(gs, rg)#it is added to root node by default if parent is not specified
nodeID
## [1] 2
getNodes(gs[[1]]) #the second population is named after filterId of the gate
## [1] "root"       "/rectangle"
#add a quadGate
qg <- quadGate("FL1-H"=2, "FL2-H"=4)
nodeIDs<-add(gs,qg,parent="rectangle")
nodeIDs #quadGate produces four population nodes
## [1] 3 4 5 6
getNodes(gs[[1]]) #population names are named after dimensions of gate if not specified
## [1] "root"                          "/rectangle"                   
## [3] "/rectangle/CD15 FITC-CD45 PE+" "/rectangle/CD15 FITC+CD45 PE+"
## [5] "/rectangle/CD15 FITC+CD45 PE-" "/rectangle/CD15 FITC-CD45 PE-"
#do the actual gating
recompute(gs)

Building Data Objects

Now that the GvHD data is gated and in a GatingSet, we can build the various objects. First we look at the markers and pick three markers as markers for general QC. Annotation is derived from the phenoData slot of gs@data. Here, we sample our data using only 1000 points per sample, in order to save memory.

#show annotation
pD <- pData(parameters(gs@data[[1]]))

qcMarkers <- pD$desc[-c(6)]

#show markers in data
qcMarkers
##                $P1S                $P2S                $P3S 
##        "FSC-Height"        "SSC-Height"         "CD15 FITC" 
##                $P4S                $P5S                $P7S 
##           "CD45 PE"        "CD14 PerCP"          "CD33 APC" 
##                $P8S 
## "Time (51.20 sec.)"
library(flowDashboard)
##build QCO object
QCO <- QCOFromGatingSet(gs,samplePop = 1000,qcMarkers = qcMarkers)
##show structure of qcFlowObj
QCO
## <qcFlowObj>
##   Inherits from: <commonDataObj>
##   Public:
##     annotation: data.table, data.frame
##     annotCols: Patient Visit Days Grade
##     checkIntegrity: function (reconcile = FALSE) 
##     clone: function (deep = FALSE) 
##     contextID: NULL
##     initialize: function (annotation, qcData, mapVar = NULL, checkIntegrity = TRUE, 
##     mapVar: name
##     markers: FSC.Height SSC.Height CD15.FITC CD45.PE CD14.PerCP CD33. ...
##     objId: QCO-VEQAJ10
##     qcData: data.table, data.frame
##     returnMergedData: function () 
##     setAnnotationDisplayOptions: function (annotCols) 
##     setMarkers: function (markers) 
##     setSubsetAndSortOptions: function (subsetOptions, sortOptions, checkIntegrity = TRUE) 
##     sortOptionList: NULL
##     sortOptions: Patient Visit Days Grade
##     subsetAnnotation: function (ids) 
##     subsetOptionList: list
##     subsetOptions: Patient Visit Days Grade

Things to notice: QCO has three main slots: QCO$qcData, which holds the expression data, QCO$annotation, which holds the annotation, and QCO$mapVar, which is the key mapping QCO$annotation into QCO$data.

Additionally, there are multiple methods that alter display options. Once these are set, the UI elements are generated automatically from the object.

Note that QCO has a slot called objId. This is the identifier used when using QCO in one of flowDashboard’s shiny modules. Having unique identfiers for each object helps to avoid namespace collisions in the shiny modules. By default, the identifer is randomly generated, though the user can override the default, either by specifying objId as an argument in the QCOfromGatingSet() function, or when invoking the modules separately. This objId lets the flowDashboard modules work together, or work separately (more on that later).

QCO$objId
## [1] "QCO-VEQAJ10"

If we use the returnMergedData() method, we can see the underlying merged data/annotation. We keep these two data tables separate in order to save space (avoiding redundant information), and because different display methods require either the annotation or the underlying data.

kable(QCO$returnMergedData()[1:15,])
idVar cellNum variable value Patient Visit Days Grade
s5a01 1679 FSC.Height 178 5 1 -6 3
s5a01 3117 FSC.Height 350 5 1 -6 3
s5a01 1847 FSC.Height 400 5 1 -6 3
s5a01 1140 FSC.Height 197 5 1 -6 3
s5a01 1576 FSC.Height 61 5 1 -6 3
s5a01 2326 FSC.Height 274 5 1 -6 3
s5a01 2419 FSC.Height 594 5 1 -6 3
s5a01 2053 FSC.Height 621 5 1 -6 3
s5a01 1491 FSC.Height 190 5 1 -6 3
s5a01 107 FSC.Height 60 5 1 -6 3
s5a01 2260 FSC.Height 183 5 1 -6 3
s5a01 492 FSC.Height 166 5 1 -6 3
s5a01 1091 FSC.Height 63 5 1 -6 3
s5a01 405 FSC.Height 62 5 1 -6 3
s5a01 3245 FSC.Height 281 5 1 -6 3

Here we build the gatingObj from the GatingSet. We also create all of the relevant gating plots. Note that if you have a lot of samples and a lot of gates, the plotting can take a while.

#run this code if you want to generate the images as well
GO <- GOFromGatingSet(gs,imageDir = "data/gating/", makeGraphs = TRUE)
#graphs not generated in this vignette -run the above code if you want to
GO <- GOFromGatingSet(gs,imageDir = "data/gating/", makeGraphs = FALSE, objId="GO-UPCWK13")

##show structure of gatingObj
GO
## <gatingObj>
##   Inherits from: <commonDataObj>
##   Public:
##     annotation: data.table, data.frame
##     annotCols: name Patient Visit Days Grade Population Count
##     checkIntegrity: function (reconcile = FALSE) 
##     clone: function (deep = FALSE) 
##     contextID: NULL
##     gates: NULL
##     imageDir: data/gating/
##     initialize: function (annotation, popTable, mapVar = NULL, gates = NULL, 
##     mapVar: name
##     objId: GO-UPCWK13
##     popSubsets: list
##     popTable: data.table, data.frame
##     populations: rectangle CD15 FITC-CD45 PE+ CD15 FITC+CD45 PE+ CD15 FIT ...
##     returnMergedData: function () 
##     setAnnotationDisplayOptions: function (annotCols) 
##     setPopulations: function (popList) 
##     setPopulationSubset: function (subPopSets = NULL) 
##     setSubsetAndSortOptions: function (subsetOptions, sortOptions, checkIntegrity = TRUE) 
##     sortOptionList: NULL
##     sortOptions: Patient Visit Days Grade
##     subsetAnnotation: function (ids) 
##     subsetOptionList: list
##     subsetOptions: Patient Visit Days Grade

Again, we can see the merged data table from GO by using the returnMergedData() method built into GO:

kable(GO$returnMergedData()[1:30,])
name Population Parent Count ParentCount idVar percentPop zscore popKey Patient Visit Days Grade
s5a01 rectangle root 97 3420 s5a01+rectangle+GO-UPCWK13 2.8362573 0.3298607 s5a01+rectangle 5 1 -6 3
s5a01 CD15 FITC-CD45 PE+ rectangle 0 97 s5a01+CD15 FITCnegCD45 PEpos+GO-UPCWK13 0.0000000 -0.3904334 s5a01+CD15 FITC-CD45 PE+ 5 1 -6 3
s5a01 CD15 FITC+CD45 PE+ rectangle 89 97 s5a01+CD15 FITCposCD45 PEpos+GO-UPCWK13 91.7525773 -0.1272707 s5a01+CD15 FITC+CD45 PE+ 5 1 -6 3
s5a01 CD15 FITC+CD45 PE- rectangle 6 97 s5a01+CD15 FITCposCD45 PEneg+GO-UPCWK13 6.1855670 1.0150601 s5a01+CD15 FITC+CD45 PE- 5 1 -6 3
s5a01 CD15 FITC-CD45 PE- rectangle 2 97 s5a01+CD15 FITCnegCD45 PEneg+GO-UPCWK13 2.0618557 -0.1072787 s5a01+CD15 FITC-CD45 PE- 5 1 -6 3
s5a02 rectangle root 54 3405 s5a02+rectangle+GO-UPCWK13 1.5859031 -0.1554846 s5a02+rectangle 5 2 0 3
s5a02 CD15 FITC-CD45 PE+ rectangle 0 54 s5a02+CD15 FITCnegCD45 PEpos+GO-UPCWK13 0.0000000 -0.3904334 s5a02+CD15 FITC-CD45 PE+ 5 2 0 3
s5a02 CD15 FITC+CD45 PE+ rectangle 46 54 s5a02+CD15 FITCposCD45 PEpos+GO-UPCWK13 85.1851852 -0.6216961 s5a02+CD15 FITC+CD45 PE+ 5 2 0 3
s5a02 CD15 FITC+CD45 PE- rectangle 1 54 s5a02+CD15 FITCposCD45 PEneg+GO-UPCWK13 1.8518519 -0.1657054 s5a02+CD15 FITC+CD45 PE- 5 2 0 3
s5a02 CD15 FITC-CD45 PE- rectangle 7 54 s5a02+CD15 FITCnegCD45 PEneg+GO-UPCWK13 12.9629630 0.8219709 s5a02+CD15 FITC-CD45 PE- 5 2 0 3
s5a03 rectangle root 11 3435 s5a03+rectangle+GO-UPCWK13 0.3202329 -0.6467750 s5a03+rectangle 5 3 6 3
s5a03 CD15 FITC-CD45 PE+ rectangle 0 11 s5a03+CD15 FITCnegCD45 PEpos+GO-UPCWK13 0.0000000 -0.3904334 s5a03+CD15 FITC-CD45 PE+ 5 3 6 3
s5a03 CD15 FITC+CD45 PE+ rectangle 7 11 s5a03+CD15 FITCposCD45 PEpos+GO-UPCWK13 63.6363636 -2.2439970 s5a03+CD15 FITC+CD45 PE+ 5 3 6 3
s5a03 CD15 FITC+CD45 PE- rectangle 1 11 s5a03+CD15 FITCposCD45 PEneg+GO-UPCWK13 9.0909091 1.8066506 s5a03+CD15 FITC+CD45 PE- 5 3 6 3
s5a03 CD15 FITC-CD45 PE- rectangle 3 11 s5a03+CD15 FITCnegCD45 PEneg+GO-UPCWK13 27.2727273 2.0417866 s5a03+CD15 FITC-CD45 PE- 5 3 6 3
s5a04 rectangle root 33 8550 s5a04+rectangle+GO-UPCWK13 0.3859649 -0.6212600 s5a04+rectangle 5 4 12 3
s5a04 CD15 FITC-CD45 PE+ rectangle 0 33 s5a04+CD15 FITCnegCD45 PEpos+GO-UPCWK13 0.0000000 -0.3904334 s5a04+CD15 FITC-CD45 PE+ 5 4 12 3
s5a04 CD15 FITC+CD45 PE+ rectangle 31 33 s5a04+CD15 FITCposCD45 PEpos+GO-UPCWK13 93.9393939 0.0373636 s5a04+CD15 FITC+CD45 PE+ 5 4 12 3
s5a04 CD15 FITC+CD45 PE- rectangle 2 33 s5a04+CD15 FITCposCD45 PEneg+GO-UPCWK13 6.0606061 0.9810132 s5a04+CD15 FITC+CD45 PE- 5 4 12 3
s5a04 CD15 FITC-CD45 PE- rectangle 0 33 s5a04+CD15 FITCnegCD45 PEneg+GO-UPCWK13 0.0000000 -0.2830387 s5a04+CD15 FITC-CD45 PE- 5 4 12 3
s5a05 rectangle root 371 10410 s5a05+rectangle+GO-UPCWK13 3.5638809 0.6122996 s5a05+rectangle 5 5 19 3
s5a05 CD15 FITC-CD45 PE+ rectangle 0 371 s5a05+CD15 FITCnegCD45 PEpos+GO-UPCWK13 0.0000000 -0.3904334 s5a05+CD15 FITC-CD45 PE+ 5 5 19 3
s5a05 CD15 FITC+CD45 PE+ rectangle 367 371 s5a05+CD15 FITCposCD45 PEpos+GO-UPCWK13 98.9218329 0.4124660 s5a05+CD15 FITC+CD45 PE+ 5 5 19 3
s5a05 CD15 FITC+CD45 PE- rectangle 3 371 s5a05+CD15 FITCposCD45 PEneg+GO-UPCWK13 0.8086253 -0.4499432 s5a05+CD15 FITC+CD45 PE- 5 5 19 3
s5a05 CD15 FITC-CD45 PE- rectangle 1 371 s5a05+CD15 FITCnegCD45 PEneg+GO-UPCWK13 0.2695418 -0.2600620 s5a05+CD15 FITC-CD45 PE- 5 5 19 3
s5a06 rectangle root 45 3750 s5a06+rectangle+GO-UPCWK13 1.2000000 -0.3052791 s5a06+rectangle 5 6 26 3
s5a06 CD15 FITC-CD45 PE+ rectangle 0 45 s5a06+CD15 FITCnegCD45 PEpos+GO-UPCWK13 0.0000000 -0.3904334 s5a06+CD15 FITC-CD45 PE+ 5 6 26 3
s5a06 CD15 FITC+CD45 PE+ rectangle 42 45 s5a06+CD15 FITCposCD45 PEpos+GO-UPCWK13 93.3333333 -0.0082636 s5a06+CD15 FITC+CD45 PE+ 5 6 26 3
s5a06 CD15 FITC+CD45 PE- rectangle 3 45 s5a06+CD15 FITCposCD45 PEneg+GO-UPCWK13 6.6666667 1.1461407 s5a06+CD15 FITC+CD45 PE- 5 6 26 3
s5a06 CD15 FITC-CD45 PE- rectangle 0 45 s5a06+CD15 FITCnegCD45 PEneg+GO-UPCWK13 0.0000000 -0.2830387 s5a06+CD15 FITC-CD45 PE- 5 6 26 3

Finally, we build a populationExpressionObj called PEO from the GatingSet. We limit our samples to

PEO <- PEOFromGatingSet(gs, samplePop = 1000)
PEO
## <populationExpressionObj>
##   Inherits from: <commonDataObj>
##   Public:
##     annotation: data.table, data.frame
##     annotCols: Patient Visit Days Grade
##     checkIntegrity: function (reconcile = FALSE) 
##     clone: function (deep = FALSE) 
##     contextID: NULL
##     expressionData: data.table, data.frame
##     initialize: function (annotation, expressionData, mapVar = NULL, checkIntegrity = TRUE, 
##     mapVar: name
##     markers: FSC-Height SSC-Height CD15 FITC CD45 PE CD14 PerCP FL2-A ...
##     objId: PEO-DGWYR90
##     populations: rectangle CD15 FITC-CD45 PE+ CD15 FITC+CD45 PE+ CD15 FIT ...
##     returnMergedData: function () 
##     setAnnotationDisplayOptions: function (annotCols) 
##     setMarkers: function (markers) 
##     setSubsetAndSortOptions: function (subsetOptions, sortOptions, checkIntegrity = TRUE) 
##     sortOptionList: NULL
##     sortOptions: Patient Visit Days Grade
##     subsetAnnotation: function (ids) 
##     subsetOptionList: list
##     subsetOptions: Patient Visit Days Grade

We want to remove Time as a marker from PEO:

markers <- as.character(PEO$markers)
markers
## [1] "FSC-Height"        "SSC-Height"        "CD15 FITC"        
## [4] "CD45 PE"           "CD14 PerCP"        "FL2-A"            
## [7] "CD33 APC"          "Time (51.20 sec.)"
PEO$setMarkers(markers[1:7])

Viewing the merged data table from PEO:

kable(PEO$returnMergedData()[1:30,])
cell idVar variable value Population Patient Visit Days Grade
1 s5a01 FSC-Height 371 rectangle 5 1 -6 3
2 s5a01 FSC-Height 236 rectangle 5 1 -6 3
3 s5a01 FSC-Height 269 rectangle 5 1 -6 3
4 s5a01 FSC-Height 259 rectangle 5 1 -6 3
5 s5a01 FSC-Height 232 rectangle 5 1 -6 3
6 s5a01 FSC-Height 201 rectangle 5 1 -6 3
7 s5a01 FSC-Height 240 rectangle 5 1 -6 3
8 s5a01 FSC-Height 380 rectangle 5 1 -6 3
9 s5a01 FSC-Height 206 rectangle 5 1 -6 3
10 s5a01 FSC-Height 213 rectangle 5 1 -6 3
11 s5a01 FSC-Height 276 rectangle 5 1 -6 3
12 s5a01 FSC-Height 217 rectangle 5 1 -6 3
13 s5a01 FSC-Height 239 rectangle 5 1 -6 3
14 s5a01 FSC-Height 232 rectangle 5 1 -6 3
15 s5a01 FSC-Height 211 rectangle 5 1 -6 3
16 s5a01 FSC-Height 226 rectangle 5 1 -6 3
17 s5a01 FSC-Height 247 rectangle 5 1 -6 3
18 s5a01 FSC-Height 229 rectangle 5 1 -6 3
19 s5a01 FSC-Height 264 rectangle 5 1 -6 3
20 s5a01 FSC-Height 205 rectangle 5 1 -6 3
21 s5a01 FSC-Height 218 rectangle 5 1 -6 3
22 s5a01 FSC-Height 223 rectangle 5 1 -6 3
23 s5a01 FSC-Height 204 rectangle 5 1 -6 3
24 s5a01 FSC-Height 209 rectangle 5 1 -6 3
25 s5a01 FSC-Height 226 rectangle 5 1 -6 3
26 s5a01 FSC-Height 247 rectangle 5 1 -6 3
27 s5a01 FSC-Height 205 rectangle 5 1 -6 3
28 s5a01 FSC-Height 206 rectangle 5 1 -6 3
29 s5a01 FSC-Height 202 rectangle 5 1 -6 3
30 s5a01 FSC-Height 300 rectangle 5 1 -6 3

Save all objects into a new Rda:

save(PEO, GO, QCO, file="data/GvHD.rda")

Reference Shiny App

The reference shiny app can be seen at https://tladeras.shinyapps.io/sampleFlowDashboard/

Subsetting Module

In the app, in our server.R, we extract the annotation in order to make it a reactive that is subsettable and that has a common interface.

For example, for our gatingObj GO, we create the following subsetModule, which is used as an input for the gatingModuleOutput module created by gatingModuleOutputGGFromGO(). Note that we just pass the reactive name annotationGO and not the evaluation annotationGO().

#look for this code in server.R
annotationGO <- subsetModuleDCO(input, output, dataObj = GO)
gatingModuleOutputGGFromGO(input, output, session, GO, annotation=annotationGO)

In ui.R, there is a corresponding entry that generates the subsetting UI, and the UI for the gatingModule itself:

#this is put in a conditional panel to work with shinydashboard
subsetModuleUICDO(GO)

#this is put into a tabPanel
gatingModuleUIFromGO(GO)

Not Using a Subset Module

If you don’t want to use the subsetModule for a visualization, you can do the following in your server.R to generate an annotation reactive. Note that gatingModuleOutputGGFromGO expects a reactive in its annotation argument.

annotationGO <- reactive({GO$annotation})
gatingModuleOutputGGFromGO(input, output, session, GO, annotation=annotationGO)

And put this in your ui.R:

#this is put into a tabPanel
gatingModuleUIFromGO(GO)