Use of the SpatialDecon package in estimating and exploring mixed cell abundance in spatially-resolved gene expression data

Installation


if(!requireNamespace("BiocManager", quietly = TRUE))
    install.packages("BiocManager")
BiocManager::install("SpatialDecon")

Overview

This vignette demonstrates the use of the SpatialDecon package to estimate cell abundance in spatial gene expression studies.

We’ll analyze a small GeoMx dataset from a lung tumor, looking for abundance of different immune cell types. This dataset has 30 ROIs. In each ROI, Tumor and Microenvironment segments have been profiled separately.

Data preparation

First, we load the package:

library(SpatialDecon)

Now let’s load our example data and examine it:

data("mini_geomx_dataset")
norm = mini_geomx_dataset$normalized
raw = mini_geomx_dataset$raw
annot = mini_geomx_dataset$annot
dim(raw)
#> [1] 545  30
head(annot)
#>                                       ROI AOI.name    x    y nuclei
#> ICP20th.L11.ICPKilo.ROI10.TME.B09   ROI10      TME 5400 8000    879
#> ICP20th.L11.ICPKilo.ROI10.Tumor.B08 ROI10    Tumor 5400 8000    555
#> ICP20th.L11.ICPKilo.ROI11.TME.B11   ROI11      TME 6000 8000    631
#> ICP20th.L11.ICPKilo.ROI11.Tumor.B10 ROI11    Tumor 6000 8000    569
#> ICP20th.L11.ICPKilo.ROI12.TME.C01   ROI12      TME 6600 8000    703
#> ICP20th.L11.ICPKilo.ROI12.Tumor.B12 ROI12    Tumor 6600 8000    667
raw[seq_len(5), seq_len(5)]
#>        ICP20th.L11.ICPKilo.ROI10.TME.B09 ICP20th.L11.ICPKilo.ROI10.Tumor.B08
#> A2M                                   76                                  20
#> ABCB1                                  9                                  15
#> ACP5                                 115                                  41
#> ADAM12                                12                                  12
#> ADORA3                                14                                   8
#>        ICP20th.L11.ICPKilo.ROI11.TME.B11 ICP20th.L11.ICPKilo.ROI11.Tumor.B10
#> A2M                                  104                                  22
#> ABCB1                                  7                                  10
#> ACP5                                 176                                  56
#> ADAM12                                10                                  11
#> ADORA3                                10                                  13
#>        ICP20th.L11.ICPKilo.ROI12.TME.C01
#> A2M                                   91
#> ABCB1                                 11
#> ACP5                                 120
#> ADAM12                                13
#> ADORA3                                11

# better segment names:
colnames(norm) = colnames(raw) = rownames(annot) = 
  paste0(annot$ROI, annot$AOI.name)

The spatialdecon function takes 3 arguments of expression data:

  1. The normalized data.
  2. A matrix of expected background for all data points in the normalized data matrix.
  3. Optionally, either a matrix of per-data-point weights, or the raw data, which is used to derive weights (low counts are less statistically stable, and this allows spatialdecon to down-weight them.)

We estimate each data point’s expected background from the negative control probes from its corresponding observation:

# use the NegProbe to estimate per-observation background
per.observation.mean.neg = norm["NegProbe", ]
# and define a background matrix in which each column (observation) is the
# appropriate value of per-observation background:
bg = sweep(norm * 0, 2, per.observation.mean.neg, "+")
dim(bg)
#> [1] 545  30

A note for background estimation: in studies with two probesets, the genes from each probeset will have distinct background values, and the above code should be run separately for each probeset using its corresponding NegProbe value. Alternatively, the “derive_GeoMx_background” can do this automatically:

bg2 = derive_GeoMx_background(norm = norm,
                             probepool = rep(1, nrow(norm)),
                             negnames = "NegProbe")

Cell profile matrices

A “cell profile matrix” is a pre-defined matrix that specifies the expected expression profiles of each cell type in the experiment. The SpatialDecon library comes with one such matrix pre-loaded, the “SafeTME” matrix, designed for estimation of immune and stroma cells in the tumor microenvironment. (This matrix was designed to avoid genes commonly expressed by cancer cells; see the SpatialDecon manuscript for details.)

Let’s take a glance at the safeTME matrix:

data("safeTME")
data("safeTME.matches")

signif(safeTME[seq_len(3), seq_len(3)], 2)
#>       macrophages  mast B.naive
#> A2M        0.8500 0.044  0.0043
#> ABCB1      0.0021 0.023  0.0250
#> ABCB4      0.0044 0.000  0.2200

heatmap(sweep(safeTME, 1, apply(safeTME, 1, max), "/"),
        labRow = NA, margins = c(10, 5))
The safeTME cell profile matrix

The safeTME cell profile matrix

For studies of other tissue types, we have provided a library of cell profile matrices, available on Github and downloadable with the “download_profile_matrix” function.

For a complete list of matrices, see ?download_profile_matrix.

Below we download a matrix of cell profiles derived from scRNA-seq of a mouse brain.

mousebrain = download_profile_matrix(matrixname = "Mouse_Brain")
dim(mousebrain)
#> [1] 315  12

heatmap(sweep(mousebrain, 1, apply(mousebrain, 1, max), "/"),
        labRow = NA, margins = c(12, 5), cexCol = 0.7)
The Mouse Brain profile matrix

The Mouse Brain profile matrix

Performing basic deconvolution with the spatialdecon function

Now our data is ready for deconvolution. First we’ll show how to use spatialdecon under the basic settings, omitting optional bells and whistles.

res = spatialdecon(norm = norm,
                   bg = bg,
                   X = safeTME,
                   align_genes = TRUE)
str(res)
#> List of 10
#>  $ beta            : num [1:18, 1:30] 7.406 0.641 0.609 0.113 0.22 ...
#>   ..- attr(*, "dimnames")=List of 2
#>   .. ..$ : chr [1:18] "macrophages" "mast" "B.naive" "B.memory" ...
#>   .. ..$ : chr [1:30] "ROI10TME" "ROI10Tumor" "ROI11TME" "ROI11Tumor" ...
#>  $ sigmas          : num [1:18, 1:18, 1:30] 1.12112 -0.00766 -0.00633 -0.00681 0.00461 ...
#>   ..- attr(*, "dimnames")=List of 3
#>   .. ..$ : chr [1:18] "macrophages" "mast" "B.naive" "B.memory" ...
#>   .. ..$ : chr [1:18] "macrophages" "mast" "B.naive" "B.memory" ...
#>   .. ..$ : chr [1:30] "ROI10TME" "ROI10Tumor" "ROI11TME" "ROI11Tumor" ...
#>  $ yhat            : num [1:544, 1:30] 59.59 1.68 29.7 2.35 2.15 ...
#>   ..- attr(*, "dimnames")=List of 2
#>   .. ..$ : chr [1:544] "A2M" "ABCB1" "ACP5" "ADAM12" ...
#>   .. ..$ : chr [1:30] "ROI10TME" "ROI10Tumor" "ROI11TME" "ROI11Tumor" ...
#>  $ resids          : num [1:544, 1:30] -1.794 0.287 -0.19 -0.879 0.51 ...
#>   ..- attr(*, "dimnames")=List of 2
#>   .. ..$ : chr [1:544] "A2M" "ABCB1" "ACP5" "ADAM12" ...
#>   .. ..$ : chr [1:30] "ROI10TME" "ROI10Tumor" "ROI11TME" "ROI11Tumor" ...
#>  $ p               : num [1:18, 1:30] 8.12e-12 1.23e-04 4.29e-01 9.00e-01 3.66e-01 ...
#>   ..- attr(*, "dimnames")=List of 2
#>   .. ..$ : chr [1:18] "macrophages" "mast" "B.naive" "B.memory" ...
#>   .. ..$ : chr [1:30] "ROI10TME" "ROI10Tumor" "ROI11TME" "ROI11Tumor" ...
#>  $ t               : num [1:18, 1:30] 6.995 3.869 0.792 0.126 0.905 ...
#>   ..- attr(*, "dimnames")=List of 2
#>   .. ..$ : chr [1:18] "macrophages" "mast" "B.naive" "B.memory" ...
#>   .. ..$ : chr [1:30] "ROI10TME" "ROI10Tumor" "ROI11TME" "ROI11Tumor" ...
#>  $ se              : num [1:18, 1:30] 1.059 0.166 0.769 0.896 0.243 ...
#>   ..- attr(*, "dimnames")=List of 2
#>   .. ..$ : chr [1:18] "macrophages" "mast" "B.naive" "B.memory" ...
#>   .. ..$ : chr [1:30] "ROI10TME" "ROI10Tumor" "ROI11TME" "ROI11Tumor" ...
#>  $ prop_of_all     : num [1:18, 1:30] 0.40775 0.03528 0.03354 0.00623 0.01211 ...
#>   ..- attr(*, "dimnames")=List of 2
#>   .. ..$ : chr [1:18] "macrophages" "mast" "B.naive" "B.memory" ...
#>   .. ..$ : chr [1:30] "ROI10TME" "ROI10Tumor" "ROI11TME" "ROI11Tumor" ...
#>  $ prop_of_nontumor: num [1:18, 1:30] 0.40775 0.03528 0.03354 0.00623 0.01211 ...
#>   ..- attr(*, "dimnames")=List of 2
#>   .. ..$ : chr [1:18] "macrophages" "mast" "B.naive" "B.memory" ...
#>   .. ..$ : chr [1:30] "ROI10TME" "ROI10Tumor" "ROI11TME" "ROI11Tumor" ...
#>  $ X               : num [1:544, 1:18] 0.85357 0.00213 3.5616 0.01582 0.13006 ...
#>   ..- attr(*, "dimnames")=List of 2
#>   .. ..$ : chr [1:544] "A2M" "ABCB1" "ACP5" "ADAM12" ...
#>   .. ..$ : chr [1:18] "macrophages" "mast" "B.naive" "B.memory" ...

We’re most interested in “beta”, the matrix of estimated cell abundances.

heatmap(res$beta, cexCol = 0.5, cexRow = 0.7, margins = c(10,7))
Cell abundance estimates

Cell abundance estimates

Using the advanced settings of spatialdecon

spatialdecon has several abilities beyond basic deconvolution:

  1. If given the nuclei counts for each region/observation, it returns results on the scale of total cell counts.
  2. If given the identities of pure tumor regions/observations, it infers a handful of tumor-specific expression profiles and appends them to the cell profile matrix. Doing this accounts for cancer cell-derived expression from any genes in the cell profile matrix, removing contaminating signal from cancer cells.
  3. If given raw count data, it derives per-data-point weights, using an error model derived for GeoMx data.
  4. If given a “cellmatches” argument, it sums multiple closely-related cell types into a single score. E.g. if the safeTME matrix is used with the cell-matching data object “safeTME.matches”, it e.g. sums the “T.CD8.naive” and “T.CD8.memory” scores into a single “CD8.T.cells” score.

Let’s take a look at an example cell matching object:

str(safeTME.matches)
#> List of 14
#>  $ macrophages      : chr "macrophages"
#>  $ mast             : chr "mast"
#>  $ B                : chr [1:2] "B.naive" "B.memory"
#>  $ plasma           : chr "plasma"
#>  $ CD4.T.cells      : chr [1:2] "T.CD4.naive" "T.CD4.memory"
#>  $ CD8.T.cells      : chr [1:2] "T.CD8.naive" "T.CD8.memory"
#>  $ NK               : chr "NK"
#>  $ pDC              : chr "pDCs"
#>  $ mDCs             : chr "mDCs"
#>  $ monocytes        : chr [1:2] "monocytes.C" "monocytes.NC.I"
#>  $ neutrophils      : chr "neutrophils"
#>  $ Treg             : chr "Treg"
#>  $ endothelial.cells: chr "endothelial.cells"
#>  $ fibroblasts      : chr "fibroblasts"

Now let’s run spatialdecon:

# vector identifying pure tumor segments:
annot$istumor = (annot$AOI.name == "Tumor")

# run spatialdecon with all the bells and whistles:
restils = spatialdecon(norm = norm,                     # normalized data
                       raw = raw,                       # raw data, used to down-weight low-count observations 
                       bg = bg,                         # expected background counts for every data point in norm
                       X = safeTME,                     # safeTME matrix, used by default
                       cellmerges = safeTME.matches,   # safeTME.matches object, used by default
                       cell_counts = annot$nuclei,      # nuclei counts, used to estimate total cells
                       is_pure_tumor = annot$istumor,   # identities of the Tumor segments/observations
                       n_tumor_clusters = 5)            # how many distinct tumor profiles to append to safeTME

str(restils)
#> List of 13
#>  $ beta            : num [1:14, 1:30] 3.64744 0.33137 0.00437 0.00252 0.38872 ...
#>   ..- attr(*, "dimnames")=List of 2
#>   .. ..$ : chr [1:14] "macrophages" "mast" "B" "plasma" ...
#>   .. ..$ : chr [1:30] "ROI10TME" "ROI10Tumor" "ROI11TME" "ROI11Tumor" ...
#>  $ yhat            : num [1:544, 1:30] 25.42 2.45 20.4 1.55 2.4 ...
#>   ..- attr(*, "dimnames")=List of 2
#>   .. ..$ : chr [1:544] "A2M" "ABCB1" "ACP5" "ADAM12" ...
#>   .. ..$ : chr [1:30] "ROI10TME" "ROI10Tumor" "ROI11TME" "ROI11Tumor" ...
#>  $ resids          : num [1:544, 1:30] -0.565 -0.261 0.352 -0.278 0.35 ...
#>   ..- attr(*, "dimnames")=List of 2
#>   .. ..$ : chr [1:544] "A2M" "ABCB1" "ACP5" "ADAM12" ...
#>   .. ..$ : chr [1:30] "ROI10TME" "ROI10Tumor" "ROI11TME" "ROI11Tumor" ...
#>  $ p               : num [1:14, 1:30] 3.17e-05 5.07e-03 9.86e-01 9.86e-01 7.78e-01 ...
#>   ..- attr(*, "dimnames")=List of 2
#>   .. ..$ : chr [1:14] "macrophages" "mast" "B" "plasma" ...
#>   .. ..$ : chr [1:30] "ROI10TME" "ROI10Tumor" "ROI11TME" "ROI11Tumor" ...
#>  $ t               : num [1:14, 1:30] 4.198 2.8143 0.0172 0.0174 0.2815 ...
#>   ..- attr(*, "dimnames")=List of 2
#>   .. ..$ : chr [1:14] "macrophages" "mast" "B" "plasma" ...
#>   .. ..$ : chr [1:30] "ROI10TME" "ROI10Tumor" "ROI11TME" "ROI11Tumor" ...
#>  $ se              : num [1:14, 1:30] 0.869 0.118 0.254 0.145 1.381 ...
#>   ..- attr(*, "dimnames")=List of 2
#>   .. ..$ : chr [1:14] "macrophages" "mast" "B" "plasma" ...
#>   .. ..$ : chr [1:30] "ROI10TME" "ROI10Tumor" "ROI11TME" "ROI11Tumor" ...
#>  $ beta.granular   : num [1:23, 1:30] 3.64744 0.33137 0.00437 0 0.00252 ...
#>   ..- attr(*, "dimnames")=List of 2
#>   .. ..$ : chr [1:23] "macrophages" "mast" "B.naive" "B.memory" ...
#>   .. ..$ : chr [1:30] "ROI10TME" "ROI10Tumor" "ROI11TME" "ROI11Tumor" ...
#>  $ sigma.granular  : num [1:23, 1:23, 1:30] 0.75492 -0.003 0.00476 -0.00196 -0.00133 ...
#>   ..- attr(*, "dimnames")=List of 3
#>   .. ..$ : chr [1:23] "macrophages" "mast" "B.naive" "B.memory" ...
#>   .. ..$ : chr [1:23] "macrophages" "mast" "B.naive" "B.memory" ...
#>   .. ..$ : chr [1:30] "ROI10TME" "ROI10Tumor" "ROI11TME" "ROI11Tumor" ...
#>  $ sigma           : num [1:14, 1:14, 1:30] 0.75492 -0.003 0.00281 -0.00133 0.06478 ...
#>   ..- attr(*, "dimnames")=List of 3
#>   .. ..$ : chr [1:14] "macrophages" "mast" "B" "plasma" ...
#>   .. ..$ : chr [1:14] "macrophages" "mast" "B" "plasma" ...
#>   .. ..$ : chr [1:30] "ROI10TME" "ROI10Tumor" "ROI11TME" "ROI11Tumor" ...
#>  $ prop_of_all     : num [1:14, 1:30] 0.504836 0.045864 0.000605 0.000349 0.053802 ...
#>   ..- attr(*, "dimnames")=List of 2
#>   .. ..$ : chr [1:14] "macrophages" "mast" "B" "plasma" ...
#>   .. ..$ : chr [1:30] "ROI10TME" "ROI10Tumor" "ROI11TME" "ROI11Tumor" ...
#>  $ prop_of_nontumor: num [1:14, 1:30] 0.504836 0.045864 0.000605 0.000349 0.053802 ...
#>   ..- attr(*, "dimnames")=List of 2
#>   .. ..$ : chr [1:14] "macrophages" "mast" "B" "plasma" ...
#>   .. ..$ : chr [1:30] "ROI10TME" "ROI10Tumor" "ROI11TME" "ROI11Tumor" ...
#>  $ cell.counts     :List of 2
#>   ..$ cells.per.100: num [1:14, 1:30] 18.8788 1.7151 0.0226 0.013 2.012 ...
#>   .. ..- attr(*, "dimnames")=List of 2
#>   .. .. ..$ : chr [1:14] "macrophages" "mast" "B" "plasma" ...
#>   .. .. ..$ : chr [1:30] "ROI10TME" "ROI10Tumor" "ROI11TME" "ROI11Tumor" ...
#>   ..$ cell.counts  : num [1:14, 1:30] 165.944 15.076 0.199 0.115 17.685 ...
#>   .. ..- attr(*, "dimnames")=List of 2
#>   .. .. ..$ : chr [1:14] "macrophages" "mast" "B" "plasma" ...
#>   .. .. ..$ : chr [1:30] "ROI10TME" "ROI10Tumor" "ROI11TME" "ROI11Tumor" ...
#>  $ X               : num [1:544, 1:23] 0.85357 0.00213 3.5616 0.01582 0.13006 ...
#>   ..- attr(*, "dimnames")=List of 2
#>   .. ..$ : chr [1:544] "A2M" "ABCB1" "ACP5" "ADAM12" ...
#>   .. ..$ : chr [1:23] "macrophages" "mast" "B.naive" "B.memory" ...

There are quite a few readouts here. Let’s review the important ones:

To illustrate the derivation of tumor profiles, let’s look at the cell profile matrix output by spatialdecon:

heatmap(sweep(restils$X, 1, apply(restils$X, 1, max), "/"),
         labRow = NA, margins = c(10, 5))
safeTME merged with newly-derived tumor profiles

safeTME merged with newly-derived tumor profiles

Note the new tumor-specific columns.

Finally, let’s compare deconvolution results from basic vs. the advanced setting with tumor profiles appended (just for a few cell types):

par(mfrow = c(2, 3))
par(mar = c(5,7,2,1))
for (i in seq_len(6)) {
  cell = rownames(res$beta)[i]
  plot(res$beta[cell, ], restils$beta.granular[cell, ],
       xlab = paste0(cell, " score under basic setting"), 
       ylab = paste0(cell, " score when tumor\ncells are modelled"), 
       pch = 16,
       col = c(rgb(0,0,1,0.5), rgb(1,0,0,0.5))[1 + annot$istumor],
       xlim = range(c(res$beta[cell, ], restils$beta.granular[cell, ])),
       ylim = range(c(res$beta[cell, ], restils$beta.granular[cell, ])))
  abline(0,1)
  if (i == 1) {
    legend("topleft", pch = 16, col = c(rgb(0,0,1,0.5), rgb(1,0,0,0.5)),
           legend = c("microenv.", "tumor"))
  }
}
Cell abundance estimates with and without modelling tumor profiles

Cell abundance estimates with and without modelling tumor profiles

So the impact of modelling tumor is two-fold:

Plotting deconvolution results

The SpatialDecon package contains two specialized plotting functions, and a default color palette for the safeTME matrix.

The first function is “TIL_barplot”, which is just a convenient way of drawing barplots of cell type abundance.

# For reference, show the TILs color data object used by the plotting functions 
# when safeTME has been used:
data("cellcols")
cellcols
#>       CD4.T.cells       CD8.T.cells              Treg       T.CD4.naive 
#>             "red"       "firebrick"         "#FF66FF"         "#CC0000" 
#>      T.CD4.memory       T.CD8.naive      T.CD8.memory                NK 
#>         "#FF0000"         "#FF6633"         "#FF9900"          "grey10" 
#>                 B           B.naive          B.memory            plasma 
#>        "darkblue"         "#000099"         "#0000FF"         "#3399CC" 
#>               pDC              pDCs       macrophages         monocytes 
#>         "#00FFFF"         "#00FFFF"         "#006600"         "#33CC00" 
#>       monocytes.C    monocytes.NC.I              mDCs       neutrophils 
#>         "#66CC66"         "#33CC00"         "#00FF00"         "#9966CC" 
#>              mast       fibroblasts endothelial.cells             tumor 
#>         "#FFFF00"         "#999999"         "#996633"         "#333333"

# show just the TME segments, since that's where the immune cells are:
layout(mat = (matrix(c(1, 2), 1)), widths = c(7, 3))
TIL_barplot(restils$cell.counts$cell.counts, draw_legend = TRUE, 
            cex.names = 0.5)
Barplots of TIL abundance

Barplots of TIL abundance

# or the proportions of cells:
TIL_barplot(restils$prop_of_nontumor[, annot$AOI.name == "TME"], 
            draw_legend = TRUE, cex.names = 0.75)
Barplots of TIL abundance

Barplots of TIL abundance

The second function is “florets”, used for plotting cell abundances atop some 2-D projection. Here, we’ll plot cell abundances atop the first 2 principal components of the data:

# PCA of the normalized data:
pc = prcomp(t(log2(pmax(norm, 1))))$x[, c(1, 2)]

# run florets function:
par(mar = c(5,5,1,1))
layout(mat = (matrix(c(1, 2), 1)), widths = c(6, 2))
florets(x = pc[, 1], y = pc[, 2],
        b = restils$beta, cex = 2,
        xlab = "PC1", ylab = "PC2")
par(mar = c(0,0,0,0))
frame()
legend("center", fill = cellcols[rownames(restils$beta)], 
       legend = rownames(restils$beta), cex = 0.7)
TIL abundance plotted on PC space

TIL abundance plotted on PC space

So we can see that PC1 roughly tracks many vs. few immune cells, and PC2 tracks the relative abundance of lymphoid/myeloid populations.

Other functions

The SpatialDecon library includes several helpful functions for further analysis/fine-tuning of deconvolution results.

Combining cell types:

When two cell types are too similar, the estimation of their abundances becomes unstable. However, their sum can still be estimated easily. The function “collapseCellTypes” takes a deconvolution results object and collapses any colsely-related cell types you tell it to:

Cell abundance estimates with related cell types collapsed

Cell abundance estimates with related cell types collapsed

Inferring an expression profile for a cell type omitted from the profile matrix

Sometimes a cell profile matrix will omit a cell type you know to be present in a tissue. If your data includes any regions that are purely this unmodelled cell type - e.g. because you’ve used the GeoMx platform’s segmentation capability to specifically select them - then you can infer a profile for that cell type and merge it with your cell profile matrix. The algorithm clusters all the observations you designate as purely the unmodelled cell type, and collapses those clusters into as many profiles of that cell type as you wish. For cancer cell, it may be appropriate to specify 10 or more clusters; for highly regular healthy cells, one cluster may suffice.

(Note: this functionality can also be run within the spatialdecon function, as is demonstrated further above.)

safeTME merged with newly-derived tumor profiles

safeTME merged with newly-derived tumor profiles

Reverse deconvolution

Once cell type abundance has been estimated, we can flip the deconvolution around, modelling the expression data as a function of cell abundances, and thereby deriving:

  1. Estimated expression of each gene in each cell type. (Including for genes not present in your cell profile matrix)
  2. Fitted expression values for each gene based on cell mixing.
  3. Residuals of each gene: how does their expression compare to what cell mixing would predict?
  4. Two metrics of how well genes are predicted by/ redundant with cell mixing: correlation between observed and fitted expression, and residual SD.

The function “reversedecon” runs this model.

Residuals from reverseDecon

Residuals from reverseDecon

Genes’ dependency on cell mixing

Genes’ dependency on cell mixing

From the above plot, we can see that genes like CXCL14 vary independently of cell mixing, genes like LYZ are correlated with cell mixing but still have variable expression, and genes like NKG7 serve as nothing but obtuse readouts of cell mixing.

Session Info

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=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] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] SpatialDecon_1.2.0
#> 
#> loaded via a namespace (and not attached):
#>  [1] digest_0.6.27     R6_2.5.0          jsonlite_1.7.2    magrittr_2.0.1   
#>  [5] evaluate_0.14     highr_0.9         rlang_0.4.11      stringi_1.6.2    
#>  [9] jquerylib_0.1.4   bslib_0.2.5.1     rmarkdown_2.8     tools_4.1.0      
#> [13] stringr_1.4.0     xfun_0.23         yaml_2.2.1        compiler_4.1.0   
#> [17] logNormReg_0.3-0  htmltools_0.5.1.1 knitr_1.33        sass_0.4.0