Exploring cell-cell interaction with blisa

Introduction

blisa identifies spatially enriched ligand-receptor (LR) interactions from spatial transcriptomics data using bivariate Local Moran’s I (LISA) statistics. The core idea is to bin cells into a hexagonal grid, compute spatial co-enrichment of every ligand-receptor pair across bins, and flag “High-High” hotspot bins where both partners are spatially co-expressed beyond chance.

This vignette walks through a typical workflow on a Xenium breast cancer dataset.

Load Package

library(blisa)
library(SpatialExperiment)

Load Example Data

The example dataset is a small SpatialExperiment object (one Xenium breast cancer section) hosted as a GitHub Release asset. Download it once and cache locally:

data_url   <- "https://github.com/ChenLaboratory/example_data/releases/download/v1.0.0/spe_xenium_bc_s1rep1.rds"
cache_dir  <- tools::R_user_dir("blisa", "cache")
data_file  <- file.path(cache_dir, "spe_xenium_bc_s1rep1.rds")

if (!file.exists(data_file)) {
  dir.create(cache_dir, recursive = TRUE, showWarnings = FALSE)
  download.file(data_url, data_file, mode = "wb")
}

spe <- readRDS(data_file)
spe
#> class: SpatialExperiment 
#> dim: 313 163797 
#> metadata(0):
#> assays(1): counts
#> rownames(313): ABCC11 ACTA2 ... ZEB2 ZNF562
#> rowData names(3): ID Symbol Type
#> colnames(163797): cell_1 cell_2 ... cell_167779 cell_167780
#> colData names(12): cell_id transcript_counts ... gene_counts cell_type
#> reducedDimNames(0):
#> mainExpName: NULL
#> altExpNames(0):
#> spatialCoords names(2) : x_centroid y_centroid
#> imgData names(1): sample_id

Bin Cells into Hexagons

hexBinCells() aggregates cells into hexagonal bins. When a group argument is supplied (here, cell type), it also returns per-cell-type bin matrices in counts_by_group, which are needed downstream for CCI scoring and spatial visualisation.

coords <- as.data.frame(SpatialExperiment::spatialCoords(spe))
counts <- SummarizedExperiment::assay(spe, "counts")

binned <- hexBinCells(
  coords_df     = coords,
  counts_matrix = counts,
  bin_size      = 50,
  group         = spe$cell_type
)

# Components:
#   binned$counts_matrix   - gene x bin sparse matrix (all cells)
#   binned$bins            - sf polygons (with n_cells column)
#   binned$counts_by_group - named list of gene x bin matrices, one per cell type

Run BLISA

blisa() does everything in one call: spatial weights, LR pair filtering against CellChatDB, bivariate Moran’s I per LR pair, hotspot identification, and (when counts_by_group is supplied) cell-cell interaction scoring.

res <- blisa(
  binned$counts_matrix,
  bins            = binned$bins,
  n_cells_col     = "n_cells",
  counts_by_group = binned$counts_by_group
)
#> Downloading CellChatDB.human from GitHub (once per session)...
#> Testing 12 LR pairs...
#>   |========================================| 100%

res
#> A blisa object
#>  LR pairs tested  : 12 
#>  Significant pairs: 12 
#>  Bins             : 17215 
#>  CCI computed     : TRUE

The result is a blisa object with four slots:

Rank LR Pairs by Hotspot Count

plotLRrank(res, top = 30)

Spatial Map of Hotspot Bins

For a chosen LR pair (here, the top-ranked pair by default), shows which bins are significant hotspots, coloured by p-value or LISA score.

plotHotspots(res, index = 1)


# Or by gene names:
# plotHotspots(res, ligand = "CXCL12", receptor = "CXCR4")

Cell-Cell Interaction (CCI) Heatmaps

Across all LR pairs

Rows are sender→receiver cell-type pairs; columns are LR pairs.

plotCCI(res, top_lr = 20, top_pairs = 30)

Filter by specific senders or receivers:

plotCCI(res,
        sender = c("CD4+_T_Cells", "CD8+_T_Cells", "B_Cells",
                   "Macrophages", "DCs"),
        receiver = c("Invasive_Tumor", "DCIS", "Myoepi") )

For a single LR pair

plotCCILR(res, ligand = "CXCL12", receptor = "CXCR4")

Aggregated across LR pairs

Sender × receiver heatmap with scores summed (or any user-supplied function) across all LR pairs:

plotCCIsummary(res)


# With a different aggregation:
# plotCCIsummary(res, agg_fun = mean)

Spatial Map of Dominant Cell-Type Pairs

For each hotspot bin of a chosen LR pair, identifies the dominant sender→receiver cell-type combination based on ligand expression in the neighbourhood and receptor expression inside the bin.

plotCCIspatial(
  res,
  counts_by_group = binned$counts_by_group,
  index = 1
)

Session Information

sessionInfo()
#> R version 4.6.0 (2026-04-24 ucrt)
#> Platform: x86_64-w64-mingw32/x64
#> Running under: Windows 11 x64 (build 26200)
#> 
#> Matrix products: default
#>   LAPACK version 3.12.1
#> 
#> locale:
#> [1] LC_COLLATE=English_Australia.utf8  LC_CTYPE=English_Australia.utf8    LC_MONETARY=English_Australia.utf8
#> [4] LC_NUMERIC=C                       LC_TIME=English_Australia.utf8    
#> 
#> time zone: Australia/Sydney
#> tzcode source: internal
#> 
#> attached base packages:
#> [1] stats4    stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#>  [1] SpatialExperiment_1.21.0    SingleCellExperiment_1.33.2 SummarizedExperiment_1.41.1 Biobase_2.71.0             
#>  [5] GenomicRanges_1.63.2        Seqinfo_1.1.0               IRanges_2.45.0              S4Vectors_0.49.2           
#>  [9] BiocGenerics_0.57.1         generics_0.1.4              MatrixGenerics_1.23.0       matrixStats_1.5.0          
#> [13] blisa_1.0.0                
#> 
#> loaded via a namespace (and not attached):
#>  [1] gtable_0.3.6          circlize_0.4.18       shape_1.4.6.1         rjson_0.2.23          xfun_0.57            
#>  [6] ggplot2_4.0.3         GlobalOptions_0.1.4   lattice_0.22-9        Cairo_1.7-0           vctrs_0.7.3          
#> [11] tools_4.6.0           spdep_1.4-2           parallel_4.6.0        tibble_3.3.1          proxy_0.4-29         
#> [16] cluster_2.1.8.2       pkgconfig_2.0.3       Matrix_1.7-5          KernSmooth_2.23-26    RColorBrewer_1.1-3   
#> [21] S7_0.2.2              lifecycle_1.0.5       deldir_2.0-4          compiler_4.6.0        farver_2.1.2         
#> [26] codetools_0.2-20      ComplexHeatmap_2.28.0 clue_0.3-68           class_7.3-23          fastLISA_1.0.1       
#> [31] pillar_1.11.1         crayon_1.5.3          classInt_0.4-11       DelayedArray_0.37.1   dbscan_1.2.4         
#> [36] wk_0.9.5              magick_2.9.1          iterators_1.0.14      boot_1.3-32           abind_1.4-8          
#> [41] foreach_1.5.2         tidyselect_1.2.1      digest_0.6.39         sf_1.1-1              dplyr_1.2.1          
#> [46] labeling_0.4.3        grid_4.6.0            colorspace_2.1-2      cli_3.6.6             SparseArray_1.11.13  
#> [51] magrittr_2.0.5        S4Arrays_1.11.1       e1071_1.7-17          withr_3.0.3           scales_1.4.0         
#> [56] sp_2.2-1              spData_2.3.5          XVector_0.51.0        otel_0.2.0            png_0.1-9            
#> [61] GetoptLong_1.1.1      evaluate_1.0.5        knitr_1.51            doParallel_1.0.17     viridisLite_0.4.3    
#> [66] s2_1.1.11             rlang_1.2.0           Rcpp_1.1.1-1.1        glue_1.8.1            DBI_1.3.0            
#> [71] R6_2.6.1              units_1.0-1