# Human pancreas (multiple technologies) {#merged-pancreas} ## Introduction For a period in 2016, there was a great deal of interest in using scRNA-seq to profile the human pancreas at cellular resolution [@muraro2016singlecell;@grun2016denovo;@lawlor2017singlecell;@segerstolpe2016singlecell]. As a consequence, we have a surplus of human pancreas datasets generated by different authors with different technologies, which provides an ideal use case for demonstrating more complex data integration strategies. This represents a more challenging application than the PBMC dataset in Chapter \@ref(integrating-datasets) as it involves different sequencing protocols and different patients, most likely with differences in cell type composition. ## The good We start by considering only two datasets from @muraro2016singlecell and @grun2016denovo. This is a relatively simple scenario involving very similar protocols (CEL-seq and CEL-seq2) and a similar set of authors.
```r #--- loading ---# library(scRNAseq) sce.grun <- GrunPancreasData() #--- gene-annotation ---# library(org.Hs.eg.db) gene.ids <- mapIds(org.Hs.eg.db, keys=rowData(sce.grun)$symbol, keytype="SYMBOL", column="ENSEMBL") keep <- !is.na(gene.ids) & !duplicated(gene.ids) sce.grun <- sce.grun[keep,] rownames(sce.grun) <- gene.ids[keep] #--- quality-control ---# library(scater) stats <- perCellQCMetrics(sce.grun) qc <- quickPerCellQC(stats, percent_subsets="altexps_ERCC_percent", batch=sce.grun$donor, subset=sce.grun$donor %in% c("D17", "D7", "D2")) sce.grun <- sce.grun[,!qc$discard] #--- normalization ---# library(scran) set.seed(1000) # for irlba. clusters <- quickCluster(sce.grun) sce.grun <- computeSumFactors(sce.grun, clusters=clusters) sce.grun <- logNormCounts(sce.grun) #--- variance-modelling ---# block <- paste0(sce.grun$sample, "_", sce.grun$donor) dec.grun <- modelGeneVarWithSpikes(sce.grun, spikes="ERCC", block=block) top.grun <- getTopHVGs(dec.grun, prop=0.1) ```
```r sce.grun ``` ``` ## class: SingleCellExperiment ## dim: 17398 1063 ## metadata(0): ## assays(2): counts logcounts ## rownames(17398): ENSG00000268895 ENSG00000121410 ... ENSG00000074755 ## ENSG00000036549 ## rowData names(2): symbol chr ## colnames(1063): D2ex_1 D2ex_2 ... D17TGFB_94 D17TGFB_95 ## colData names(3): donor sample sizeFactor ## reducedDimNames(0): ## mainExpName: endogenous ## altExpNames(1): ERCC ```
```r #--- loading ---# library(scRNAseq) sce.muraro <- MuraroPancreasData() #--- gene-annotation ---# library(AnnotationHub) edb <- AnnotationHub()[["AH73881"]] gene.symb <- sub("__chr.*$", "", rownames(sce.muraro)) gene.ids <- mapIds(edb, keys=gene.symb, keytype="SYMBOL", column="GENEID") # Removing duplicated genes or genes without Ensembl IDs. keep <- !is.na(gene.ids) & !duplicated(gene.ids) sce.muraro <- sce.muraro[keep,] rownames(sce.muraro) <- gene.ids[keep] #--- quality-control ---# library(scater) stats <- perCellQCMetrics(sce.muraro) qc <- quickPerCellQC(stats, percent_subsets="altexps_ERCC_percent", batch=sce.muraro$donor, subset=sce.muraro$donor!="D28") sce.muraro <- sce.muraro[,!qc$discard] #--- normalization ---# library(scran) set.seed(1000) clusters <- quickCluster(sce.muraro) sce.muraro <- computeSumFactors(sce.muraro, clusters=clusters) sce.muraro <- logNormCounts(sce.muraro) #--- variance-modelling ---# block <- paste0(sce.muraro$plate, "_", sce.muraro$donor) dec.muraro <- modelGeneVarWithSpikes(sce.muraro, "ERCC", block=block) top.muraro <- getTopHVGs(dec.muraro, prop=0.1) ```
```r sce.muraro ``` ``` ## class: SingleCellExperiment ## dim: 16940 2299 ## metadata(0): ## assays(2): counts logcounts ## rownames(16940): ENSG00000268895 ENSG00000121410 ... ENSG00000159840 ## ENSG00000074755 ## rowData names(2): symbol chr ## colnames(2299): D28-1_1 D28-1_2 ... D30-8_93 D30-8_94 ## colData names(4): label donor plate sizeFactor ## reducedDimNames(0): ## mainExpName: endogenous ## altExpNames(1): ERCC ``` We subset both batches to their common universe of genes; adjust their scaling to equalize sequencing coverage (not really necessary in this case, as the coverage is already similar, but we will do so anyway for consistency); and select those genes with positive average biological components for further use. ```r universe <- intersect(rownames(sce.grun), rownames(sce.muraro)) sce.grun2 <- sce.grun[universe,] dec.grun2 <- dec.grun[universe,] sce.muraro2 <- sce.muraro[universe,] dec.muraro2 <- dec.muraro[universe,] library(batchelor) normed.pancreas <- multiBatchNorm(sce.grun2, sce.muraro2) sce.grun2 <- normed.pancreas[[1]] sce.muraro2 <- normed.pancreas[[2]] library(scran) combined.pan <- combineVar(dec.grun2, dec.muraro2) chosen.genes <- rownames(combined.pan)[combined.pan$bio > 0] ``` We observe that `rescaleBatches()` is unable to align cells from different batches in Figure \@ref(fig:tsne-pancreas-rescaled). This is attributable to differences in population composition between batches, with additional complications from non-linearities in the batch effect, e.g., when the magnitude or direction of the batch effect differs between cell types. ```r library(scater) rescaled.pancreas <- rescaleBatches(sce.grun2, sce.muraro2) set.seed(100101) rescaled.pancreas <- runPCA(rescaled.pancreas, subset_row=chosen.genes, exprs_values="corrected") rescaled.pancreas <- runTSNE(rescaled.pancreas, dimred="PCA") plotTSNE(rescaled.pancreas, colour_by="batch") ```
$t$-SNE plot of the two pancreas datasets after correction with `rescaleBatches()`. Each point represents a cell and is colored according to the batch of origin.

(\#fig:tsne-pancreas-rescaled)$t$-SNE plot of the two pancreas datasets after correction with `rescaleBatches()`. Each point represents a cell and is colored according to the batch of origin.

Here, we use `fastMNN()` to merge together the two human pancreas datasets described earlier. Clustering on the merged datasets yields fewer batch-specific clusters, which is recapitulated as greater intermingling between batches in Figure \@ref(fig:tsne-pancreas-mnn). This improvement over Figure \@ref(fig:tsne-pancreas-rescaled) represents the ability of `fastMNN()` to adapt to more complex situations involving differences in population composition between batches. ```r set.seed(1011011) mnn.pancreas <- fastMNN(sce.grun2, sce.muraro2, subset.row=chosen.genes) snn.gr <- buildSNNGraph(mnn.pancreas, use.dimred="corrected") clusters <- igraph::cluster_walktrap(snn.gr)$membership tab <- table(Cluster=clusters, Batch=mnn.pancreas$batch) tab ``` ``` ## Batch ## Cluster 1 2 ## 1 242 280 ## 2 304 250 ## 3 205 846 ## 4 37 2 ## 5 56 194 ## 6 24 108 ## 7 119 399 ## 8 53 71 ## 9 18 113 ## 10 0 17 ## 11 5 19 ``` ```r mnn.pancreas <- runTSNE(mnn.pancreas, dimred="corrected") plotTSNE(mnn.pancreas, colour_by="batch") ```
$t$-SNE plot of the two pancreas datasets after correction with `fastMNN()`. Each point represents a cell and is colored according to the batch of origin.

(\#fig:tsne-pancreas-mnn)$t$-SNE plot of the two pancreas datasets after correction with `fastMNN()`. Each point represents a cell and is colored according to the batch of origin.

## The bad Flushed with our previous success, we now attempt to merge the other datasets from @lawlor2017singlecell and @segerstolpe2016singlecell. This is a more challenging task as it involves different technologies, mixtures of UMI and read count data and a more diverse set of authors (presumably with greater differences in the patient population).
```r #--- loading ---# library(scRNAseq) sce.lawlor <- LawlorPancreasData() #--- gene-annotation ---# library(AnnotationHub) edb <- AnnotationHub()[["AH73881"]] anno <- select(edb, keys=rownames(sce.lawlor), keytype="GENEID", columns=c("SYMBOL", "SEQNAME")) rowData(sce.lawlor) <- anno[match(rownames(sce.lawlor), anno[,1]),-1] #--- quality-control ---# library(scater) stats <- perCellQCMetrics(sce.lawlor, subsets=list(Mito=which(rowData(sce.lawlor)$SEQNAME=="MT"))) qc <- quickPerCellQC(stats, percent_subsets="subsets_Mito_percent", batch=sce.lawlor$`islet unos id`) sce.lawlor <- sce.lawlor[,!qc$discard] #--- normalization ---# library(scran) set.seed(1000) clusters <- quickCluster(sce.lawlor) sce.lawlor <- computeSumFactors(sce.lawlor, clusters=clusters) sce.lawlor <- logNormCounts(sce.lawlor) #--- variance-modelling ---# dec.lawlor <- modelGeneVar(sce.lawlor, block=sce.lawlor$`islet unos id`) chosen.genes <- getTopHVGs(dec.lawlor, n=2000) ```
```r sce.lawlor ``` ``` ## class: SingleCellExperiment ## dim: 26616 604 ## metadata(0): ## assays(2): counts logcounts ## rownames(26616): ENSG00000229483 ENSG00000232849 ... ENSG00000251576 ## ENSG00000082898 ## rowData names(2): SYMBOL SEQNAME ## colnames(604): 10th_C11_S96 10th_C13_S61 ... 9th-C96_S81 9th-C9_S13 ## colData names(9): title age ... Sex sizeFactor ## reducedDimNames(0): ## mainExpName: NULL ## altExpNames(0): ```
```r #--- loading ---# library(scRNAseq) sce.seger <- SegerstolpePancreasData() #--- gene-annotation ---# library(AnnotationHub) edb <- AnnotationHub()[["AH73881"]] symbols <- rowData(sce.seger)$symbol ens.id <- mapIds(edb, keys=symbols, keytype="SYMBOL", column="GENEID") ens.id <- ifelse(is.na(ens.id), symbols, ens.id) # Removing duplicated rows. keep <- !duplicated(ens.id) sce.seger <- sce.seger[keep,] rownames(sce.seger) <- ens.id[keep] #--- sample-annotation ---# emtab.meta <- colData(sce.seger)[,c("cell type", "disease", "individual", "single cell well quality")] colnames(emtab.meta) <- c("CellType", "Disease", "Donor", "Quality") colData(sce.seger) <- emtab.meta sce.seger$CellType <- gsub(" cell", "", sce.seger$CellType) sce.seger$CellType <- paste0( toupper(substr(sce.seger$CellType, 1, 1)), substring(sce.seger$CellType, 2)) #--- quality-control ---# low.qual <- sce.seger$Quality == "low quality cell" library(scater) stats <- perCellQCMetrics(sce.seger) qc <- quickPerCellQC(stats, percent_subsets="altexps_ERCC_percent", batch=sce.seger$Donor, subset=!sce.seger$Donor %in% c("HP1504901", "HP1509101")) sce.seger <- sce.seger[,!(qc$discard | low.qual)] #--- normalization ---# library(scran) clusters <- quickCluster(sce.seger) sce.seger <- computeSumFactors(sce.seger, clusters=clusters) sce.seger <- logNormCounts(sce.seger) #--- variance-modelling ---# for.hvg <- sce.seger[,librarySizeFactors(altExp(sce.seger)) > 0 & sce.seger$Donor!="AZ"] dec.seger <- modelGeneVarWithSpikes(for.hvg, "ERCC", block=for.hvg$Donor) chosen.hvgs <- getTopHVGs(dec.seger, n=2000) ```
```r sce.seger ``` ``` ## class: SingleCellExperiment ## dim: 25454 2090 ## metadata(0): ## assays(2): counts logcounts ## rownames(25454): ENSG00000118473 ENSG00000142920 ... ENSG00000278306 ## eGFP ## rowData names(2): symbol refseq ## colnames(2090): HP1502401_H13 HP1502401_J14 ... HP1526901T2D_N8 ## HP1526901T2D_A8 ## colData names(5): CellType Disease Donor Quality sizeFactor ## reducedDimNames(0): ## mainExpName: endogenous ## altExpNames(1): ERCC ``` We perform the usual routine to obtain re-normalized values and a set of HVGs. Here, we put all the objects into a list to avoid having to explicitly type their names separately. ```r all.sce <- list(Grun=sce.grun, Muraro=sce.muraro, Lawlor=sce.lawlor, Seger=sce.seger) all.dec <- list(Grun=dec.grun, Muraro=dec.muraro, Lawlor=dec.lawlor, Seger=dec.seger) universe <- Reduce(intersect, lapply(all.sce, rownames)) all.sce <- lapply(all.sce, "[", i=universe,) all.dec <- lapply(all.dec, "[", i=universe,) normed.pancreas <- do.call(multiBatchNorm, all.sce) combined.pan <- do.call(combineVar, all.dec) chosen.genes <- rownames(combined.pan)[combined.pan$bio > 0] ``` We observe that the merge is generally successful, with many clusters containing contributions from each batch (Figure \@ref(fig:tsne-pancreas-mnn-many)). There are few clusters that are specific to the Segerstolpe dataset, and if we were naive, we might consider them to represent interesting subpopulations that are not present in the other datasets. ```r set.seed(1011110) mnn.pancreas <- fastMNN(normed.pancreas) # Bumping up 'k' to get broader clusters for this demonstration. snn.gr <- buildSNNGraph(mnn.pancreas, use.dimred="corrected", k=20) clusters <- igraph::cluster_walktrap(snn.gr)$membership clusters <- factor(clusters) tab <- table(Cluster=clusters, Batch=mnn.pancreas$batch) tab ``` ``` ## Batch ## Cluster Grun Lawlor Muraro Seger ## 1 304 28 256 384 ## 2 54 12 599 196 ## 3 0 0 0 238 ## 4 106 244 390 176 ## 5 226 17 250 152 ## 6 35 0 1 0 ## 7 0 0 0 47 ## 8 146 212 234 121 ## 9 56 17 196 108 ## 10 67 24 83 18 ## 11 0 0 0 50 ## 12 24 18 107 55 ## 13 0 1 16 4 ## 14 19 18 118 157 ## 15 0 0 0 193 ## 16 0 0 0 26 ## 17 0 0 0 112 ## 18 21 5 30 36 ## 19 5 8 19 17 ``` ```r mnn.pancreas <- runTSNE(mnn.pancreas, dimred="corrected") gridExtra::grid.arrange( plotTSNE(mnn.pancreas, colour_by="batch", text_by=I(clusters)), plotTSNE(mnn.pancreas, colour_by=I(clusters), text_by=I(clusters)), ncol=2 ) ```
$t$-SNE plots of the four pancreas datasets after correction with `fastMNN()`. Each point represents a cell and is colored according to the batch of origin (left) or the assigned cluster (right). The cluster label is shown at the median location across all cells in the cluster.

(\#fig:tsne-pancreas-mnn-many)$t$-SNE plots of the four pancreas datasets after correction with `fastMNN()`. Each point represents a cell and is colored according to the batch of origin (left) or the assigned cluster (right). The cluster label is shown at the median location across all cells in the cluster.

Fortunately, we are battle-hardened and cynical, so we are sure to check for other sources of variation. The most obvious candidate is the donor of origin for each cell (Figure \@ref(fig:tsne-pancreas-mnn-donor)), which correlates strongly to these Segerstolpe-only clusters. This is not surprising given the large differences between humans in the wild, but donor-level variation is not interesting for the purposes of cell type characterization. (That said, preservation of the within-dataset donor effects is the technically correct course of action here, as a batch correction method should try to avoid removing heterogeneity within each of its defined batches.) ```r donors <- c( normed.pancreas$Grun$donor, normed.pancreas$Muraro$donor, normed.pancreas$Lawlor$`islet unos id`, normed.pancreas$Seger$Donor ) seger.donors <- donors seger.donors[mnn.pancreas$batch!="Seger"] <- NA plotTSNE(mnn.pancreas, colour_by=I(seger.donors)) ```
$t$-SNE plots of the four pancreas datasets after correction with `fastMNN()`. Each point represents a cell and is colored according to the donor of origin for the Segerstolpe dataset.

(\#fig:tsne-pancreas-mnn-donor)$t$-SNE plots of the four pancreas datasets after correction with `fastMNN()`. Each point represents a cell and is colored according to the donor of origin for the Segerstolpe dataset.

## The ugly Given these results, the most prudent course of action is to remove the donor effects within each dataset in addition to the batch effects across datasets. This involves a bit more work to properly specify the two levels of unwanted heterogeneity. To make our job a bit easier, we use the `noCorrect()` utility to combine all batches into a single `SingleCellExperiment` object. ```r combined <- noCorrect(normed.pancreas) assayNames(combined) <- "logcounts" combined$donor <- donors ``` We then call `fastMNN()` on the combined object with our chosen HVGs, using the `batch=` argument to specify which cells belong to which donors. This will progressively merge cells from each donor in each batch until all cells are mapped onto a common coordinate space. For some extra sophistication, we also set the `weights=` argument to ensure that each batch contributes equally to the PCA, regardless of the number of donors present in that batch; see `?multiBatchPCA` for more details. ```r donors.per.batch <- split(combined$donor, combined$batch) donors.per.batch <- lapply(donors.per.batch, unique) donors.per.batch ``` ``` ## $Grun ## [1] "D2" "D3" "D7" "D10" "D17" ## ## $Lawlor ## [1] "ACIW009" "ACJV399" "ACCG268" "ACCR015A" "ACEK420A" "ACEL337" "ACHY057" ## [8] "ACIB065" ## ## $Muraro ## [1] "D28" "D29" "D31" "D30" ## ## $Seger ## [1] "HP1502401" "HP1504101T2D" "AZ" "HP1508501T2D" "HP1506401" ## [6] "HP1507101" "HP1509101" "HP1504901" "HP1525301T2D" "HP1526901T2D" ``` ```r set.seed(1010100) multiout <- fastMNN(combined, batch=combined$donor, subset.row=chosen.genes, weights=donors.per.batch) # Renaming metadata fields for easier communication later. multiout$dataset <- combined$batch multiout$donor <- multiout$batch multiout$batch <- NULL ``` With this approach, we see that the Segerstolpe-only clusters have disappeared (Figure \@ref(fig:tsne-pancreas-mnn-donor-all)). Visually, there also seems to be much greater mixing between cells from different Segerstolpe donors. This suggests that we have removed most of the donor effect, which simplifies the interpretation of our clusters. ```r library(scater) g <- buildSNNGraph(multiout, use.dimred=1, k=20) clusters <- igraph::cluster_walktrap(g)$membership tab <- table(clusters, multiout$dataset) tab ``` ``` ## ## clusters Grun Lawlor Muraro Seger ## 1 248 20 278 186 ## 2 338 27 258 388 ## 3 171 251 458 270 ## 4 201 242 851 887 ## 5 57 17 193 108 ## 6 24 18 108 55 ## 7 5 9 19 17 ## 8 0 1 17 4 ## 9 19 19 117 175 ``` ```r multiout <- runTSNE(multiout, dimred="corrected") gridExtra::grid.arrange( plotTSNE(multiout, colour_by="dataset", text_by=I(clusters)), plotTSNE(multiout, colour_by=I(seger.donors)), ncol=2 ) ```
$t$-SNE plots of the four pancreas datasets after donor-level correction with `fastMNN()`. Each point represents a cell and is colored according to the batch of origin (left) or the donor of origin for the Segerstolpe-derived cells (right). The cluster label is shown at the median location across all cells in the cluster.

(\#fig:tsne-pancreas-mnn-donor-all)$t$-SNE plots of the four pancreas datasets after donor-level correction with `fastMNN()`. Each point represents a cell and is colored according to the batch of origin (left) or the donor of origin for the Segerstolpe-derived cells (right). The cluster label is shown at the median location across all cells in the cluster.

Our clusters compare well to the published annotations, indicating that we did not inadvertently discard important factors of variation during correction. (Though in this case, the cell types are so well defined, it would be quite a feat to fail to separate them!) ```r proposed <- c(rep(NA, ncol(sce.grun)), sce.muraro$label, sce.lawlor$`cell type`, sce.seger$CellType) proposed <- tolower(proposed) proposed[proposed=="gamma/pp"] <- "gamma" proposed[proposed=="pp"] <- "gamma" proposed[proposed=="duct"] <- "ductal" proposed[proposed=="psc"] <- "stellate" table(proposed, clusters) ``` ``` ## clusters ## proposed 1 2 3 4 5 6 7 8 9 ## acinar 421 2 0 1 0 0 1 0 0 ## alpha 1 7 6 1869 1 0 1 0 2 ## beta 3 4 918 4 0 2 1 1 7 ## co-expression 0 0 17 22 0 0 0 0 0 ## delta 0 2 5 1 306 0 1 0 1 ## ductal 6 613 4 0 0 6 0 10 1 ## endothelial 0 0 0 0 0 1 33 0 0 ## epsilon 0 0 1 1 0 0 0 0 6 ## gamma 2 0 0 0 0 0 0 0 280 ## mesenchymal 0 1 0 0 0 79 0 0 0 ## mhc class ii 0 0 0 0 0 0 0 4 0 ## nana 2 8 1 13 1 0 1 0 0 ## none/other 0 3 1 2 0 0 4 1 1 ## stellate 0 0 0 0 0 71 1 0 0 ## unclassified 0 0 0 0 0 2 0 0 0 ## unclassified endocrine 0 0 3 3 0 0 0 0 0 ## unclear 0 4 0 0 0 0 0 0 0 ``` ## Session Info {-}
``` R version 4.1.0 beta (2021-05-03 r80259) Platform: x86_64-pc-linux-gnu (64-bit) Running under: Ubuntu 20.04.2 LTS Matrix products: default BLAS: /home/biocbuild/bbs-3.14-bioc/R/lib/libRblas.so LAPACK: /home/biocbuild/bbs-3.14-bioc/R/lib/libRlapack.so locale: [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C [3] LC_TIME=en_GB LC_COLLATE=C [5] LC_MONETARY=en_US.UTF-8 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] parallel stats4 stats graphics grDevices utils datasets [8] methods base other attached packages: [1] bluster_1.3.0 scater_1.21.0 [3] ggplot2_3.3.3 scran_1.21.1 [5] scuttle_1.3.0 batchelor_1.9.0 [7] SingleCellExperiment_1.15.1 SummarizedExperiment_1.23.0 [9] Biobase_2.53.0 GenomicRanges_1.45.0 [11] GenomeInfoDb_1.29.0 IRanges_2.27.0 [13] S4Vectors_0.31.0 BiocGenerics_0.39.0 [15] MatrixGenerics_1.5.0 matrixStats_0.58.0 [17] BiocStyle_2.21.0 rebook_1.3.0 loaded via a namespace (and not attached): [1] bitops_1.0-7 filelock_1.0.2 [3] tools_4.1.0 bslib_0.2.5.1 [5] utf8_1.2.1 R6_2.5.0 [7] irlba_2.3.3 ResidualMatrix_1.3.0 [9] vipor_0.4.5 DBI_1.1.1 [11] colorspace_2.0-1 withr_2.4.2 [13] gridExtra_2.3 tidyselect_1.1.1 [15] compiler_4.1.0 graph_1.71.0 [17] BiocNeighbors_1.11.0 DelayedArray_0.19.0 [19] labeling_0.4.2 bookdown_0.22 [21] sass_0.4.0 scales_1.1.1 [23] rappdirs_0.3.3 stringr_1.4.0 [25] digest_0.6.27 rmarkdown_2.8 [27] XVector_0.33.0 pkgconfig_2.0.3 [29] htmltools_0.5.1.1 sparseMatrixStats_1.5.0 [31] highr_0.9 limma_3.49.0 [33] rlang_0.4.11 DelayedMatrixStats_1.15.0 [35] farver_2.1.0 jquerylib_0.1.4 [37] generics_0.1.0 jsonlite_1.7.2 [39] BiocParallel_1.27.0 dplyr_1.0.6 [41] RCurl_1.98-1.3 magrittr_2.0.1 [43] BiocSingular_1.9.0 GenomeInfoDbData_1.2.6 [45] Matrix_1.3-3 ggbeeswarm_0.6.0 [47] Rcpp_1.0.6 munsell_0.5.0 [49] fansi_0.4.2 viridis_0.6.1 [51] lifecycle_1.0.0 stringi_1.6.2 [53] yaml_2.2.1 edgeR_3.35.0 [55] zlibbioc_1.39.0 Rtsne_0.15 [57] grid_4.1.0 dqrng_0.3.0 [59] crayon_1.4.1 dir.expiry_1.1.0 [61] lattice_0.20-44 cowplot_1.1.1 [63] beachmat_2.9.0 locfit_1.5-9.4 [65] CodeDepends_0.6.5 metapod_1.1.0 [67] knitr_1.33 pillar_1.6.1 [69] igraph_1.2.6 codetools_0.2-18 [71] ScaledMatrix_1.1.0 XML_3.99-0.6 [73] glue_1.4.2 evaluate_0.14 [75] BiocManager_1.30.15 vctrs_0.3.8 [77] purrr_0.3.4 gtable_0.3.0 [79] assertthat_0.2.1 xfun_0.23 [81] rsvd_1.0.5 viridisLite_0.4.0 [83] tibble_3.1.2 beeswarm_0.3.1 [85] cluster_2.1.2 statmod_1.4.36 [87] ellipsis_0.3.2 ```