This document demonstrates the potential usefulness of beadplexr with data generated with the CBA assay system from BD, and the MACSPlex assay system from Miltenyi Biotec. The package has been tested with assay data from the two systems. Unfortunately, I cannot share the data - for demonstration purposes I have simulated data for the two systems. The simulated data is deliberately noisy, and do not reflect the true quality of the respective assays. The sole purpose of the simulated data is to mimic the two dimensional bead identification.
## ## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats': ## ## filter, lag
## The following objects are masked from 'package:base': ## ## intersect, setdiff, setequal, union
The data from the CBA system is similar to the data from the MACSPlex system: there is a single population in the forward-side scatter and 10 populations separated by brightness of FITC and PE. The concentration of the analytes are given in the APC channel.
<- simplex[["mplex"]] mplex_data |> mplex_data facs_plot(.x = "FSC", .y = "SSC", .type = "hex") |> mplex_data facs_plot(.x = "FITC", .y = "PE", .type = "hex")
There is a single bead population in the forward-side scatter and up to 30 bead populations separated by the brightness of APC and APC-Cy7. The concentration of the analytes are given in the PS channel.
<- simplex[["cba"]] cba_data |> cba_data facs_plot(.x = "FSC", .y = "SSC", .type = "hex") |> cba_data facs_plot(.x = "APC", .y = "APC-Cy7", .type = "hex")
Since there is just a single population in the forward-side scatter we can jump straight to identification of the analytes. It is of course possible to decrease spurious analyte signals by focusing on true bead events in the forward-side scatter, and it might be necessary in a true experiment.
We use the
identify_analyte() to annotate the correct analytes in the MACSPlex-like data. The function requires the parameter
.analyte_id, which – in a true experiment – correspond to some identifier given by Miltenyi Biotec. Here we just assign a number.
The function also allows for removing events that are far from the center of the analyte clusters. This is done by the parameter
.trim. Here we set the value to
0.1 which means that 10% of the events are removed from the clusters. Excluded events are given the analyte ID
NA and can easily be excluded from further processing.
<- mplex_data |> mplex_analyte identify_analyte(.parameter = c("FITC", "PE"), .analyte_id = as.character(c(1:10))) |> mplex_analyte facs_plot(.x = "FITC", .y = "PE", .beads = "analyte")
<- mplex_data |> mplex_analyte identify_analyte(.parameter = c("FITC", "PE"), .analyte_id = as.character(c(1:10)), .trim = 0.1) |> mplex_analyte facs_plot(.x = "FITC", .y = "PE", .beads = "analyte")
We can now look at the intensity of the APC channel, to find the relative amount of analyte bound to each bead. In this case it makes very little sense, but we’ll do it for the purpose of completeness
|> mplex_analyte filter(!is.na(analyte)) |> mutate(analyte = factor(analyte, levels = c(1:10))) |> ggplot() + aes(x = APC, fill = analyte) + geom_density()+ facet_wrap(~ analyte)+ theme(legend.position = "none")
The trimming can also be done on the identified analytes. Here we remove 5% of the events.
|> mplex_analyte # filter(!is.na(analyte)) |> group_by(analyte) |> trim_population(.parameter = "APC", .column_name = "analyte", .trim = 0.05) |> ungroup() |> mutate(analyte = factor(analyte, levels = c(1:10))) |> ggplot() + aes(x = APC, fill = analyte) + geom_density() + facet_wrap(~ analyte) + theme(legend.position = "none")
For the CBA assay system, the approach is quite similar to the MACSPlex:
<- cba_data |> cba_analyte identify_analyte(.parameter = c("APC", "APC-Cy7"), .analyte_id = as.character(c(1:30)), .trim = 0.1) |> cba_analyte facs_plot(.x = "APC", .y = "APC-Cy7", .beads = "analyte")
The analyte ID is assigned to each cluster, based on the order of the center of the cluster. For analytes where the bead is identified by two fluorescent parameters it could be a little tricky, because the centers are first sorted by the first parameter given, and then the second. The order of parameters and analyte IDs matter a lot! I urge you to double check that the beads are identified as expected. Once you have everything sorted out, it should remain stable for your cytometer.
For the simulated MACSPlex-like data the IDs are assigned as below.
|> mplex_analyte filter(!is.na(analyte)) |> group_by(analyte) |> summarise(`FITC mean` = mean(FITC), `PE mean` = mean(PE)) |> arrange(`FITC mean`, `PE mean`) |> ::kable() knitr
|analyte||FITC mean||PE mean|
For the CBA-like data, some clusters are still a bit too wide, while the trimming of
0.1 made others look quite fine. The simulated data contains a bit of noise hat might interfere the the cluster identification. This noise might not be present in the a true experiment, but is included to demonstrate the removal of lonely, noisy events by the
despeckel() functionality of beadplexr.
<- cba_data |> cba_analyte despeckle(.parameter = c("APC", "APC-Cy7"), .neighbours = 2) |> identify_analyte(.parameter = c("APC", "APC-Cy7"), .analyte_id = as.character(c(1:30)), .trim = 0.01)
## Loading required namespace: igraph
|> cba_analyte facs_plot(.x = "APC", .y = "APC-Cy7", .beads = "analyte")
In this particular example, the
despeckle() is a little too rough on the population in the lower left corner, and it is probably more prudent to accept a little noise on one analyte than decimation of another.
## R version 4.1.2 (2021-11-01) ## Platform: x86_64-pc-linux-gnu (64-bit) ## Running under: Ubuntu 21.04 ## ## Matrix products: default ## BLAS: /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.9.0 ## LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.9.0 ## ## locale: ##  LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C ##  LC_TIME=de_DE.UTF-8 LC_COLLATE=C ##  LC_MONETARY=de_DE.UTF-8 LC_MESSAGES=en_US.UTF-8 ##  LC_PAPER=de_DE.UTF-8 LC_NAME=C ##  LC_ADDRESS=C LC_TELEPHONE=C ##  LC_MEASUREMENT=de_DE.UTF-8 LC_IDENTIFICATION=C ## ## attached base packages: ##  stats graphics grDevices utils datasets methods base ## ## other attached packages: ##  ggplot2_3.3.5 dplyr_1.0.8 beadplexr_0.4.1 ## ## loaded via a namespace (and not attached): ##  tidyselect_1.1.2 terra_1.5-21 xfun_0.30 bslib_0.3.1 ##  purrr_0.3.4 lattice_0.20-45 colorspace_2.0-3 vctrs_0.3.8 ##  generics_0.1.2 htmltools_0.5.2 yaml_2.3.5 utf8_1.2.2 ##  rlang_1.0.1 hexbin_1.28.2 jquerylib_0.1.4 pillar_1.7.0 ##  glue_1.6.2 withr_2.5.0 sp_1.4-6 lifecycle_1.0.1 ##  stringr_1.4.0 munsell_0.5.0 gtable_0.3.0 raster_3.5-15 ##  codetools_0.2-18 evaluate_0.15 labeling_0.4.2 knitr_1.37 ##  fastmap_1.1.0 fansi_1.0.2 highr_0.9 Rcpp_1.0.8 ##  scales_1.1.1 jsonlite_1.8.0 farver_2.1.0 digest_0.6.29 ##  stringi_1.7.6 grid_4.1.2 cli_3.2.0 tools_4.1.2 ##  magrittr_2.0.2 sass_0.4.0 tibble_3.1.6 cluster_2.1.2 ##  crayon_1.5.0 tidyr_1.2.0 pkgconfig_2.0.3 ellipsis_0.3.2 ##  rmarkdown_2.12 rstudioapi_0.13 R6_2.5.1 mclust_5.4.9 ##  igraph_1.2.11 compiler_4.1.2