Skip to contents

scider is an user-friendly R package providing functions to model the global density of cells in a slide of spatial transcriptomics data. All functions in the package are built based on the SpatialExperiment object, allowing integration into various spatial transcriptomics-related packages from Bioconductor. After modelling density, the package allows for serveral downstream analysis, including colocalization analysis, boundary detection analysis and differential density analysis.

Installation

if (!require("BiocManager", quietly = TRUE)) {
    install.packages("BiocManager")
}

BiocManager::install("scider")

The development version of scider can be installed from GitHub:

devtools::install_github("ChenLaboratory/scider")

Load data

In this vignette, we will use a subset of a Xenium Breast Cancer dataset.

data("xenium_bc_spe")

In the data, we have quantification of 541 genes from 10000 cells.

spe
## class: SpatialExperiment 
## dim: 541 10000 
## metadata(0):
## assays(1): counts
## rownames(541): ENSG00000121270 ENSG00000213088 ... BLANK_0444
##   BLANK_0447
## rowData names(3): ID Symbol Type
## colnames(10000): cell_212124 cell_120108 ... cell_252054 cell_568560
## colData names(21): cell_id transcript_counts ... cell_type sample_id
## reducedDimNames(0):
## mainExpName: NULL
## altExpNames(0):
## spatialCoords names(2) : x_centroid y_centroid
## imgData names(1): sample_id

We also have cell-type annotations of these cells, there are 4 cell types.

table(colData(spe)$cell_type)
## 
##       B cells Breast cancer   Fibroblasts       T cells 
##           643          3550          4234          1573

We can use the function plotSpatial to visualise the cell position and color the cells by cell types.

plotSpatial(spe, color = cell_type, alpha = 0.8)

Grid-based analysis

scider can conduct grid-based density analysis for spatial transcriptomics data.

Density calculation

Before calculating density, we need to define cell type-of-interest (COI). In this case, all cell types are COIs.

coi <- unique(colData(spe)$cell_type)
coi
## [1] "Fibroblasts"   "Breast cancer" "T cells"       "B cells"

We can perform density calculation for each COI using function gridDensity. The calculated density and grid information are saved in the metadata of the SpatialExperimnet object.

spe <- gridDensity(spe, coi = coi)

names(metadata(spe))
## [1] "grid_density" "grid_info"
metadata(spe)$grid_density
## DataFrame with 11400 rows and 9 columns
##          x_grid    y_grid    node_x    node_y        node density_fibroblasts
##       <numeric> <numeric> <integer> <integer> <character>           <numeric>
## 1       329.102   203.852         1         1         1-1          0.00352639
## 2       329.102   299.810         1         2         1-2          0.00432724
## 3       329.102   395.767         1         3         1-3          0.00526027
## 4       329.102   491.725         1         4         1-4          0.00633438
## 5       329.102   587.683         1         5         1-5          0.00755759
## ...         ...       ...       ...       ...         ...                 ...
## 11396   9865.73   10663.2       100       110     100-110          0.00743260
## 11397   9865.73   10759.2       100       111     100-111          0.00601659
## 11398   9865.73   10855.2       100       112     100-112          0.00482908
## 11399   9865.73   10951.1       100       113     100-113          0.00384286
## 11400   9865.73   11047.1       100       114     100-114          0.00303176
##       density_breast_cancer density_t_cells density_b_cells
##                   <numeric>       <numeric>       <numeric>
## 1               0.000124482      0.00200091      0.00308062
## 2               0.000158866      0.00269403      0.00447254
## 3               0.000207521      0.00360691      0.00639036
## 4               0.000278528      0.00478231      0.00893371
## 5               0.000385603      0.00624991      0.01215386
## ...                     ...             ...             ...
## 11396            0.00992645     1.85862e-05     0.000385502
## 11397            0.00715312     9.95949e-06     0.000337516
## 11398            0.00504125     5.53001e-06     0.000288967
## 11399            0.00348873     3.29144e-06     0.000242472
## 11400            0.00237981     2.14802e-06     0.000199856

We can visualise the density of each COI using function plotDensity.

plotDensity(spe, coi = coi[1])

Find Regions-of-interest (ROIs)

After obtaining grid-based density for each COI, we can then detect regions-of-interest (ROIs) based on density or select by user.

Detected by algorithm

To detect ROIs automatically, we can use the function findROI.

The detected ROIs are saved in the metadata of the SpatialExperiment object.

spe <- findROI(spe, coi = coi)

metadata(spe)$roi
## DataFrame with 1627 rows and 6 columns
##      component     members           x           y    xcoord    ycoord
##       <factor> <character> <character> <character> <numeric> <numeric>
## 1            1       73-97          73          97   7264.83   9415.79
## 2            1       73-98          73          98   7264.83   9511.75
## 3            1       73-99          73          99   7264.83   9607.71
## 4            1       74-95          74          95   7361.16   9223.88
## 5            1       74-96          74          96   7361.16   9319.83
## ...        ...         ...         ...         ...       ...       ...
## 1623        27      81-109          81         109   8035.47   10567.3
## 1624        27      82-106          82         106   8131.80   10279.4
## 1625        27      82-107          82         107   8131.80   10375.4
## 1626        27      82-108          82         108   8131.80   10471.3
## 1627        27      82-109          82         109   8131.80   10567.3

We can visualise the ROIs with function plotROI.

plotROI(spe)

Select ROI by user

Alternatively, users can select ROIs based on their own research interest (drawn by hand). This can be done using function selectRegion. This function will open an interactive window with an interactive plot for users to zoom-in/-out and select ROI using either a rectangular or lasso selection tool. Users can also press the Export selected points button to save the ROIs as object in the R environment.

selectRegion(metadata(spe)$grid_density, x.col = "x_grid", y.col = "y_grid")

After closing the interactive window, the selected ROI has been saved as a data.frame object named sel_region in the R environment.

sel_region

We can then use the postSelRegion to save the ROI in the metadata of the SpatialExperiment object.

spe1 <- postSelRegion(spe, sel_region = sel_region)

metadata(spe1)$roi

Similarly, we can plot visualise the user-defined ROI with function plotROI.

plotROI(spe1)

Testing relationship between cell types

After defining ROIs, we can then test the relationship between any two cell types within each ROI or overall but account for ROI variation using a cubic spline or a linear fit.

This can be done with function corrDensity, by setting the celltype1 and celltype2 parameters, the modelling results are saved in the metadata of the SpatialExperiment object.

model_result <- corDensity(spe, trace = FALSE)

We can see the correlation between breast cancer and fibroblasts in each ROI.

model_result$ROI
## DataFrame with 162 rows and 9 columns
##       celltype1     celltype2         ROI     ngrid  cor.coef         t
##     <character>   <character> <character> <numeric> <numeric> <numeric>
## 1   Fibroblasts Breast cancer           1        51 -0.261185 -0.726206
## 2   Fibroblasts Breast cancer           2        64 -0.850429 -3.191725
## 3   Fibroblasts Breast cancer           3        37 -0.609734 -1.547117
## 4   Fibroblasts Breast cancer           4       109 -0.619364 -1.301741
## 5   Fibroblasts Breast cancer           5        81 -0.713589 -2.246380
## ...         ...           ...         ...       ...       ...       ...
## 158     T cells       B cells          23        35  0.256837  0.588082
## 159     T cells       B cells          24        32  0.593693  1.300636
## 160     T cells       B cells          25        23  0.949763  5.778241
## 161     T cells       B cells          26        23  0.692722  1.719319
## 162     T cells       B cells          27        20  0.236879  0.534743
##            df      p.Pos     p.Neg
##     <numeric>  <numeric> <numeric>
## 1     7.20338   0.754685 0.2453153
## 2     3.89846   0.982807 0.0171933
## 3     4.04464   0.902016 0.0979838
## 4     2.72278   0.853903 0.1460965
## 5     4.86369   0.961945 0.0380552
## ...       ...        ...       ...
## 158   4.89692 0.29127441  0.708726
## 159   3.10775 0.14071356  0.859286
## 160   3.62552 0.00299803  0.997002
## 161   3.20414 0.08909362  0.910906
## 162   4.81012 0.30830407  0.691696

Or the correlation between breast cancer and fibroblasts across the whole slide:

model_result$overall
## DataFrame with 6 rows and 5 columns
##       celltype1     celltype2  cor.coef       p.Pos       p.Neg
##     <character>   <character> <numeric>   <numeric>   <numeric>
## 1   Fibroblasts Breast cancer -0.533620 9.99967e-01 6.01577e-08
## 2   Fibroblasts       T cells  0.593064 1.36192e-07 9.99697e-01
## 3   Fibroblasts       B cells  0.219668 1.66538e-02 6.99226e-01
## 4 Breast cancer       T cells -0.633887 9.96068e-01 7.65064e-12
## 5 Breast cancer       B cells -0.130037 3.96428e-01 1.94609e-03
## 6       T cells       B cells  0.422464 2.22015e-07 9.88677e-01

We can also visualise the fitting using function plotDensCor.

plotDensCor(spe, celltype1 = "Breast cancer", celltype2 = "Fibroblasts")

Or, we can visualise the statistics between each pair of cell types using function plotCorHeatmap in the ROIs:

plotCorHeatmap(model_result$ROI)

Or the correlation between cell type pairs across the whole slide:

plotCorHeatmap(model_result$overall)

Cell-based analysis

Based on the grid density, we can ask many biological question about the data. For example, we would like to know if a certain cell type that are located in high density of breast cancer cells are different to the same cell type from a different level of breast cancer region.

cell annotation based on grid density

To address this question, we first need to divide cells into different levels of grid density. This can be done using a contour identification strategy with function getContour.

spe <- getContour(spe, coi = "Breast cancer")

Different level of contour can be visualised with cells using plotContour.

plotContour(spe, coi = "Breast cancer")

We can then annotate cells by their locations within each contour using function allocateCells.

spe <- allocateCells(spe)
plotSpatial(spe, color = breast_cancer_contour, alpha = 0.5)

We can visualise cell type composition per level.

plotCellCompo(spe, coi = "Breast cancer")

plotCellCompo(spe, coi = "Breast cancer", by.roi = TRUE)

## R version 4.4.0 (2024-04-24)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 22.04.4 LTS
## 
## Matrix products: default
## BLAS:   /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3 
## LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.20.so;  LAPACK version 3.10.0
## 
## locale:
##  [1] LC_CTYPE=C.UTF-8       LC_NUMERIC=C           LC_TIME=C.UTF-8       
##  [4] LC_COLLATE=C.UTF-8     LC_MONETARY=C.UTF-8    LC_MESSAGES=C.UTF-8   
##  [7] LC_PAPER=C.UTF-8       LC_NAME=C              LC_ADDRESS=C          
## [10] LC_TELEPHONE=C         LC_MEASUREMENT=C.UTF-8 LC_IDENTIFICATION=C   
## 
## time zone: UTC
## tzcode source: system (glibc)
## 
## attached base packages:
## [1] stats4    stats     graphics  grDevices utils     datasets  methods  
## [8] base     
## 
## other attached packages:
##  [1] sf_1.0-16                   SpatialExperiment_1.14.0   
##  [3] SingleCellExperiment_1.26.0 SummarizedExperiment_1.34.0
##  [5] Biobase_2.64.0              GenomicRanges_1.56.0       
##  [7] GenomeInfoDb_1.40.0         IRanges_2.38.0             
##  [9] S4Vectors_0.42.0            BiocGenerics_0.50.0        
## [11] MatrixGenerics_1.16.0       matrixStats_1.3.0          
## [13] scider_1.1.2                ggplot2_3.5.1              
## 
## loaded via a namespace (and not attached):
##   [1] DBI_1.2.2               deldir_2.0-4            rlang_1.1.3            
##   [4] magrittr_2.0.3          snakecase_0.11.1        e1071_1.7-14           
##   [7] compiler_4.4.0          spatstat.geom_3.2-9     mgcv_1.9-1             
##  [10] systemfonts_1.0.6       vctrs_0.6.5             stringr_1.5.1          
##  [13] pkgconfig_2.0.3         crayon_1.5.2            fastmap_1.1.1          
##  [16] magick_2.8.3            XVector_0.44.0          lwgeom_0.2-14          
##  [19] labeling_0.4.3          utf8_1.2.4              promises_1.3.0         
##  [22] rmarkdown_2.26          UCSC.utils_1.0.0        ragg_1.3.0             
##  [25] purrr_1.0.2             xfun_0.43               zlibbioc_1.50.0        
##  [28] cachem_1.0.8            jsonlite_1.8.8          goftest_1.2-3          
##  [31] highr_0.10              later_1.3.2             DelayedArray_0.30.0    
##  [34] spatstat.utils_3.0-4    R6_2.5.1                RColorBrewer_1.1-3     
##  [37] bslib_0.7.0             stringi_1.8.3           spatstat.data_3.0-4    
##  [40] lubridate_1.9.3         jquerylib_0.1.4         Rcpp_1.0.12            
##  [43] knitr_1.46              tensor_1.5              splines_4.4.0          
##  [46] httpuv_1.6.15           Matrix_1.7-0            igraph_2.0.3           
##  [49] timechange_0.3.0        tidyselect_1.2.1        abind_1.4-5            
##  [52] yaml_2.3.8              spatstat.random_3.2-3   spatstat.explore_3.2-7 
##  [55] lattice_0.22-6          tibble_3.2.1            shiny_1.8.1.1          
##  [58] withr_3.0.0             evaluate_0.23           desc_1.4.3             
##  [61] isoband_0.2.7           units_0.8-5             proxy_0.4-27           
##  [64] polyclip_1.10-6         pillar_1.9.0            KernSmooth_2.23-22     
##  [67] plotly_4.10.4           generics_0.1.3          dbscan_1.1-12          
##  [70] munsell_0.5.1           scales_1.3.0            xtable_1.8-4           
##  [73] class_7.3-22            glue_1.7.0              janitor_2.2.0          
##  [76] pheatmap_1.0.12         lazyeval_0.2.2          tools_4.4.0            
##  [79] data.table_1.15.4       fs_1.6.4                grid_4.4.0             
##  [82] tidyr_1.3.1             colorspace_2.1-0        nlme_3.1-164           
##  [85] GenomeInfoDbData_1.2.12 fastmatrix_0.5-772      cli_3.6.2              
##  [88] spatstat.sparse_3.0-3   textshaping_0.3.7       fansi_1.0.6            
##  [91] S4Arrays_1.4.0          viridisLite_0.4.2       dplyr_1.1.4            
##  [94] gtable_0.3.5            SpatialPack_0.4         sass_0.4.9             
##  [97] digest_0.6.35           classInt_0.4-10         SparseArray_1.4.0      
## [100] rjson_0.2.21            htmlwidgets_1.6.4       farver_2.1.1           
## [103] memoise_2.0.1           htmltools_0.5.8.1       pkgdown_2.0.9          
## [106] lifecycle_1.0.4         httr_1.4.7              mime_0.12