Identify and visualise regions of cell type colocalization in multiplexed imaging data that has been segmented at a single-cell resolution.
lisaClust 1.17.0
if (!require("BiocManager")) {
install.packages("BiocManager")
}
BiocManager::install("lisaClust")
# load required packages
library(lisaClust)
library(spicyR)
library(ggplot2)
library(SingleCellExperiment)
library(SpatialDatasets)
Clustering local indicators of spatial association (LISA) functions is a
methodology for identifying consistent spatial organisation of multiple
cell-types in an unsupervised way. This can be used to enable the
characterization of interactions between multiple cell-types
simultaneously and can complement traditional pairwise analysis. In our
implementation our LISA curves are a localised summary of an L-function
from a Poisson point process model. Our framework lisaClust can be
used to provide a high-level summary of cell-type colocalization in
high-parameter spatial cytometry data, facilitating the identification
of distinct tissue compartments or identification of complex cellular
microenvironments.
To illustrate our lisaClust framework, we consider a very simple
toy example where two cell-types are completely separated spatially. We
simulate data for two different images.
set.seed(51773)
x <- round(c(
runif(200), runif(200) + 1, runif(200) + 2, runif(200) + 3,
runif(200) + 3, runif(200) + 2, runif(200) + 1, runif(200)
), 4) * 100
y <- round(c(
runif(200), runif(200) + 1, runif(200) + 2, runif(200) + 3,
runif(200), runif(200) + 1, runif(200) + 2, runif(200) + 3
), 4) * 100
cellType <- factor(paste("c", rep(rep(c(1:2), rep(200, 2)), 4), sep = ""))
imageID <- rep(c("s1", "s2"), c(800, 800))
cells <- data.frame(x, y, cellType, imageID)
ggplot(cells, aes(x, y, colour = cellType)) +
geom_point() +
facet_wrap(~imageID) +
theme_minimal()
First we store our data in a SingleCellExperiment object.
SCE <- SingleCellExperiment(colData = cells)
SCE
## class: SingleCellExperiment
## dim: 0 1600
## metadata(0):
## assays(0):
## rownames: NULL
## rowData names(0):
## colnames: NULL
## colData names(4): x y cellType imageID
## reducedDimNames(0):
## mainExpName: NULL
## altExpNames(0):
We can then use the convenience function lisaClust to simultaneously
calculate local indicators of spatial association (LISA) functions and perform
k-means clustering. The number of clusters can be specified with the k = parameter.
In the example below, we’ve chosen k = 2, resulting in a total of 2 clusters. The cell type column can be specified using the cellType = argument. By default, lisaClust uses the column named cellType.
The clusters identified by lisaClust are stored in colData of the SingleCellExperiment
object as a new column called regions.
SCE <- lisaClust(SCE, k = 2)
colData(SCE) |> head()
## DataFrame with 6 rows and 5 columns
## x y cellType imageID region
## <numeric> <numeric> <factor> <character> <character>
## 1 36.72 38.58 c1 s1 region_2
## 2 61.38 41.29 c1 s1 region_2
## 3 33.59 80.98 c1 s1 region_2
## 4 50.17 64.91 c1 s1 region_2
## 5 82.93 35.60 c1 s1 region_2
## 6 83.13 2.69 c1 s1 region_2
lisaClust also provides the convenient hatchingPlot function to
visualise the different regions that have been demarcated by the
clustering. hatchingPlot outputs a ggplot object where the regions
are marked by different hatching patterns. In a real biological dataset,
this allows us to plot both regions and cell-types on the same
visualization.
In the example below, we can visualise our stimulated data where our 2
cell types have been separated neatly into 2 distinct regions based on
which cell type each region is dominated by. region_2 is dominated by
the red cell type c1, and region_1 is dominated by the blue cell
type c2.
hatchingPlot(SCE, useImages = c("s1", "s2"))
## Using other clustering methods.
While the lisaClust function is convenient, we have not implemented an exhaustive
suite of clustering methods as it is very easy to do this yourself. There are
just two simple steps.
We can calculate local indicators of spatial association (LISA) functions
using the lisa function. Here the LISA curves are a
localised summary of an L-function from a Poisson point process model. The radii
that will be calculated over can be set with Rs.
lisaCurves <- lisa(SCE, Rs = c(20, 50, 100))
head(lisaCurves)
## 20_c1 20_c2 50_c1 50_c2 100_c1 100_c2
## cell_1 5.556700 -2.764143 15.631209 -6.910357 11.733097 -9.198914
## cell_2 4.833149 -2.764143 13.940407 -6.910357 9.532662 -8.543440
## cell_3 5.918476 -2.764143 9.008588 -6.910357 9.157887 -7.813862
## cell_4 4.109597 -2.764143 11.907928 -6.910357 8.404425 -8.140036
## cell_5 3.024270 -2.764143 10.159278 -6.910357 9.006286 -8.283564
## cell_6 7.986742 -2.764143 8.675070 -6.910357 12.859615 -13.820714
The LISA curves can then be used to cluster the cells. Here we use k-means
clustering. However, other clustering methods like SOM could also be used. We can store these
cell clusters or cell “regions” in our SingleCellExperiment object.
# Custom clustering algorithm
kM <- kmeans(lisaCurves, 2)
# Storing clusters into colData
colData(SCE)$custom_region <- paste("region", kM$cluster, sep = "_")
colData(SCE) |> head()
## DataFrame with 6 rows and 6 columns
## x y cellType imageID region custom_region
## <numeric> <numeric> <factor> <character> <character> <character>
## 1 36.72 38.58 c1 s1 region_2 region_2
## 2 61.38 41.29 c1 s1 region_2 region_2
## 3 33.59 80.98 c1 s1 region_2 region_2
## 4 50.17 64.91 c1 s1 region_2 region_2
## 5 82.93 35.60 c1 s1 region_2 region_2
## 6 83.13 2.69 c1 s1 region_2 region_2
Next, we apply our lisaClust framework to two images of breast cancer obtained by Keren et al. (2018).
We will start by reading in the data from the SpatialDatasets package
as a SingleCellExperiment object. Here the data is in a format consistent with
that outputted by CellProfiler.
kerenSPE <- SpatialDatasets::spe_Keren_2018()
This data includes annotation of the cell-types of each cell. Hence, we can
move directly to performing k-means clustering on the local indicators of
spatial association (LISA) functions using the lisaClust function, remembering
to specify the imageID, cellType, and spatialCoords
columns in colData. For the purpose of demonstration, we will be using
only images 5 and 6 of the kerenSPE dataset.
kerenSPE <- kerenSPE[,kerenSPE$imageID %in% c("5", "6")]
kerenSPE <- lisaClust(kerenSPE,
k = 5
)
These regions are stored in colData and can be extracted.
colData(kerenSPE)[, c("imageID", "region")] |>
head(20)
## DataFrame with 20 rows and 2 columns
## imageID region
## <character> <character>
## 21154 5 region_4
## 21155 5 region_4
## 21156 5 region_4
## 21157 5 region_3
## 21158 5 region_3
## ... ... ...
## 21169 5 region_3
## 21170 5 region_3
## 21171 5 region_1
## 21172 5 region_3
## 21173 5 region_1
lisaClust also provides a convenient function, regionMap, for examining which
cell types are located in which regions. In this example, we use this to check
which cell types appear more frequently in each region than expected by chance.
Here, we clearly see that healthy epithelial and mesenchymal tissue are highly concentrated in region 1, immune cells are concentrated in regions 2 and 4, whilst tumour cells are concentrated in region 3.
We can further segregate these cells by increasing the number of clusters, i.e.,
increasing the parameter k = in the lisaClust() function. For the purposes
of demonstration, let’s take a look at the hatchingPlot of these regions.
regionMap(kerenSPE,
type = "bubble"
)
Finally, we can use hatchingPlot to construct a ggplot object where
the regions are marked by different hatching patterns. This allows us to
visualize the 5 regions and 17 cell-types simultaneously.
hatchingPlot(kerenSPE, nbp = 300)
Keren, L, M Bosse, D Marquez, R Angoshtari, S Jain, S Varma, S Yang, et al. 2018. “A Structured Tumor-Immune Microenvironment in Triple Negative Breast Cancer Revealed by Multiplexed Ion Beam Imaging.” Cell 174 (6): 1373–1387.e19.
sessionInfo()
## R version 4.5.1 Patched (2025-08-23 r88802)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 24.04.3 LTS
##
## Matrix products: default
## BLAS: /home/biocbuild/bbs-3.22-bioc/R/lib/libRblas.so
## LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.12.0 LAPACK version 3.12.0
##
## 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
##
## time zone: America/New_York
## tzcode source: system (glibc)
##
## attached base packages:
## [1] stats4 stats graphics grDevices utils datasets methods
## [8] base
##
## other attached packages:
## [1] SpatialDatasets_1.7.3 SpatialExperiment_1.19.1
## [3] ExperimentHub_2.99.5 AnnotationHub_3.99.6
## [5] BiocFileCache_2.99.6 dbplyr_2.5.1
## [7] SingleCellExperiment_1.31.1 SummarizedExperiment_1.39.2
## [9] Biobase_2.69.1 GenomicRanges_1.61.5
## [11] Seqinfo_0.99.2 IRanges_2.43.5
## [13] S4Vectors_0.47.4 BiocGenerics_0.55.1
## [15] generics_0.1.4 MatrixGenerics_1.21.0
## [17] matrixStats_1.5.0 ggplot2_4.0.0
## [19] spicyR_1.21.8 lisaClust_1.17.0
## [21] BiocStyle_2.37.1
##
## loaded via a namespace (and not attached):
## [1] later_1.4.4 splines_4.5.1
## [3] bitops_1.0-9 filelock_1.0.3
## [5] svgPanZoom_0.3.4 tibble_3.3.0
## [7] polyclip_1.10-7 lifecycle_1.0.4
## [9] httr2_1.2.1 Rdpack_2.6.4
## [11] rstatix_0.7.2 lattice_0.22-7
## [13] MASS_7.3-65 MultiAssayExperiment_1.35.9
## [15] backports_1.5.0 magrittr_2.0.4
## [17] sass_0.4.10 rmarkdown_2.30
## [19] jquerylib_0.1.4 yaml_2.3.10
## [21] httpuv_1.6.16 doRNG_1.8.6.2
## [23] ClassifyR_3.13.5 sp_2.2-0
## [25] dcanr_1.25.0 spatstat.sparse_3.1-0
## [27] DBI_1.2.3 minqa_1.2.8
## [29] RColorBrewer_1.1-3 abind_1.4-8
## [31] purrr_1.1.0 RCurl_1.98-1.17
## [33] tweenr_2.0.3 rappdirs_0.3.3
## [35] spatstat.utils_3.2-0 terra_1.8-70
## [37] pheatmap_1.0.13 goftest_1.2-3
## [39] simpleSeg_1.11.1 spatstat.random_3.4-2
## [41] svglite_2.2.1 codetools_0.2-20
## [43] DelayedArray_0.35.3 ggforce_0.5.0
## [45] tidyselect_1.2.1 raster_3.6-32
## [47] farver_2.1.2 viridis_0.6.5
## [49] lme4_1.1-37 spatstat.explore_3.5-3
## [51] jsonlite_2.0.0 Formula_1.2-5
## [53] survival_3.8-3 iterators_1.0.14
## [55] systemfonts_1.3.1 foreach_1.5.2
## [57] tools_4.5.1 ggnewscale_0.5.2
## [59] Rcpp_1.1.0 glue_1.8.0
## [61] gridExtra_2.3 SparseArray_1.9.1
## [63] BiocBaseUtils_1.11.2 xfun_0.53
## [65] mgcv_1.9-3 ggthemes_5.1.0
## [67] EBImage_4.51.0 HDF5Array_1.37.0
## [69] dplyr_1.1.4 shinydashboard_0.7.3
## [71] scam_1.2-19 withr_3.0.2
## [73] numDeriv_2016.8-1.1 BiocManager_1.30.26
## [75] fastmap_1.2.0 ggh4x_0.3.1
## [77] rhdf5filters_1.21.0 boot_1.3-32
## [79] digest_0.6.37 mime_0.13
## [81] R6_2.6.1 textshaping_1.0.3
## [83] tensor_1.5.1 jpeg_0.1-11
## [85] dichromat_2.0-0.1 spatstat.data_3.1-8
## [87] RSQLite_2.4.3 h5mread_1.1.1
## [89] tidyr_1.3.1 data.table_1.17.8
## [91] class_7.3-23 httr_1.4.7
## [93] htmlwidgets_1.6.4 S4Arrays_1.9.1
## [95] pkgconfig_2.0.3 gtable_0.3.6
## [97] blob_1.2.4 S7_0.2.0
## [99] XVector_0.49.1 htmltools_0.5.8.1
## [101] carData_3.0-5 bookdown_0.45
## [103] fftwtools_0.9-11 scales_1.4.0
## [105] ggupset_0.4.1 png_0.1-8
## [107] spatstat.univar_3.1-4 reformulas_0.4.1
## [109] knitr_1.50 reshape2_1.4.4
## [111] rjson_0.2.23 nlme_3.1-168
## [113] curl_7.0.0 nloptr_2.2.1
## [115] bdsmatrix_1.3-7 rhdf5_2.53.5
## [117] cachem_1.1.0 stringr_1.5.2
## [119] BiocVersion_3.22.0 vipor_0.4.7
## [121] parallel_4.5.1 concaveman_1.2.0
## [123] AnnotationDbi_1.71.1 pillar_1.11.1
## [125] grid_4.5.1 vctrs_0.6.5
## [127] coxme_2.2-22 promises_1.3.3
## [129] ggpubr_0.6.1 car_3.1-3
## [131] xtable_1.8-4 beeswarm_0.4.0
## [133] evaluate_1.0.5 tinytex_0.57
## [135] magick_2.9.0 cli_3.6.5
## [137] locfit_1.5-9.12 compiler_4.5.1
## [139] rlang_1.1.6 crayon_1.5.3
## [141] rngtools_1.5.2 ggsignif_0.6.4
## [143] labeling_0.4.3 ggbeeswarm_0.7.2
## [145] plyr_1.8.9 stringi_1.8.7
## [147] viridisLite_0.4.2 nnls_1.6
## [149] deldir_2.0-4 BiocParallel_1.43.4
## [151] cytomapper_1.21.0 lmerTest_3.1-3
## [153] Biostrings_2.77.2 tiff_0.1-12
## [155] spatstat.geom_3.6-0 V8_8.0.0
## [157] Matrix_1.7-4 bit64_4.6.0-1
## [159] Rhdf5lib_1.31.0 shiny_1.11.1
## [161] KEGGREST_1.49.1 rbibutils_2.3
## [163] igraph_2.1.4 broom_1.0.10
## [165] memoise_2.0.1 bslib_0.9.0
## [167] bit_4.6.0