# Build N-way classifier (N>2) from clinical and multi-omic data

Shraddha Pai

#### 2021-05-19

#### Package

netDx 1.4.0

# 1 TL;DR

This code block is not evaluated. Need a breakdown? Look at the following sections.

    suppressWarnings(suppressMessages(require(netDx)))
    suppressWarnings(suppressMessages(library(curatedTCGAData)))

    # fetch RNA, methylation and proteomic data for TCGA BRCA set
    brca <- suppressMessages(
       curatedTCGAData("BRCA",
                   c("mRNAArray","RPPA*","Methylation_methyl27*"),
        dry.run=FALSE,version="1.1.38"))

    # process input variables
    # prepare clinical variable - stage
    staget <- sub("[abcd]","",sub("t","",colData(brca)$pathology_T_stage))
    staget <- suppressWarnings(as.integer(staget))
    colData(brca)$STAGE <- staget
    # exclude normal, HER2 (small num samples)
    pam50 <- colData(brca)$PAM50.mRNA
    idx <- union(which(pam50 %in% c("Normal-like","HER2-enriched")),
        which(is.na(staget)))
    idx <- union(idx, which(is.na(pam50)))
    pID <- colData(brca)$patientID
    tokeep <- setdiff(pID, pID[idx])
    brca <- brca[,tokeep,]
    pam50 <- colData(brca)$PAM50.mRNA
    colData(brca)$pam_mod <- pam50

    # remove duplicate names
    smp <- sampleMap(brca)
    for (nm in names(brca)) {
        samps <- smp[which(smp$assay==nm),]
        notdup <- samps[which(!duplicated(samps$primary)),"colname"]
        brca[[nm]] <- suppressMessages(brca[[nm]][,notdup])
    }

    # colData must have ID and STATUS columns
    pID <- colData(brca)$patientID
    colData(brca)$ID <- pID
    colData(brca)$STATUS <- gsub(" ","_",colData(brca)$pam_mod)

    # create grouping rules
    groupList <- list()
    # genes in mRNA data are grouped by pathways
    pathList <- readPathways(fetchPathwayDefinitions("January",2018))
    groupList[["BRCA_mRNAArray-20160128"]] <- pathList[1:3]
    # clinical data is not grouped; each variable is its own feature
    groupList[["clinical"]] <- list(
          age="patient.age_at_initial_pathologic_diagnosis",
           stage="STAGE"
    )
    # for methylation generate one feature containing all probes
    # same for proteomics data
    tmp <- list(rownames(experiments(brca)[[2]]));
    names(tmp) <- names(brca)[2]
    groupList[[names(brca)[2]]] <- tmp

    tmp <- list(rownames(experiments(brca)[[3]]));
    names(tmp) <- names(brca)[3]
    groupList[[names(brca)[3]]] <- tmp

    # create function to tell netDx how to build features
    # (PSN) from your data
    makeNets <- function(dataList, groupList, netDir,...) {
        netList <- c() # initialize before is.null() check
        # correlation-based similarity for mRNA, RPPA and methylation data
        # (Pearson correlation)
        for (nm in setdiff(names(groupList),"clinical")) {
           # NOTE: the check for is.null() is important!
            if (!is.null(groupList[[nm]])) {
            netList <- makePSN_NamedMatrix(dataList[[nm]],
                         rownames(dataList[[nm]]),
                       groupList[[nm]],netDir,verbose=FALSE,
                         writeProfiles=TRUE,...)
            }
        }

        # make clinical nets (normalized difference)
        netList2 <- c()
        if (!is.null(groupList[["clinical"]])) {
        netList2 <- makePSN_NamedMatrix(dataList$clinical,
            rownames(dataList$clinical),
            groupList[["clinical"]],netDir,
            simMetric="custom",customFunc=normDiff, # custom function
            writeProfiles=FALSE,
            sparsify=TRUE,verbose=TRUE,...)
        }
        netList <- c(unlist(netList),unlist(netList2))
        return(netList)
    }

    # run predictor
    set.seed(42) # make results reproducible
    outDir <- paste(tempdir(),randAlphanumString(),"pred_output",sep=getFileSep())
    # To see all messages, remove suppressMessages()
    # and set logging="default".
    # To keep all intermediate data, set keepAllData=TRUE
    numSplits <- 2L
    out <- suppressMessages(
       buildPredictor(dataList=brca,groupList=groupList,
          makeNetFunc=makeNets,
          outDir=outDir, ## netDx requires absolute path
          numSplits=numSplits, featScoreMax=2L, featSelCutoff=1L,
           numCores=1L)
    )

    # collect results for accuracy
    st <- unique(colData(brca)$STATUS)
    acc <- matrix(NA,ncol=length(st),nrow=numSplits)  # accuracy by class
    colnames(acc) <- st
    for (k in 1:numSplits) {
        pred <- out[[sprintf("Split%i",k)]][["predictions"]];
        tmp <- pred[,c("ID","STATUS","TT_STATUS","PRED_CLASS",
                         sprintf("%s_SCORE",st))]
        for (m in 1:length(st)) {
           tmp2 <- subset(tmp, STATUS==st[m])
           acc[k,m] <- sum(tmp2$PRED==tmp2$STATUS)/nrow(tmp2)
        }
    }

    # accuracy by class
    print(round(acc*100,2))

    # confusion matrix
    res <- out$Split1$predictions
    print(table(res[,c("STATUS","PRED_CLASS")]))

    sessionInfo()

# 2 Introduction

In this example, we will use clinical data and three types of ’omic data - gene expression, DNA methylation and proteomic data - to classify breast tumours as being one of three types: Luminal A, Luminal B, or Basal. This example is nearly identical to the one used to build a binary classifier.

We also use several strategies and definitions of similarity to create features:

- Clinical variables: Each variable is its own feature (e.g. age); similarity is defined as normalized difference.
- Gene expression: Features are defined at the level of pathways; i.e. a feature groups genes corresponding to the pathway. Similarity is defined as pairwise Pearson correlation
- Proteomic and methylation data: Features are defined at the level of the entire data layer; a single feature is created for all of proteomic data, and the same for methylation. Similarity is defined by pairwise Pearson correlation

# 3 Setup

Load the `netDx` package.

    suppressWarnings(suppressMessages(require(netDx)))

# 4 Data

For this example we pull data from the The Cancer Genome Atlas through the BioConductor `curatedTCGAData` package. The fetch command automatically brings in a `MultiAssayExperiment` object.

    suppressMessages(library(curatedTCGAData))

We use the `curatedTCGAData()` command to look at available assays in the breast cancer dataset.

    curatedTCGAData(diseaseCode="BRCA", assays="*",dry.run=TRUE,version="1.1.38")

    ## snapshotDate(): 2021-05-05

    ## See '?curatedTCGAData' for 'diseaseCode' and 'assays' inputs

    ##     ah_id                                      title file_size
    ## 1   EH584                       BRCA_CNASeq-20160128      0 Mb
    ## 2   EH585                       BRCA_CNASNP-20160128    9.8 Mb
    ## 3   EH586                       BRCA_CNVSNP-20160128    2.8 Mb
    ## 4   EH588             BRCA_GISTIC_AllByGene-20160128    1.3 Mb
    ## 5  EH2121                 BRCA_GISTIC_Peaks-20160128      0 Mb
    ## 6   EH589     BRCA_GISTIC_ThresholdedByGene-20160128    0.4 Mb
    ## 7  EH2122  BRCA_Methylation_methyl27-20160128_assays   63.2 Mb
    ## 8  EH2123      BRCA_Methylation_methyl27-20160128_se    0.4 Mb
    ## 9  EH2124 BRCA_Methylation_methyl450-20160128_assays 2613.2 Mb
    ## 10 EH2125     BRCA_Methylation_methyl450-20160128_se    6.1 Mb
    ## 11  EH593                 BRCA_miRNASeqGene-20160128    0.6 Mb
    ## 12  EH594                    BRCA_mRNAArray-20160128   27.3 Mb
    ## 13  EH595                     BRCA_Mutation-20160128    4.5 Mb
    ## 14  EH596              BRCA_RNASeq2GeneNorm-20160128   64.5 Mb
    ## 15  EH597                   BRCA_RNASeqGene-20160128     30 Mb
    ## 16  EH598                    BRCA_RPPAArray-20160128    1.6 Mb
    ##                    rdataclass rdatadateadded rdatadateremoved
    ## 1            RaggedExperiment     2017-10-10             <NA>
    ## 2            RaggedExperiment     2017-10-10             <NA>
    ## 3            RaggedExperiment     2017-10-10             <NA>
    ## 4        SummarizedExperiment     2017-10-10             <NA>
    ## 5  RangedSummarizedExperiment     2019-01-09             <NA>
    ## 6        SummarizedExperiment     2017-10-10             <NA>
    ## 7        SummarizedExperiment     2019-01-09             <NA>
    ## 8        SummarizedExperiment     2019-01-09             <NA>
    ## 9            RaggedExperiment     2019-01-09             <NA>
    ## 10       SummarizedExperiment     2019-01-09             <NA>
    ## 11       SummarizedExperiment     2017-10-10             <NA>
    ## 12       SummarizedExperiment     2017-10-10             <NA>
    ## 13           RaggedExperiment     2017-10-10             <NA>
    ## 14       SummarizedExperiment     2017-10-10             <NA>
    ## 15       SummarizedExperiment     2017-10-10             <NA>
    ## 16       SummarizedExperiment     2017-10-10             <NA>

In this call we fetch only the gene expression, proteomic and methylation data; setting `dry.run=FALSE` initiates the fetching of the data.

    brca <- suppressMessages(
       curatedTCGAData("BRCA",
                   c("mRNAArray","RPPA*","Methylation_methyl27*"),
        dry.run=FALSE,version="1.1.38"))

This next code block prepares the TCGA data. In practice you would do this once, and save the data before running netDx, but we run it here to see an end-to-end example.

    # prepare clinical variable - stage
    staget <- sub("[abcd]","",sub("t","",colData(brca)$pathology_T_stage))
    staget <- suppressWarnings(as.integer(staget))
    colData(brca)$STAGE <- staget

    # exclude normal, HER2 (small num samples)
    pam50 <- colData(brca)$PAM50.mRNA
    idx <- union(which(pam50 %in% c("Normal-like","HER2-enriched")),
        which(is.na(staget)))
    idx <- union(idx, which(is.na(pam50)))
    pID <- colData(brca)$patientID
    tokeep <- setdiff(pID, pID[idx])
    brca <- brca[,tokeep,]

    pam50 <- colData(brca)$PAM50.mRNA
    colData(brca)$pam_mod <- pam50

    # remove duplicate names
    smp <- sampleMap(brca)
    for (nm in names(brca)) {
        samps <- smp[which(smp$assay==nm),]
        notdup <- samps[which(!duplicated(samps$primary)),"colname"]
        brca[[nm]] <- suppressMessages(brca[[nm]][,notdup])
    }

    ## harmonizing input:
    ##   removing 59 sampleMap rows with 'colname' not in colnames of experiments

    ## harmonizing input:
    ##   removing 19 sampleMap rows with 'colname' not in colnames of experiments

    ## harmonizing input:
    ##   removing 26 sampleMap rows with 'colname' not in colnames of experiments

The important thing is to create `ID` and `STATUS` columns in the sample metadata slot. netDx uses these to get the patient identifiers and labels, respectively.

    pID <- colData(brca)$patientID
    colData(brca)$ID <- pID
    colData(brca)$STATUS <- gsub(" ","_",colData(brca)$pam_mod)

# 5 Rules to create features (patient similarity networks)

Our plan is to group gene expression data by pathways and clinical data by single variables. We will treat methylation and proteomic data each as a single feature, so each of those groups will contain the entire input table for those corresponding data types.

In the code below, we fetch pathway definitions for January 2018 from (http://download.baderlab.org/EM_Genesets) and group gene expression data by pathways. To keep the example short, we limit to only three pathways, but in practice you would use all pathways meeting a size criterion; e.g. those containing between 10 and 500 genes.

Grouping rules are accordingly created for the clinical, methylation and proteomic data.

    groupList <- list()

    # genes in mRNA data are grouped by pathways
    pathList <- readPathways(fetchPathwayDefinitions("January",2018))

    ## ---------------------------------------

    ## Fetching http://download.baderlab.org/EM_Genesets/January_01_2018/Human/symbol/Human_AllPathways_January_01_2018_symbol.gmt

    ## File: 3aeb7097bb288_Human_AllPathways_January_01_2018_symbol.gmt

    ## Read 3028 pathways in total, internal list has 3009 entries

    ##  FILTER: sets with num genes in [10, 200]

    ##    => 971 pathways excluded
    ##    => 2038 left

    groupList[["BRCA_mRNAArray-20160128"]] <- pathList[1:3]
    # clinical data is not grouped; each variable is its own feature
    groupList[["clinical"]] <- list(
          age="patient.age_at_initial_pathologic_diagnosis",
           stage="STAGE"
    )
    # for methylation generate one feature containing all probes
    # same for proteomics data
    tmp <- list(rownames(experiments(brca)[[2]]));
    names(tmp) <- names(brca)[2]
    groupList[[names(brca)[2]]] <- tmp

    tmp <- list(rownames(experiments(brca)[[3]]));
    names(tmp) <- names(brca)[3]
    groupList[[names(brca)[3]]] <- tmp

## 5.1 Define patient similarity for each network

We provide `netDx` with a custom function to generate similarity networks (i.e. features). The first block tells netDx to generate correlation-based networks using everything but the clinical data. This is achieved by the call:

    makePSN_NamedMatrix(..., writeProfiles=TRUE,...)`

The second block makes a different call to `makePSN_NamedMatrix()` but this time, requesting the use of the normalized difference similarity metric. This is achieved by calling:

       makePSN_NamedMatrix(,...,
                           simMetric="custom", customFunc=normDiff,
                           writeProfiles=FALSE)

`normDiff` is a function provided in the `netDx` package, but the user may define custom similarity functions in this block of code and pass those to `makePSN_NamedMatrix()`, using the `customFunc` parameter.

    makeNets <- function(dataList, groupList, netDir,...) {
        netList <- c() # initialize before is.null() check
        # correlation-based similarity for mRNA, RPPA and methylation data
        # (Pearson correlation)
        for (nm in setdiff(names(groupList),"clinical")) {
           # NOTE: the check for is.null() is important!
            if (!is.null(groupList[[nm]])) {
            netList <- makePSN_NamedMatrix(dataList[[nm]],
                         rownames(dataList[[nm]]),
                       groupList[[nm]],netDir,verbose=FALSE,
                         writeProfiles=TRUE,...)
            }
        }

        # make clinical nets (normalized difference)
        netList2 <- c()
        if (!is.null(groupList[["clinical"]])) {
        netList2 <- makePSN_NamedMatrix(dataList$clinical,
            rownames(dataList$clinical),
            groupList[["clinical"]],netDir,
            simMetric="custom",customFunc=normDiff, # custom function
            writeProfiles=FALSE,
            sparsify=TRUE,verbose=TRUE,...)
        }
        netList <- c(unlist(netList),unlist(netList2))
        return(netList)
    }

# 6 Build predictor

Finally we make the call to build the predictor.

    set.seed(42) # make results reproducible

    # location for intermediate work
    # set keepAllData to TRUE to not delete at the end of the
    # predictor run.
    # This can be useful for debugging.
    outDir <- paste(tempdir(),"pred_output",sep=getFileSep()) # use absolute path
    numSplits <- 2L
    out <- suppressMessages(
       buildPredictor(dataList=brca,groupList=groupList,
          makeNetFunc=makeNets,
          outDir=outDir, ## netDx requires absolute path
          numSplits=numSplits, featScoreMax=2L, featSelCutoff=1L,
           numCores=1L)
    )

    ## function(dataList, groupList, netDir,...) {
    ##  netList <- c() # initialize before is.null() check
    ##  # correlation-based similarity for mRNA, RPPA and methylation data
    ##  # (Pearson correlation)
    ##  for (nm in setdiff(names(groupList),"clinical")) {
    ##     # NOTE: the check for is.null() is important!
    ##      if (!is.null(groupList[[nm]])) {
    ##      netList <- makePSN_NamedMatrix(dataList[[nm]],
    ##                   rownames(dataList[[nm]]),
    ##                    groupList[[nm]],netDir,verbose=FALSE,
    ##                   writeProfiles=TRUE,...)
    ##      }
    ##  }
    ##
    ##  # make clinical nets (normalized difference)
    ##  netList2 <- c()
    ##  if (!is.null(groupList[["clinical"]])) {
    ##  netList2 <- makePSN_NamedMatrix(dataList$clinical,
    ##      rownames(dataList$clinical),
    ##      groupList[["clinical"]],netDir,
    ##      simMetric="custom",customFunc=normDiff, # custom function
    ##      writeProfiles=FALSE,
    ##      sparsify=TRUE,verbose=TRUE,...)
    ##  }
    ##  netList <- c(unlist(netList),unlist(netList2))
    ##  return(netList)
    ## }
    ##             IS_TRAIN
    ## STATUS       TRAIN TEST
    ##   Basal-like    77   20
    ##   Luminal_A    184   46
    ##   Luminal_B    101   26
    ##
    ## Luminal_A   nonpred      <NA>
    ##       184       178         0
    ##
    ## Basal-like    nonpred       <NA>
    ##         77        285          0
    ##
    ## Luminal_B   nonpred      <NA>
    ##       101       261         0

    ## Warning in sprintf("\tCreate & compile features", g): one argument not used by
    ## format ' Create & compile features'

    ## Warning in sprintf("\tCreate & compile features", g): one argument not used by
    ## format ' Create & compile features'

    ## Warning in sprintf("\tCreate & compile features", g): one argument not used by
    ## format ' Create & compile features'

    ##             IS_TRAIN
    ## STATUS       TRAIN TEST
    ##   Basal-like    77   20
    ##   Luminal_A    184   46
    ##   Luminal_B    101   26
    ##
    ## Luminal_A   nonpred      <NA>
    ##       184       178         0
    ##
    ## Basal-like    nonpred       <NA>
    ##         77        285          0
    ##
    ## Luminal_B   nonpred      <NA>
    ##       101       261         0

    ## Warning in sprintf("\tCreate & compile features", g): one argument not used by
    ## format ' Create & compile features'

    ## Warning in sprintf("\tCreate & compile features", g): one argument not used by
    ## format ' Create & compile features'

    ## Warning in sprintf("\tCreate & compile features", g): one argument not used by
    ## format ' Create & compile features'

# 7 Examine output

The results are stored in the list object returned by the `buildPredictor()` call. This list contains:

- `inputNets`: all input networks that the model started with.
- `Split<i>`: a list with results for each train-test split
  - `featureScores`: feature scores for each label (list with `g` entries, where `g` is number of patient labels). Each entry contains the feature selection scores for the corresponding label.
  - `featureSelected`: vector of features that pass feature selection. List of length `g`, with one entry per label.
  - `predictions`: real and predicted labels for test patients
  - `accuracy`: percent accuracy of predictions

    summary(out)

    ##           Length Class  Mode
    ## inputNets 14     -none- character
    ## Split1     4     -none- list
    ## Split2     4     -none- list

    summary(out$Split1)

    ##                 Length Class      Mode
    ## featureScores      3   -none-     list
    ## featureSelected    3   -none-     list
    ## predictions     2693   data.frame list
    ## accuracy           1   -none-     numeric

Compute accuracy for three-way classificationL

    # Average accuracy
    st <- unique(colData(brca)$STATUS)
    acc <- matrix(NA,ncol=length(st),nrow=numSplits)
    colnames(acc) <- st
    for (k in 1:numSplits) {
        pred <- out[[sprintf("Split%i",k)]][["predictions"]];
        tmp <- pred[,c("ID","STATUS","TT_STATUS","PRED_CLASS",
                         sprintf("%s_SCORE",st))]
        for (m in 1:length(st)) {
           tmp2 <- subset(tmp, STATUS==st[m])
           acc[k,m] <- sum(tmp2$PRED==tmp2$STATUS)/nrow(tmp2)
        }
    }
    print(round(acc*100,2))

    ##      Luminal_A Basal-like Luminal_B
    ## [1,]     46.43        100     35.71
    ## [2,]     44.83        100     55.00

Also, examine the confusion matrix. We can see that the model perfectly classifies basal tumours, but performs poorly in distinguishing between the two types of luminal tumours.

    res <- out$Split1$predictions
    print(table(res[,c("STATUS","PRED_CLASS")]))

    ##             PRED_CLASS
    ## STATUS       Basal-like Luminal_A Luminal_B
    ##   Basal-like         14         0         0
    ##   Luminal_A           5        13        10
    ##   Luminal_B           4         5         5

# 8 sessionInfo

    sessionInfo()

    ## R version 4.1.0 (2021-05-18)
    ## Platform: x86_64-pc-linux-gnu (64-bit)
    ## Running under: Ubuntu 20.04.2 LTS
    ##
    ## Matrix products: default
    ## BLAS:   /home/biocbuild/bbs-3.13-bioc/R/lib/libRblas.so
    ## LAPACK: /home/biocbuild/bbs-3.13-bioc/R/lib/libRlapack.so
    ##
    ## locale:
    ##  [1] LC_CTYPE=C                 LC_NUMERIC=C
    ##  [3] LC_TIME=C                  LC_COLLATE=C
    ##  [5] LC_MONETARY=C              LC_MESSAGES=en_US.UTF-8
    ##  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C
    ##  [9] LC_ADDRESS=C               LC_TELEPHONE=C
    ## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
    ##
    ## attached base packages:
    ## [1] stats4    parallel  stats     graphics  grDevices utils     datasets
    ## [8] methods   base
    ##
    ## other attached packages:
    ##  [1] rhdf5_2.36.0                BiocFileCache_2.0.0
    ##  [3] dbplyr_2.1.1                curatedTCGAData_1.13.9
    ##  [5] MultiAssayExperiment_1.18.0 SummarizedExperiment_1.22.0
    ##  [7] GenomicRanges_1.44.0        GenomeInfoDb_1.28.0
    ##  [9] IRanges_2.26.0              S4Vectors_0.30.0
    ## [11] MatrixGenerics_1.4.0        matrixStats_0.58.0
    ## [13] netDx_1.4.0                 bigmemory_4.5.36
    ## [15] Biobase_2.52.0              BiocGenerics_0.38.0
    ## [17] BiocStyle_2.20.0
    ##
    ## loaded via a namespace (and not attached):
    ##   [1] utf8_1.2.1                    R.utils_2.10.1
    ##   [3] tidyselect_1.1.1              RSQLite_2.2.7
    ##   [5] AnnotationDbi_1.54.0          grid_4.1.0
    ##   [7] combinat_0.0-8                BiocParallel_1.26.0
    ##   [9] Rtsne_0.15                    RNeXML_2.4.5
    ##  [11] munsell_0.5.0                 ScaledMatrix_1.0.0
    ##  [13] base64url_1.4                 codetools_0.2-18
    ##  [15] pbdZMQ_0.3-5                  withr_2.4.2
    ##  [17] colorspace_2.0-1              filelock_1.0.2
    ##  [19] highr_0.9                     knitr_1.33
    ##  [21] dplR_1.7.2                    uuid_0.1-4
    ##  [23] zinbwave_1.14.0               SingleCellExperiment_1.14.0
    ##  [25] ROCR_1.0-11                   NMF_0.23.0
    ##  [27] labeling_0.4.2                repr_1.1.3
    ##  [29] GenomeInfoDbData_1.2.6        farver_2.1.0
    ##  [31] bit64_4.0.5                   vctrs_0.3.8
    ##  [33] generics_0.1.0                xfun_0.23
    ##  [35] R6_2.5.0                      doParallel_1.0.16
    ##  [37] ggbeeswarm_0.6.0              netSmooth_1.12.0
    ##  [39] rsvd_1.0.5                    RJSONIO_1.3-1.4
    ##  [41] locfit_1.5-9.4                bitops_1.0-7
    ##  [43] rhdf5filters_1.4.0            cachem_1.0.5
    ##  [45] DelayedArray_0.18.0           assertthat_0.2.1
    ##  [47] promises_1.2.0.1              scales_1.1.1
    ##  [49] beeswarm_0.3.1                gtable_0.3.0
    ##  [51] phylobase_0.8.10              beachmat_2.8.0
    ##  [53] rlang_0.4.11                  genefilter_1.74.0
    ##  [55] splines_4.1.0                 lazyeval_0.2.2
    ##  [57] BiocManager_1.30.15           yaml_2.2.1
    ##  [59] reshape2_1.4.4                backports_1.2.1
    ##  [61] httpuv_1.6.1                  tools_4.1.0
    ##  [63] bookdown_0.22                 gridBase_0.4-7
    ##  [65] ggplot2_3.3.3                 ellipsis_0.3.2
    ##  [67] jquerylib_0.1.4               RColorBrewer_1.1-2
    ##  [69] Rcpp_1.0.6                    plyr_1.8.6
    ##  [71] base64enc_0.1-3               sparseMatrixStats_1.4.0
    ##  [73] progress_1.2.2                zlibbioc_1.38.0
    ##  [75] purrr_0.3.4                   RCurl_1.98-1.3
    ##  [77] prettyunits_1.1.1             viridis_0.6.1
    ##  [79] cluster_2.1.2                 magrittr_2.0.1
    ##  [81] magick_2.7.2                  data.table_1.14.0
    ##  [83] mime_0.10                     hms_1.1.0
    ##  [85] evaluate_0.14                 xtable_1.8-4
    ##  [87] XML_3.99-0.6                  gridExtra_2.3
    ##  [89] shape_1.4.6                   compiler_4.1.0
    ##  [91] scater_1.20.0                 tibble_3.1.2
    ##  [93] RCy3_2.12.0                   crayon_1.4.1
    ##  [95] R.oo_1.24.0                   htmltools_0.5.1.1
    ##  [97] entropy_1.3.0                 later_1.2.0
    ##  [99] tidyr_1.1.3                   howmany_0.3-1
    ## [101] DBI_1.1.1                     ExperimentHub_2.0.0
    ## [103] MASS_7.3-54                   rappdirs_0.3.3
    ## [105] Matrix_1.3-3                  ade4_1.7-16
    ## [107] uchardet_1.1.0                R.methodsS3_1.8.1
    ## [109] igraph_1.2.6                  pkgconfig_2.0.3
    ## [111] bigmemory.sri_0.1.3           rncl_0.8.4
    ## [113] registry_0.5-1                locfdr_1.1-8
    ## [115] signal_0.7-6                  IRdisplay_1.0
    ## [117] scuttle_1.2.0                 xml2_1.3.2
    ## [119] foreach_1.5.1                 annotate_1.70.0
    ## [121] vipor_0.4.5                   bslib_0.2.5.1
    ## [123] rngtools_1.5                  pkgmaker_0.32.2
    ## [125] XVector_0.32.0                stringr_1.4.0
    ## [127] digest_0.6.27                 pracma_2.3.3
    ## [129] graph_1.70.0                  softImpute_1.4-1
    ## [131] Biostrings_2.60.0             rmarkdown_2.8
    ## [133] edgeR_3.34.0                  DelayedMatrixStats_1.14.0
    ## [135] curl_4.3.1                    kernlab_0.9-29
    ## [137] shiny_1.6.0                   lifecycle_1.0.0
    ## [139] nlme_3.1-152                  jsonlite_1.7.2
    ## [141] clusterExperiment_2.12.0      Rhdf5lib_1.14.0
    ## [143] BiocNeighbors_1.10.0          viridisLite_0.4.0
    ## [145] limma_3.48.0                  fansi_0.4.2
    ## [147] pillar_1.6.1                  lattice_0.20-44
    ## [149] KEGGREST_1.32.0               fastmap_1.1.0
    ## [151] httr_1.4.2                    survival_3.2-11
    ## [153] interactiveDisplayBase_1.30.0 glue_1.4.2
    ## [155] png_0.1-7                     iterators_1.0.13
    ## [157] BiocVersion_3.13.1            glmnet_4.1-1
    ## [159] bit_4.0.4                     stringi_1.6.2
    ## [161] sass_0.4.0                    HDF5Array_1.20.0
    ## [163] blob_1.2.1                    AnnotationHub_3.0.0
    ## [165] BiocSingular_1.8.0            memoise_2.0.0
    ## [167] IRkernel_1.2                  dplyr_1.0.6
    ## [169] irlba_2.3.3                   ape_5.5