Contents

1 Installation

# Install the package from Bioconductor
if (!requireNamespace("BiocManager", quietly = TRUE)) {
  install.packages("BiocManager")
}
BiocManager::install("Statial")

2 Load packages

# Loading required packages
library(Statial)
library(spicyR)
library(ClassifyR)
library(lisaClust)
library(dplyr)
library(SingleCellExperiment)
library(ggplot2)
library(ggsurvfit)
library(survival)
library(tibble)
library(treekoR)

theme_set(theme_classic())
nCores <- 1

3 Overview

There are over 37 trillion cells in the human body, each taking up different forms and functions. The behaviour of these cells can be described by canonical characteristics, but their functions can also dynamically change based on their environmental context. Understanding the interplay between cells is key to understanding their mechanisms of action and how they contribute to human disease. Statial is a suite of functions to quantify the spatial relationships between cell types. This guide will provide a step-by-step overview of some key functions within Statial.

4 Loading example data

To illustrate the functionality of Statial we will use a multiplexed ion beam imaging by time-of-flight (MIBI-TOF) dataset profiling tissue from triple-negative breast cancer patients\(^1\) by Keren et al., 2018. This dataset simultaneously quantifies in situ expression of 36 proteins in 34 immune rich patients. Note: The data contains some “uninformative” probes and the original cohort included 41 patients.

These images are stored in a SingleCellExperiment object called kerenSCE. This object contains 57811 cells across 10 images and includes information on cell type and patient survival.

Note: The original dataset was reduced down from 41 images to 10 images for the purposes of this vignette, due to size restrictions.

# Load head and neck data
data("kerenSCE")

kerenSCE
#> class: SingleCellExperiment 
#> dim: 48 57811 
#> metadata(0):
#> assays(1): intensities
#> rownames(48): Na Si ... Ta Au
#> rowData names(0):
#> colnames(57811): 1 2 ... 171281 171282
#> colData names(8): x y ... Survival_days_capped Censored
#> reducedDimNames(0):
#> mainExpName: NULL
#> altExpNames(0):

5 Kontextual: Context aware spatial relationships

Kontextual is a method for performing inference on cell localisation which explicitly defines the contexts in which spatial relationships between cells can be identified and interpreted. These contexts may represent landmarks, spatial domains, or groups of functionally similar cells which are consistent across regions. By modelling spatial relationships between cells relative to these contexts, Kontextual produces robust spatial quantifications that are not confounded by biases such as the choice of region to image and the tissue structure present in the images.

In this example we demonstrate how cell type hierarchies can be used as a means to derive appropriate “contexts” for the evaluation of cell localisation. We then demonstrate the types of conclusions which Kontextual enables.

5.1 Using cell type hierarchies to define a “context”

A cell type hierarchy may be used to define the “context” in which cell type relationships are evaluated within. A cell type hierarchy defines how cell types are functionally related to one another. The bottom of the hierarchy represents homogeneous populations of a cell type (child), the cell populations at the nodes of the hierarchy represent broader parent populations with shared generalised function. For example CD4 T cells may be considered a child population to the Immune parent population.

There are two ways to define the cell type hierarchy. First, they can be defined based on our biological understanding of the cell types. We can represent this by creating a named list containing the names of each parent and the associated vector of child cell types.

Note: The all vector must be created to include cell types which do not have a parent e.g. the undefined cell type in this data set.

# Examine all cell types in image
unique(kerenSCE$cellType)
#>  [1] "Keratin_Tumour" "CD3_Cell"       "B"              "CD4_Cell"      
#>  [5] "Dc/Mono"        "Unidentified"   "Macrophages"    "CD8_Cell"      
#>  [9] "other immune"   "Endothelial"    "Mono/Neu"       "Mesenchymal"   
#> [13] "Neutrophils"    "NK"             "Tumour"         "DC"            
#> [17] "Tregs"

# Named list of parents and their child cell types
biologicalHierarchy = list(
  "tumour" = c("Keratin_Tumour", "Tumour"),
  "bcells" = c("B"),
  "tcells" = c("CD3_Cell", "CD4_Cell", "CD8_Cell", "Tregs"),
  "myeloid" = c("Dc/Mono", "DC", "Mono/Neu", "Macrophages", "Neutrophils"),
  "tissue" = c("Endothelial", "Mesenchymal")
)

# Adding more broader immune parent populationse
biologicalHierarchy$immune = c(biologicalHierarchy$bcells,
                               biologicalHierarchy$tcells,
                               biologicalHierarchy$myeloid,
                               "NK", "other immune")


# Creating a vector for all cellTypes
all <- unique(kerenSCE$cellType)

Alternatively, you can use the treeKor bioconductor package treekoR to define these hierarchies in a data driven way.

Note: These parent populations may not be accurate as we are using a small subset of the data.

# Calculate hierarchy using treekoR
kerenTree <- treekoR::getClusterTree(t(assay(kerenSCE, "intensities")),
                            kerenSCE$cellType,
                            hierarchy_method="hopach",
                            hopach_K = 1)

# Convert treekoR result to a name list of parents and children.
treekorParents = getParentPhylo(kerenTree)

treekorParents
#> $parent_1
#> [1] "Dc/Mono"     "Macrophages" "NK"         
#> 
#> $parent_2
#> [1] "CD3_Cell" "B"        "CD4_Cell" "CD8_Cell" "Tregs"   
#> 
#> $parent_3
#> [1] "Endothelial" "Mesenchymal" "DC"         
#> 
#> $parent_4
#> [1] "Unidentified" "other immune" "Mono/Neu"     "Neutrophils"  "Tumour"

5.2 Application on triple negative breast cancer image.

Here we examine an image highlighted in the Keren et al. 2018 manuscript where accounting for context information enables new conclusions. In image 6 of the Keren et al. dataset, we can see that p53+ tumour cells and immune cells are dispersed i.e. these two cell types are not mixing. However we can also see that p53+ tumour cells appear much more localised to immune cells relative to the tumour context (tumour cells and p53+ tumour cells).

# Lets define a new cell type vector
kerenSCE$cellTypeNew <- kerenSCE$cellType

# Select for all cells that express higher than baseline level of p53
p53Pos <- assay(kerenSCE)["p53", ] > -0.300460

# Find p53+ tumour cells
kerenSCE$cellTypeNew[kerenSCE$cellType %in% biologicalHierarchy$tumour] <- "Tumour"
kerenSCE$cellTypeNew[p53Pos & kerenSCE$cellType %in% biologicalHierarchy$tumour] <- "p53_Tumour"

# Group all immune cells under the name "Immune"
kerenSCE$cellTypeNew[kerenSCE$cellType %in% biologicalHierarchy$immune] <- "Immune"

# Plot image 6
kerenSCE |>
  colData() |>
  as.data.frame() |>
  filter(imageID == "6") |>
  filter(cellTypeNew %in% c("Immune", "Tumour", "p53_Tumour")) |>
  arrange(cellTypeNew) |>
  ggplot(aes(x = x, y = y, color = cellTypeNew)) +
  geom_point(size = 1) +
  scale_colour_manual(values = c("Immune" = "#505050", "p53_Tumour" = "#64BC46", "Tumour" = "#D6D6D6")) +
  guides(colour = guide_legend(title = "Cell types", override.aes = list(size = 3)))

Kontextual accepts a SingleCellExperiment object, a single image, or list of images from a SingleCellExperiment object, which gets passed into the cells argument. The two cell types which will be evaluated are specified in the to and from arguments. A parent population must also be specified in the parent argument, note the parent cell population must include the to cell type. The argument r will specify the radius which the cell relationship will be evaluated on. Kontextual supports parallel processing, the number of cores can be specified using the cores argument. Kontextual can take a single value or multiple values for each argument and will test all combinations of the arguments specified.

We can calculate these relationships across all images for a single radius (r = 100). Small radii will examine local spatial relationships, whereas larger radii will examine global spatial relationships.

p53_Kontextual <- Kontextual(
  cells = kerenSCE,
  r = 100,
  from = "Immune",
  to = "p53_Tumour",
  parent = c("p53_Tumour", "Tumour"),
  cellType = "cellTypeNew"
)

p53_Kontextual
#>    imageID               test   original  kontextual   r inhomL
#> 1        1 Immune__p53_Tumour -16.212016  -1.6815952 100  FALSE
#> 2       14 Immune__p53_Tumour -14.671281  -4.2879138 100  FALSE
#> 3       18 Immune__p53_Tumour  -1.953366   0.5795853 100  FALSE
#> 4       21 Immune__p53_Tumour -14.300802  -7.1425133 100  FALSE
#> 5       29 Immune__p53_Tumour -20.728463  -7.0172785 100  FALSE
#> 6        3 Immune__p53_Tumour   1.719549  44.5060581 100  FALSE
#> 7       32 Immune__p53_Tumour -18.174569 -10.8972277 100  FALSE
#> 8       35 Immune__p53_Tumour -75.980619 -66.2395276 100  FALSE
#> 9        5 Immune__p53_Tumour         NA          NA 100  FALSE
#> 10       6 Immune__p53_Tumour -24.897348  -1.2724241 100  FALSE

The kontextCurve function plots the L-function value and Kontextual values over a range of radii. If the points lie above the red line (expected pattern) then localisation is indicated for that radius, if the points lie below the red line then dispersion is indicated.

As seen in the following plot the L-function produces negative values over a range of radii, indicating that p53+ tumour cells and immune cells are dispersed from one another. However by taking into account the tumour context, Kontextual shows positive values over some radii, indicating localisation between p53+ tumour cells and immune cells.

curves <- kontextCurve(
  cells = kerenSCE,
  from = "Immune",
  to = "p53_Tumour",
  parent = c("p53_Tumour", "Tumour"),
  rs = seq(50, 510, 50),
  image = "6",
  cellType = "cellTypeNew",
  cores = nCores
)

kontextPlot(curves)

Alternatively all pairwise cell relationships and their corresponding parent in the dataset can be tested. A data frame with all pairwise combinations can be creating using the parentCombinations function. This function takes in a vector of all the cells, as well as the named list of parents and children created earlier in the parentList argument. As shown below the output is a data frame specifying the to, from, and parent arguments for Kontextual.

Note: the output of getPhyloParent may also be using the in the parentList argument, for example if you wanted to use the treekoR defined hierarchy instead.

# Get all relationships between cell types and their parents
parentDf <- parentCombinations(
  all = all,
  parentList = biologicalHierarchy
)

parentDf
#>               from             to       parent parent_name
#> 1   Keratin_Tumour              B            B      bcells
#> 2         CD3_Cell              B            B      bcells
#> 3         CD4_Cell              B            B      bcells
#> 4          Dc/Mono              B            B      bcells
#> 5     Unidentified              B            B      bcells
#> 6      Macrophages              B            B      bcells
#> 7         CD8_Cell              B            B      bcells
#> 8     other immune              B            B      bcells
#> 9      Endothelial              B            B      bcells
#> 10        Mono/Neu              B            B      bcells
#> 11     Mesenchymal              B            B      bcells
#> 12     Neutrophils              B            B      bcells
#> 13              NK              B            B      bcells
#> 14          Tumour              B            B      bcells
#> 15              DC              B            B      bcells
#> 16           Tregs              B            B      bcells
#> 17  Keratin_Tumour              B B, CD3_C....      immune
#> 18        CD3_Cell              B B, CD3_C....      immune
#> 19        CD4_Cell              B B, CD3_C....      immune
#> 20         Dc/Mono              B B, CD3_C....      immune
#> 21    Unidentified              B B, CD3_C....      immune
#> 22     Macrophages              B B, CD3_C....      immune
#> 23        CD8_Cell              B B, CD3_C....      immune
#> 24    other immune              B B, CD3_C....      immune
#> 25     Endothelial              B B, CD3_C....      immune
#> 26        Mono/Neu              B B, CD3_C....      immune
#> 27     Mesenchymal              B B, CD3_C....      immune
#> 28     Neutrophils              B B, CD3_C....      immune
#> 29              NK              B B, CD3_C....      immune
#> 30          Tumour              B B, CD3_C....      immune
#> 31              DC              B B, CD3_C....      immune
#> 32           Tregs              B B, CD3_C....      immune
#> 33  Keratin_Tumour       CD3_Cell B, CD3_C....      immune
#> 34               B       CD3_Cell B, CD3_C....      immune
#> 35        CD4_Cell       CD3_Cell B, CD3_C....      immune
#> 36         Dc/Mono       CD3_Cell B, CD3_C....      immune
#> 37    Unidentified       CD3_Cell B, CD3_C....      immune
#> 38     Macrophages       CD3_Cell B, CD3_C....      immune
#> 39        CD8_Cell       CD3_Cell B, CD3_C....      immune
#> 40    other immune       CD3_Cell B, CD3_C....      immune
#> 41     Endothelial       CD3_Cell B, CD3_C....      immune
#> 42        Mono/Neu       CD3_Cell B, CD3_C....      immune
#> 43     Mesenchymal       CD3_Cell B, CD3_C....      immune
#> 44     Neutrophils       CD3_Cell B, CD3_C....      immune
#> 45              NK       CD3_Cell B, CD3_C....      immune
#> 46          Tumour       CD3_Cell B, CD3_C....      immune
#> 47              DC       CD3_Cell B, CD3_C....      immune
#> 48           Tregs       CD3_Cell B, CD3_C....      immune
#> 49  Keratin_Tumour       CD4_Cell B, CD3_C....      immune
#> 50        CD3_Cell       CD4_Cell B, CD3_C....      immune
#> 51               B       CD4_Cell B, CD3_C....      immune
#> 52         Dc/Mono       CD4_Cell B, CD3_C....      immune
#> 53    Unidentified       CD4_Cell B, CD3_C....      immune
#> 54     Macrophages       CD4_Cell B, CD3_C....      immune
#> 55        CD8_Cell       CD4_Cell B, CD3_C....      immune
#> 56    other immune       CD4_Cell B, CD3_C....      immune
#> 57     Endothelial       CD4_Cell B, CD3_C....      immune
#> 58        Mono/Neu       CD4_Cell B, CD3_C....      immune
#> 59     Mesenchymal       CD4_Cell B, CD3_C....      immune
#> 60     Neutrophils       CD4_Cell B, CD3_C....      immune
#> 61              NK       CD4_Cell B, CD3_C....      immune
#> 62          Tumour       CD4_Cell B, CD3_C....      immune
#> 63              DC       CD4_Cell B, CD3_C....      immune
#> 64           Tregs       CD4_Cell B, CD3_C....      immune
#> 65  Keratin_Tumour       CD8_Cell B, CD3_C....      immune
#> 66        CD3_Cell       CD8_Cell B, CD3_C....      immune
#> 67               B       CD8_Cell B, CD3_C....      immune
#> 68        CD4_Cell       CD8_Cell B, CD3_C....      immune
#> 69         Dc/Mono       CD8_Cell B, CD3_C....      immune
#> 70    Unidentified       CD8_Cell B, CD3_C....      immune
#> 71     Macrophages       CD8_Cell B, CD3_C....      immune
#> 72    other immune       CD8_Cell B, CD3_C....      immune
#> 73     Endothelial       CD8_Cell B, CD3_C....      immune
#> 74        Mono/Neu       CD8_Cell B, CD3_C....      immune
#> 75     Mesenchymal       CD8_Cell B, CD3_C....      immune
#> 76     Neutrophils       CD8_Cell B, CD3_C....      immune
#> 77              NK       CD8_Cell B, CD3_C....      immune
#> 78          Tumour       CD8_Cell B, CD3_C....      immune
#> 79              DC       CD8_Cell B, CD3_C....      immune
#> 80           Tregs       CD8_Cell B, CD3_C....      immune
#> 81  Keratin_Tumour             DC B, CD3_C....      immune
#> 82        CD3_Cell             DC B, CD3_C....      immune
#> 83               B             DC B, CD3_C....      immune
#> 84        CD4_Cell             DC B, CD3_C....      immune
#> 85         Dc/Mono             DC B, CD3_C....      immune
#> 86    Unidentified             DC B, CD3_C....      immune
#> 87     Macrophages             DC B, CD3_C....      immune
#> 88        CD8_Cell             DC B, CD3_C....      immune
#> 89    other immune             DC B, CD3_C....      immune
#> 90     Endothelial             DC B, CD3_C....      immune
#> 91        Mono/Neu             DC B, CD3_C....      immune
#> 92     Mesenchymal             DC B, CD3_C....      immune
#> 93     Neutrophils             DC B, CD3_C....      immune
#> 94              NK             DC B, CD3_C....      immune
#> 95          Tumour             DC B, CD3_C....      immune
#> 96           Tregs             DC B, CD3_C....      immune
#> 97  Keratin_Tumour        Dc/Mono B, CD3_C....      immune
#> 98        CD3_Cell        Dc/Mono B, CD3_C....      immune
#> 99               B        Dc/Mono B, CD3_C....      immune
#> 100       CD4_Cell        Dc/Mono B, CD3_C....      immune
#> 101   Unidentified        Dc/Mono B, CD3_C....      immune
#> 102    Macrophages        Dc/Mono B, CD3_C....      immune
#> 103       CD8_Cell        Dc/Mono B, CD3_C....      immune
#> 104   other immune        Dc/Mono B, CD3_C....      immune
#> 105    Endothelial        Dc/Mono B, CD3_C....      immune
#> 106       Mono/Neu        Dc/Mono B, CD3_C....      immune
#> 107    Mesenchymal        Dc/Mono B, CD3_C....      immune
#> 108    Neutrophils        Dc/Mono B, CD3_C....      immune
#> 109             NK        Dc/Mono B, CD3_C....      immune
#> 110         Tumour        Dc/Mono B, CD3_C....      immune
#> 111             DC        Dc/Mono B, CD3_C....      immune
#> 112          Tregs        Dc/Mono B, CD3_C....      immune
#> 113 Keratin_Tumour    Macrophages B, CD3_C....      immune
#> 114       CD3_Cell    Macrophages B, CD3_C....      immune
#> 115              B    Macrophages B, CD3_C....      immune
#> 116       CD4_Cell    Macrophages B, CD3_C....      immune
#> 117        Dc/Mono    Macrophages B, CD3_C....      immune
#> 118   Unidentified    Macrophages B, CD3_C....      immune
#> 119       CD8_Cell    Macrophages B, CD3_C....      immune
#> 120   other immune    Macrophages B, CD3_C....      immune
#> 121    Endothelial    Macrophages B, CD3_C....      immune
#> 122       Mono/Neu    Macrophages B, CD3_C....      immune
#> 123    Mesenchymal    Macrophages B, CD3_C....      immune
#> 124    Neutrophils    Macrophages B, CD3_C....      immune
#> 125             NK    Macrophages B, CD3_C....      immune
#> 126         Tumour    Macrophages B, CD3_C....      immune
#> 127             DC    Macrophages B, CD3_C....      immune
#> 128          Tregs    Macrophages B, CD3_C....      immune
#> 129 Keratin_Tumour       Mono/Neu B, CD3_C....      immune
#> 130       CD3_Cell       Mono/Neu B, CD3_C....      immune
#> 131              B       Mono/Neu B, CD3_C....      immune
#> 132       CD4_Cell       Mono/Neu B, CD3_C....      immune
#> 133        Dc/Mono       Mono/Neu B, CD3_C....      immune
#> 134   Unidentified       Mono/Neu B, CD3_C....      immune
#> 135    Macrophages       Mono/Neu B, CD3_C....      immune
#> 136       CD8_Cell       Mono/Neu B, CD3_C....      immune
#> 137   other immune       Mono/Neu B, CD3_C....      immune
#> 138    Endothelial       Mono/Neu B, CD3_C....      immune
#> 139    Mesenchymal       Mono/Neu B, CD3_C....      immune
#> 140    Neutrophils       Mono/Neu B, CD3_C....      immune
#> 141             NK       Mono/Neu B, CD3_C....      immune
#> 142         Tumour       Mono/Neu B, CD3_C....      immune
#> 143             DC       Mono/Neu B, CD3_C....      immune
#> 144          Tregs       Mono/Neu B, CD3_C....      immune
#> 145 Keratin_Tumour             NK B, CD3_C....      immune
#> 146       CD3_Cell             NK B, CD3_C....      immune
#> 147              B             NK B, CD3_C....      immune
#> 148       CD4_Cell             NK B, CD3_C....      immune
#> 149        Dc/Mono             NK B, CD3_C....      immune
#> 150   Unidentified             NK B, CD3_C....      immune
#> 151    Macrophages             NK B, CD3_C....      immune
#> 152       CD8_Cell             NK B, CD3_C....      immune
#> 153   other immune             NK B, CD3_C....      immune
#> 154    Endothelial             NK B, CD3_C....      immune
#> 155       Mono/Neu             NK B, CD3_C....      immune
#> 156    Mesenchymal             NK B, CD3_C....      immune
#> 157    Neutrophils             NK B, CD3_C....      immune
#> 158         Tumour             NK B, CD3_C....      immune
#> 159             DC             NK B, CD3_C....      immune
#> 160          Tregs             NK B, CD3_C....      immune
#> 161 Keratin_Tumour    Neutrophils B, CD3_C....      immune
#> 162       CD3_Cell    Neutrophils B, CD3_C....      immune
#> 163              B    Neutrophils B, CD3_C....      immune
#> 164       CD4_Cell    Neutrophils B, CD3_C....      immune
#> 165        Dc/Mono    Neutrophils B, CD3_C....      immune
#> 166   Unidentified    Neutrophils B, CD3_C....      immune
#> 167    Macrophages    Neutrophils B, CD3_C....      immune
#> 168       CD8_Cell    Neutrophils B, CD3_C....      immune
#> 169   other immune    Neutrophils B, CD3_C....      immune
#> 170    Endothelial    Neutrophils B, CD3_C....      immune
#> 171       Mono/Neu    Neutrophils B, CD3_C....      immune
#> 172    Mesenchymal    Neutrophils B, CD3_C....      immune
#> 173             NK    Neutrophils B, CD3_C....      immune
#> 174         Tumour    Neutrophils B, CD3_C....      immune
#> 175             DC    Neutrophils B, CD3_C....      immune
#> 176          Tregs    Neutrophils B, CD3_C....      immune
#> 177 Keratin_Tumour          Tregs B, CD3_C....      immune
#> 178       CD3_Cell          Tregs B, CD3_C....      immune
#> 179              B          Tregs B, CD3_C....      immune
#> 180       CD4_Cell          Tregs B, CD3_C....      immune
#> 181        Dc/Mono          Tregs B, CD3_C....      immune
#> 182   Unidentified          Tregs B, CD3_C....      immune
#> 183    Macrophages          Tregs B, CD3_C....      immune
#> 184       CD8_Cell          Tregs B, CD3_C....      immune
#> 185   other immune          Tregs B, CD3_C....      immune
#> 186    Endothelial          Tregs B, CD3_C....      immune
#> 187       Mono/Neu          Tregs B, CD3_C....      immune
#> 188    Mesenchymal          Tregs B, CD3_C....      immune
#> 189    Neutrophils          Tregs B, CD3_C....      immune
#> 190             NK          Tregs B, CD3_C....      immune
#> 191         Tumour          Tregs B, CD3_C....      immune
#> 192             DC          Tregs B, CD3_C....      immune
#> 193 Keratin_Tumour   other immune B, CD3_C....      immune
#> 194       CD3_Cell   other immune B, CD3_C....      immune
#> 195              B   other immune B, CD3_C....      immune
#> 196       CD4_Cell   other immune B, CD3_C....      immune
#> 197        Dc/Mono   other immune B, CD3_C....      immune
#> 198   Unidentified   other immune B, CD3_C....      immune
#> 199    Macrophages   other immune B, CD3_C....      immune
#> 200       CD8_Cell   other immune B, CD3_C....      immune
#> 201    Endothelial   other immune B, CD3_C....      immune
#> 202       Mono/Neu   other immune B, CD3_C....      immune
#> 203    Mesenchymal   other immune B, CD3_C....      immune
#> 204    Neutrophils   other immune B, CD3_C....      immune
#> 205             NK   other immune B, CD3_C....      immune
#> 206         Tumour   other immune B, CD3_C....      immune
#> 207             DC   other immune B, CD3_C....      immune
#> 208          Tregs   other immune B, CD3_C....      immune
#> 209 Keratin_Tumour             DC Dc/Mono,....     myeloid
#> 210       CD3_Cell             DC Dc/Mono,....     myeloid
#> 211              B             DC Dc/Mono,....     myeloid
#> 212       CD4_Cell             DC Dc/Mono,....     myeloid
#> 213        Dc/Mono             DC Dc/Mono,....     myeloid
#> 214   Unidentified             DC Dc/Mono,....     myeloid
#> 215    Macrophages             DC Dc/Mono,....     myeloid
#> 216       CD8_Cell             DC Dc/Mono,....     myeloid
#> 217   other immune             DC Dc/Mono,....     myeloid
#> 218    Endothelial             DC Dc/Mono,....     myeloid
#> 219       Mono/Neu             DC Dc/Mono,....     myeloid
#> 220    Mesenchymal             DC Dc/Mono,....     myeloid
#> 221    Neutrophils             DC Dc/Mono,....     myeloid
#> 222             NK             DC Dc/Mono,....     myeloid
#> 223         Tumour             DC Dc/Mono,....     myeloid
#> 224          Tregs             DC Dc/Mono,....     myeloid
#> 225 Keratin_Tumour        Dc/Mono Dc/Mono,....     myeloid
#> 226       CD3_Cell        Dc/Mono Dc/Mono,....     myeloid
#> 227              B        Dc/Mono Dc/Mono,....     myeloid
#> 228       CD4_Cell        Dc/Mono Dc/Mono,....     myeloid
#> 229   Unidentified        Dc/Mono Dc/Mono,....     myeloid
#> 230    Macrophages        Dc/Mono Dc/Mono,....     myeloid
#> 231       CD8_Cell        Dc/Mono Dc/Mono,....     myeloid
#> 232   other immune        Dc/Mono Dc/Mono,....     myeloid
#> 233    Endothelial        Dc/Mono Dc/Mono,....     myeloid
#> 234       Mono/Neu        Dc/Mono Dc/Mono,....     myeloid
#> 235    Mesenchymal        Dc/Mono Dc/Mono,....     myeloid
#> 236    Neutrophils        Dc/Mono Dc/Mono,....     myeloid
#> 237             NK        Dc/Mono Dc/Mono,....     myeloid
#> 238         Tumour        Dc/Mono Dc/Mono,....     myeloid
#> 239             DC        Dc/Mono Dc/Mono,....     myeloid
#> 240          Tregs        Dc/Mono Dc/Mono,....     myeloid
#> 241 Keratin_Tumour    Macrophages Dc/Mono,....     myeloid
#> 242       CD3_Cell    Macrophages Dc/Mono,....     myeloid
#> 243              B    Macrophages Dc/Mono,....     myeloid
#> 244       CD4_Cell    Macrophages Dc/Mono,....     myeloid
#> 245        Dc/Mono    Macrophages Dc/Mono,....     myeloid
#> 246   Unidentified    Macrophages Dc/Mono,....     myeloid
#> 247       CD8_Cell    Macrophages Dc/Mono,....     myeloid
#> 248   other immune    Macrophages Dc/Mono,....     myeloid
#> 249    Endothelial    Macrophages Dc/Mono,....     myeloid
#> 250       Mono/Neu    Macrophages Dc/Mono,....     myeloid
#> 251    Mesenchymal    Macrophages Dc/Mono,....     myeloid
#> 252    Neutrophils    Macrophages Dc/Mono,....     myeloid
#> 253             NK    Macrophages Dc/Mono,....     myeloid
#> 254         Tumour    Macrophages Dc/Mono,....     myeloid
#> 255             DC    Macrophages Dc/Mono,....     myeloid
#> 256          Tregs    Macrophages Dc/Mono,....     myeloid
#> 257 Keratin_Tumour       Mono/Neu Dc/Mono,....     myeloid
#> 258       CD3_Cell       Mono/Neu Dc/Mono,....     myeloid
#> 259              B       Mono/Neu Dc/Mono,....     myeloid
#> 260       CD4_Cell       Mono/Neu Dc/Mono,....     myeloid
#> 261        Dc/Mono       Mono/Neu Dc/Mono,....     myeloid
#> 262   Unidentified       Mono/Neu Dc/Mono,....     myeloid
#> 263    Macrophages       Mono/Neu Dc/Mono,....     myeloid
#> 264       CD8_Cell       Mono/Neu Dc/Mono,....     myeloid
#> 265   other immune       Mono/Neu Dc/Mono,....     myeloid
#> 266    Endothelial       Mono/Neu Dc/Mono,....     myeloid
#> 267    Mesenchymal       Mono/Neu Dc/Mono,....     myeloid
#> 268    Neutrophils       Mono/Neu Dc/Mono,....     myeloid
#> 269             NK       Mono/Neu Dc/Mono,....     myeloid
#> 270         Tumour       Mono/Neu Dc/Mono,....     myeloid
#> 271             DC       Mono/Neu Dc/Mono,....     myeloid
#> 272          Tregs       Mono/Neu Dc/Mono,....     myeloid
#> 273 Keratin_Tumour    Neutrophils Dc/Mono,....     myeloid
#> 274       CD3_Cell    Neutrophils Dc/Mono,....     myeloid
#> 275              B    Neutrophils Dc/Mono,....     myeloid
#> 276       CD4_Cell    Neutrophils Dc/Mono,....     myeloid
#> 277        Dc/Mono    Neutrophils Dc/Mono,....     myeloid
#> 278   Unidentified    Neutrophils Dc/Mono,....     myeloid
#> 279    Macrophages    Neutrophils Dc/Mono,....     myeloid
#> 280       CD8_Cell    Neutrophils Dc/Mono,....     myeloid
#> 281   other immune    Neutrophils Dc/Mono,....     myeloid
#> 282    Endothelial    Neutrophils Dc/Mono,....     myeloid
#> 283       Mono/Neu    Neutrophils Dc/Mono,....     myeloid
#> 284    Mesenchymal    Neutrophils Dc/Mono,....     myeloid
#> 285             NK    Neutrophils Dc/Mono,....     myeloid
#> 286         Tumour    Neutrophils Dc/Mono,....     myeloid
#> 287             DC    Neutrophils Dc/Mono,....     myeloid
#> 288          Tregs    Neutrophils Dc/Mono,....     myeloid
#> 289 Keratin_Tumour       CD3_Cell CD3_Cell....      tcells
#> 290              B       CD3_Cell CD3_Cell....      tcells
#> 291       CD4_Cell       CD3_Cell CD3_Cell....      tcells
#> 292        Dc/Mono       CD3_Cell CD3_Cell....      tcells
#> 293   Unidentified       CD3_Cell CD3_Cell....      tcells
#> 294    Macrophages       CD3_Cell CD3_Cell....      tcells
#> 295       CD8_Cell       CD3_Cell CD3_Cell....      tcells
#> 296   other immune       CD3_Cell CD3_Cell....      tcells
#> 297    Endothelial       CD3_Cell CD3_Cell....      tcells
#> 298       Mono/Neu       CD3_Cell CD3_Cell....      tcells
#> 299    Mesenchymal       CD3_Cell CD3_Cell....      tcells
#> 300    Neutrophils       CD3_Cell CD3_Cell....      tcells
#> 301             NK       CD3_Cell CD3_Cell....      tcells
#> 302         Tumour       CD3_Cell CD3_Cell....      tcells
#> 303             DC       CD3_Cell CD3_Cell....      tcells
#> 304          Tregs       CD3_Cell CD3_Cell....      tcells
#> 305 Keratin_Tumour       CD4_Cell CD3_Cell....      tcells
#> 306       CD3_Cell       CD4_Cell CD3_Cell....      tcells
#> 307              B       CD4_Cell CD3_Cell....      tcells
#> 308        Dc/Mono       CD4_Cell CD3_Cell....      tcells
#> 309   Unidentified       CD4_Cell CD3_Cell....      tcells
#> 310    Macrophages       CD4_Cell CD3_Cell....      tcells
#> 311       CD8_Cell       CD4_Cell CD3_Cell....      tcells
#> 312   other immune       CD4_Cell CD3_Cell....      tcells
#> 313    Endothelial       CD4_Cell CD3_Cell....      tcells
#> 314       Mono/Neu       CD4_Cell CD3_Cell....      tcells
#> 315    Mesenchymal       CD4_Cell CD3_Cell....      tcells
#> 316    Neutrophils       CD4_Cell CD3_Cell....      tcells
#> 317             NK       CD4_Cell CD3_Cell....      tcells
#> 318         Tumour       CD4_Cell CD3_Cell....      tcells
#> 319             DC       CD4_Cell CD3_Cell....      tcells
#> 320          Tregs       CD4_Cell CD3_Cell....      tcells
#> 321 Keratin_Tumour       CD8_Cell CD3_Cell....      tcells
#> 322       CD3_Cell       CD8_Cell CD3_Cell....      tcells
#> 323              B       CD8_Cell CD3_Cell....      tcells
#> 324       CD4_Cell       CD8_Cell CD3_Cell....      tcells
#> 325        Dc/Mono       CD8_Cell CD3_Cell....      tcells
#> 326   Unidentified       CD8_Cell CD3_Cell....      tcells
#> 327    Macrophages       CD8_Cell CD3_Cell....      tcells
#> 328   other immune       CD8_Cell CD3_Cell....      tcells
#> 329    Endothelial       CD8_Cell CD3_Cell....      tcells
#> 330       Mono/Neu       CD8_Cell CD3_Cell....      tcells
#> 331    Mesenchymal       CD8_Cell CD3_Cell....      tcells
#> 332    Neutrophils       CD8_Cell CD3_Cell....      tcells
#> 333             NK       CD8_Cell CD3_Cell....      tcells
#> 334         Tumour       CD8_Cell CD3_Cell....      tcells
#> 335             DC       CD8_Cell CD3_Cell....      tcells
#> 336          Tregs       CD8_Cell CD3_Cell....      tcells
#> 337 Keratin_Tumour          Tregs CD3_Cell....      tcells
#> 338       CD3_Cell          Tregs CD3_Cell....      tcells
#> 339              B          Tregs CD3_Cell....      tcells
#> 340       CD4_Cell          Tregs CD3_Cell....      tcells
#> 341        Dc/Mono          Tregs CD3_Cell....      tcells
#> 342   Unidentified          Tregs CD3_Cell....      tcells
#> 343    Macrophages          Tregs CD3_Cell....      tcells
#> 344       CD8_Cell          Tregs CD3_Cell....      tcells
#> 345   other immune          Tregs CD3_Cell....      tcells
#> 346    Endothelial          Tregs CD3_Cell....      tcells
#> 347       Mono/Neu          Tregs CD3_Cell....      tcells
#> 348    Mesenchymal          Tregs CD3_Cell....      tcells
#> 349    Neutrophils          Tregs CD3_Cell....      tcells
#> 350             NK          Tregs CD3_Cell....      tcells
#> 351         Tumour          Tregs CD3_Cell....      tcells
#> 352             DC          Tregs CD3_Cell....      tcells
#> 353 Keratin_Tumour    Endothelial Endothel....      tissue
#> 354       CD3_Cell    Endothelial Endothel....      tissue
#> 355              B    Endothelial Endothel....      tissue
#> 356       CD4_Cell    Endothelial Endothel....      tissue
#> 357        Dc/Mono    Endothelial Endothel....      tissue
#> 358   Unidentified    Endothelial Endothel....      tissue
#> 359    Macrophages    Endothelial Endothel....      tissue
#> 360       CD8_Cell    Endothelial Endothel....      tissue
#> 361   other immune    Endothelial Endothel....      tissue
#> 362       Mono/Neu    Endothelial Endothel....      tissue
#> 363    Mesenchymal    Endothelial Endothel....      tissue
#> 364    Neutrophils    Endothelial Endothel....      tissue
#> 365             NK    Endothelial Endothel....      tissue
#> 366         Tumour    Endothelial Endothel....      tissue
#> 367             DC    Endothelial Endothel....      tissue
#> 368          Tregs    Endothelial Endothel....      tissue
#> 369 Keratin_Tumour    Mesenchymal Endothel....      tissue
#> 370       CD3_Cell    Mesenchymal Endothel....      tissue
#> 371              B    Mesenchymal Endothel....      tissue
#> 372       CD4_Cell    Mesenchymal Endothel....      tissue
#> 373        Dc/Mono    Mesenchymal Endothel....      tissue
#> 374   Unidentified    Mesenchymal Endothel....      tissue
#> 375    Macrophages    Mesenchymal Endothel....      tissue
#> 376       CD8_Cell    Mesenchymal Endothel....      tissue
#> 377   other immune    Mesenchymal Endothel....      tissue
#> 378    Endothelial    Mesenchymal Endothel....      tissue
#> 379       Mono/Neu    Mesenchymal Endothel....      tissue
#> 380    Neutrophils    Mesenchymal Endothel....      tissue
#> 381             NK    Mesenchymal Endothel....      tissue
#> 382         Tumour    Mesenchymal Endothel....      tissue
#> 383             DC    Mesenchymal Endothel....      tissue
#> 384          Tregs    Mesenchymal Endothel....      tissue
#> 385       CD3_Cell Keratin_Tumour Keratin_....      tumour
#> 386              B Keratin_Tumour Keratin_....      tumour
#> 387       CD4_Cell Keratin_Tumour Keratin_....      tumour
#> 388        Dc/Mono Keratin_Tumour Keratin_....      tumour
#> 389   Unidentified Keratin_Tumour Keratin_....      tumour
#> 390    Macrophages Keratin_Tumour Keratin_....      tumour
#> 391       CD8_Cell Keratin_Tumour Keratin_....      tumour
#> 392   other immune Keratin_Tumour Keratin_....      tumour
#> 393    Endothelial Keratin_Tumour Keratin_....      tumour
#> 394       Mono/Neu Keratin_Tumour Keratin_....      tumour
#> 395    Mesenchymal Keratin_Tumour Keratin_....      tumour
#> 396    Neutrophils Keratin_Tumour Keratin_....      tumour
#> 397             NK Keratin_Tumour Keratin_....      tumour
#> 398         Tumour Keratin_Tumour Keratin_....      tumour
#> 399             DC Keratin_Tumour Keratin_....      tumour
#> 400          Tregs Keratin_Tumour Keratin_....      tumour
#> 401 Keratin_Tumour         Tumour Keratin_....      tumour
#> 402       CD3_Cell         Tumour Keratin_....      tumour
#> 403              B         Tumour Keratin_....      tumour
#> 404       CD4_Cell         Tumour Keratin_....      tumour
#> 405        Dc/Mono         Tumour Keratin_....      tumour
#> 406   Unidentified         Tumour Keratin_....      tumour
#> 407    Macrophages         Tumour Keratin_....      tumour
#> 408       CD8_Cell         Tumour Keratin_....      tumour
#> 409   other immune         Tumour Keratin_....      tumour
#> 410    Endothelial         Tumour Keratin_....      tumour
#> 411       Mono/Neu         Tumour Keratin_....      tumour
#> 412    Mesenchymal         Tumour Keratin_....      tumour
#> 413    Neutrophils         Tumour Keratin_....      tumour
#> 414             NK         Tumour Keratin_....      tumour
#> 415             DC         Tumour Keratin_....      tumour
#> 416          Tregs         Tumour Keratin_....      tumour

5.3 Calculating all pairwise relationships

Rather than specifying to, from, and parent in Kontextual, the output from parentCombinations can be inputed into Kontextual using the parentDf argument, to examine all pairwise relationships in the dataset. This chunk will take a signficant amount of time to run, for demonstration the results have been saved and are loaded in.

# Running Kontextual on all relationships across all images.
kerenKontextual <- Kontextual(
  cells = kerenSCE,
  parentDf = parentDf,
  r = 100,
  cores = nCores
)

For every pairwise relationship (named accordingly: from__to__parent) Kontextual outputs the L-function values (original) and the Kontextual value. The relationships where the L-function and Kontextual disagree (e.g. one metric is positive and the other is negative) represent relationships where adding context information results in different conclusions on the spatial relationship between the two cell types.

data("kerenKontextual")

head(kerenKontextual, 10)
#>    imageID                      test   original    kontextual   r inhomL
#> 1        1 Keratin_Tumour__B__bcells -32.547645  1.493952e+00 100  FALSE
#> 2       14 Keratin_Tumour__B__bcells         NA            NA 100  FALSE
#> 3       18 Keratin_Tumour__B__bcells  -2.879684  1.347071e+01 100  FALSE
#> 4       21 Keratin_Tumour__B__bcells         NA            NA 100  FALSE
#> 5       29 Keratin_Tumour__B__bcells         NA            NA 100  FALSE
#> 6        3 Keratin_Tumour__B__bcells -36.175444 -4.673469e+00 100  FALSE
#> 7       32 Keratin_Tumour__B__bcells -43.187880 -4.854109e+00 100  FALSE
#> 8       35 Keratin_Tumour__B__bcells -66.782273 -1.535179e+01 100  FALSE
#> 9        5 Keratin_Tumour__B__bcells -68.676955  1.421085e-14 100  FALSE
#> 10       6 Keratin_Tumour__B__bcells -31.393046  3.509126e-02 100  FALSE

5.4 Associating the relationships with survival outcomes.

To examine whether the features obtained from Statial are associated with patient outcomes or groupings, we can use the colTest function from SpicyR. To understand if survival outcomes differ significantly between 2 patient groups, specify type = "survival" in colTest. Here we examine which features are most associated with patient survival using the Kontextual values as an example. To do so, survival data is extracted from kerenSCE and converted into the survival object kerenSurv.

# Extracting survival data
survData <- kerenSCE |>
  colData() |>
  data.frame() |>
  select(imageID, Survival_days_capped, Censored) |>
  mutate(event = 1 - Censored) |>
  unique()

# Creating survival vector
kerenSurv <- Surv(survData$Survival_days_capped, survData$event)
names(kerenSurv) <- survData$imageID

In addition to this, the Kontextual results must be converted from a data.frame to a wide matrix, this can be done using prepMatrix. Note, to extract the original L-function values, specify column = "original" in prepMatrix.

# Converting Kontextual result into data matrix
kontextMat <- prepMatrix(kerenKontextual)

# Ensuring rownames of kontextMat match up with rownames of the survival vector
kontextMat <- kontextMat[names(kerenSurv), ]

# Replace NAs with 0
kontextMat[is.na(kontextMat)] <- 0

Finally, both the Kontextual matrix and survival object are passed into colTest, with type = "survival" to obtain the survival results.

# Running survival analysis
survivalResults <- spicyR::colTest(kontextMat, kerenSurv, type = "survival")


head(survivalResults)
#>                                       coef se.coef  pval adjPval
#> CD8_Cell__Neutrophils__myeloid       0.082   0.042 0.049    0.89
#> CD8_Cell__Tumour__tumour            -0.033   0.017 0.052    0.89
#> other immune__DC__myeloid           -0.500   0.260 0.056    0.89
#> Tumour__CD8_Cell__immune            -0.029   0.016 0.063    0.89
#> Keratin_Tumour__Endothelial__tissue  0.170   0.092 0.066    0.89
#> CD8_Cell__Macrophages__immune       -0.160   0.090 0.067    0.89
#>                                                                 cluster
#> CD8_Cell__Neutrophils__myeloid           CD8_Cell__Neutrophils__myeloid
#> CD8_Cell__Tumour__tumour                       CD8_Cell__Tumour__tumour
#> other immune__DC__myeloid                     other immune__DC__myeloid
#> Tumour__CD8_Cell__immune                       Tumour__CD8_Cell__immune
#> Keratin_Tumour__Endothelial__tissue Keratin_Tumour__Endothelial__tissue
#> CD8_Cell__Macrophages__immune             CD8_Cell__Macrophages__immune

As we can see from the results CD8_Cell__Neutrophils__myeloid is the one of the most significant pairwise relationship which contributes to patient survival. That is the relationship between CD8 T cells and Neutrophils, relative to the parent population of all myeloid cells. We can see that there is a positive coefficient associated with this relationship, which tells us an increase in localisation of CD8 T cells and Neutrophils relative to myeloid cells leads to poorer survival outcomes for patients.

The association between CD8_Cell__Neutrophils__myeloid and survival can also be visualised on a Kaplan-Meier curve. We must first extract the Kontextual values of this relationship across all images. Next we determine if CD8 T cells and Neutrophils are relatively attracted or avoiding in each image, by comparing the Kontextual value in each image to the median Kontextual value. Finally we plot the Kaplan-Meier curve using the ggsurvfit package.

As shown below, when CD8_Cell and Neutrophils are more localised to one another relative to the myeloid cell population, patients tend to have worse survival outcomes.

# Selecting most significant relationship
survRelationship <- kontextMat[["CD8_Cell__Neutrophils__myeloid"]]
survRelationship <- ifelse(survRelationship > median(survRelationship), "Localised", "Dispersed")

# Plotting Kaplan-Meier curve
survfit2(kerenSurv ~ survRelationship) |>
  ggsurvfit() +
  ggtitle("CD8_Cell__Neutrophils__myeloid")

6 SpatioMark: Identifying continuous changes in cell state

Changes in cell states can be analytically framed as the change in abundance of a gene or protein within a particular cell type. We can use marker expression to identify and quantify evidence of cell interactions that catalyse cell state changes. This approach measures how protein markers in a cell change with spatial proximity and abundance to other cell types. The methods utilised here will thereby provide a framework to explore how the dynamic behaviour of cells are altered by the agents they are surrounded by.

6.1 Continuous cell state changes within a single image

The first step in analysing these changes is to calculate the spatial proximity (getDistances) and abundance (getAbundances) of each cell to every cell type. These values will then be stored in the reducedDims slot of the SingleCellExperiment object under the names distances and abundances respectively.

kerenSCE <- getDistances(kerenSCE,
  maxDist = 200,
  nCores = 1
)

kerenSCE <- getAbundances(kerenSCE,
  r = 200,
  nCores = 1
)

First, let’s examine the same effect observed earlier with Kontextual - the localisation between p53-positive keratin/tumour cells and macrophages in the context of total keratin/tumour cells for image 6 of the Keren et al. dataset.

Statial provides two main functions to assess this relationship - calcStateChanges and plotStateChanges. We can use calcStateChanges to examine the relationship between 2 cell types for 1 marker in a specific image. In this case, we’re examining the relationship between keratin/tumour cells (from = Keratin_Tumour) and macrophages (to = "Macrophages") for the marker p53 (marker = "p53") in image = "6". We can appreciate that the fdr statistic for this relationship is significant, with a negative tvalue, indicating that the expression of p53 in keratin/tumour cells decreases as distance from macrophages increases.

stateChanges <- calcStateChanges(
  cells = kerenSCE,
  type = "distances",
  image = "6",
  from = "Keratin_Tumour",
  to = "Macrophages",
  marker = "p53",
  nCores = 1
)

stateChanges
#>   imageID primaryCellType otherCellType marker         coef      tval
#> 1       6  Keratin_Tumour   Macrophages    p53 -0.001402178 -7.010113
#>           pval          fdr
#> 1 2.868257e-12 2.868257e-12

Statial also provides a convenient function for visualising this interaction - plotStateChanges. Here, again we can specify image = 6 and our main cell types of interest, keratin/tumour cells and macrophages, and our marker p53, in the same format as calcStateChanges.

Through this analysis, we can observe that keratin/tumour cells closer to a group of macrophages tend to have higher expression of p53, as observed in the first graph. This relationship is quantified with the second graph, showing an overall decrease of p53 expression in keratin/tumour cells as distance to macrophages increase.

These results allow us to essentially arrive at the same result as Kontextual, which calculated a localisation between p53+ keratin/tumour cells and macrophages in the wider context of keratin/tumour cells.

p <- plotStateChanges(
  cells = kerenSCE,
  type = "distances",
  image = "6",
  from = "Keratin_Tumour",
  to = "Macrophages",
  marker = "p53",
  size = 1,
  shape = 19,
  interactive = FALSE,
  plotModelFit = FALSE,
  method = "lm"
)

p
#> $image

#> 
#> $scatter

6.2 Continuous cell state changes across all images

Beyond looking at single cell-to-cell interactions for a single image, we can also look at all interactions across all images. The calcStateChanges function provided by Statial can be expanded for this exact purpose - by not specifying cell types, a marker, or an image, calcStateChanges will examine the most significant correlations between distance and marker expression across the entire dataset. Here, we’ve filtered out the most significant interactions to only include those found within image 6 of the Keren et al. dataset.

stateChanges <- calcStateChanges(
  cells = kerenSCE,
  type = "distances",
  nCores = 1,
  minCells = 100
)

stateChanges |>
  filter(imageID == 6) |>
  head(n = 10)
#>    imageID primaryCellType otherCellType       marker         coef      tval
#> 1        6  Keratin_Tumour  Unidentified           Na  0.004218419  25.03039
#> 2        6  Keratin_Tumour   Macrophages  HLA_Class_1 -0.003823497 -24.69629
#> 3        6  Keratin_Tumour      CD4_Cell  HLA_Class_1 -0.003582774 -23.87797
#> 4        6  Keratin_Tumour  Unidentified Beta.catenin  0.005893120  23.41953
#> 5        6  Keratin_Tumour      CD8_Cell  HLA_Class_1 -0.003154544 -23.13804
#> 6        6  Keratin_Tumour       Dc/Mono  HLA_Class_1 -0.003353834 -22.98944
#> 7        6  Keratin_Tumour      CD3_Cell  HLA_Class_1 -0.003123446 -22.63197
#> 8        6  Keratin_Tumour        Tumour  HLA_Class_1  0.003684079  21.94265
#> 9        6  Keratin_Tumour      CD4_Cell           Fe -0.003457338 -21.43550
#> 10       6  Keratin_Tumour      CD4_Cell   phospho.S6 -0.002892457 -20.50767
#>             pval           fdr
#> 1  6.971648e-127 1.176442e-123
#> 2  7.814253e-124 1.236215e-120
#> 3  1.745242e-116 2.208779e-113
#> 4  1.917245e-112 2.257178e-109
#> 5  5.444541e-110 5.991836e-107
#> 6  1.053130e-108 1.110701e-105
#> 7  1.237988e-105 1.205229e-102
#> 8  8.188258e-100  7.025803e-97
#> 9   1.287478e-95  9.727951e-93
#> 10  3.928912e-88  2.583081e-85

In image 6, the majority of the top 10 most significant interactions occur between keratin/tumour cells and an immune population, and many of these interactions appear to involve the HLA class I ligand.

We can examine some of these interactions further with the plotStateChanges function. Taking a closer examination of the relationship between macrophages and keratin/tumour HLA class I expression, the plot below shows us a clear visual correlation - as macrophage density increases, keratin/tumour cells increase their expression HLA class I.

Biologically, HLA Class I is a ligand which exists on all nucleated cells, tasked with presenting internal cell antigens for recognition by the immune system, marking aberrant cells for destruction by either CD8+ T cells or NK cells.

p <- plotStateChanges(
  cells = kerenSCE,
  type = "distances",
  image = "6",
  from = "Keratin_Tumour",
  to = "Macrophages",
  marker = "HLA_Class_1",
  size = 1,
  shape = 19,
  interactive = FALSE,
  plotModelFit = FALSE,
  method = "lm"
)

p
#> $image

#> 
#> $scatter

Next, let’s take a look at the top 10 most significant results across all images.

stateChanges |> head(n = 10)
#>      imageID primaryCellType otherCellType     marker         coef      tval
#> 8674      35        CD4_Cell             B       CD20 -0.029185750 -40.57355
#> 8770      35        CD4_Cell       Dc/Mono       CD20  0.019125946  40.53436
#> 1819      35               B       Dc/Mono phospho.S6  0.005282065  40.41385
#> 8779      35        CD4_Cell       Dc/Mono phospho.S6  0.004033218  34.72882
#> 1813      35               B       Dc/Mono     HLA.DR  0.011120703  34.15344
#> 1971      35               B  other immune          P  0.011182182  34.14375
#> 8626      35        CD4_Cell      CD3_Cell       CD20  0.016349492  33.91901
#> 1816      35               B       Dc/Mono     H3K9ac  0.005096632  33.99856
#> 2011      35               B  other immune phospho.S6  0.005986586  33.66466
#> 1818      35               B       Dc/Mono   H3K27me3  0.006980810  33.22740
#>               pval           fdr
#> 8674 7.019343e-282 3.553472e-277
#> 8770 1.891267e-281 4.787176e-277
#> 1819 5.306590e-278 8.954694e-274
#> 8779 4.519947e-219 5.720445e-215
#> 1813 8.401034e-212 8.505879e-208
#> 1971 1.056403e-211 8.913225e-208
#> 8626 1.219488e-210 8.819335e-207
#> 1816 3.266533e-210 2.067062e-206
#> 2011 8.545691e-207 4.806856e-203
#> 1818 2.438769e-202 1.234603e-198

Immediately, we can appreciate that a couple of these interactions are not biologically plausible. One of the most significant interactions occurs between B cells and CD4 T cells in image 35, where CD4 T cells are found to increase in CD20 expression when in close proximity to B cells. Biologically, CD20 is a highly specific ligand for B cells, and under healthy circumstances are usually not expressed in T cells.

Could this potentially be an artefact of calcStateChanges? We can examine the image through the plotStateChanges function, where we indeed observe a strong increase in CD20 expression in T cells nearby B cell populations.

p <- plotStateChanges(
  cells = kerenSCE,
  type = "distances",
  image = "35",
  from = "CD4_Cell",
  to = "B",
  marker = "CD20",
  size = 1,
  shape = 19,
  interactive = FALSE,
  plotModelFit = FALSE,
  method = "lm"
)

p
#> $image

#> 
#> $scatter

So why are T cells expressing CD20? This brings us to a key problem of cell segmentation - contamination.

6.3 Contamination (Lateral marker spill over)

Contamination, or lateral marker spill over is an issue that results in a cell’s marker expressions being wrongly attributed to another adjacent cell. This issue arises from incorrect segmentation where components of one cell are wrongly determined as belonging to another cell. Alternatively, this issue can arise when antibodies used to tag and measure marker expressions don’t latch on properly to a cell of interest, thereby resulting in residual markers being wrongly assigned as belonging to a cell near the intended target cell. It is important that we either correct or account for this incorrect attribution of markers in our modelling process. This is critical in understanding whether significant cell-cell interactions detected are an artefact of technical measurement errors driven by spill over or are real biological changes that represent a shift in a cell’s state.

To circumvent this problem, Statial provides a function that predicts the probability that a cell is any particular cell type - calcContamination. calcContamination returns a dataframe of probabilities demarcating the chance of a cell being any particular cell type. This dataframe is stored under contaminations in the reducedDim slot of the SingleCellExperiment object. It also provides the rfMainCellProb column, which provides the probability that a cell is indeed the cell type it has been designated. E.g. For a cell designated as CD8, rfMainCellProb could give a 80% chance that the cell is indeed CD8, due to contamination.

We can then introduce these probabilities as covariates into our linear model by setting contamination = TRUE as a parameter in our calcStateChanges function. However, this is not a perfect solution for the issue of contamination. As we can see, despite factoring in contamination into our linear model, the correlation between B cell density and CD20 expression in CD4 T cells remains one of the most significant interactions in our model.

kerenSCE <- calcContamination(kerenSCE)

stateChangesCorrected <- calcStateChanges(
  cells = kerenSCE,
  type = "distances",
  nCores = 1,
  minCells = 100,
  contamination = TRUE
)

stateChangesCorrected |> head(n = 20)
#>       imageID primaryCellType otherCellType      marker         coef      tval
#> 8674       35        CD4_Cell             B        CD20 -0.025236910 -35.65423
#> 8770       35        CD4_Cell       Dc/Mono        CD20  0.016169515  34.32413
#> 8779       35        CD4_Cell       Dc/Mono  phospho.S6  0.003647452  30.08963
#> 8626       35        CD4_Cell      CD3_Cell        CD20  0.013722287  29.83108
#> 1819       35               B       Dc/Mono  phospho.S6  0.004225463  29.52566
#> 29188       3  Keratin_Tumour            DC          Ca -0.014052568 -29.71641
#> 8629       35        CD4_Cell      CD3_Cell      HLA.DR  0.010327936  29.15362
#> 1669       35               B      CD3_Cell      HLA.DR  0.009010617  25.71265
#> 8763       35        CD4_Cell       Dc/Mono      CSF.1R  0.008599071  25.19455
#> 1813       35               B       Dc/Mono      HLA.DR  0.008884431  25.17220
#> 8635       35        CD4_Cell      CD3_Cell  phospho.S6  0.002976185  25.11953
#> 31825       6  Keratin_Tumour  Unidentified          Na  0.004224706  24.74090
#> 27641      21  Keratin_Tumour            DC Pan.Keratin -0.005867472 -24.40923
#> 2011       35               B  other immune  phospho.S6  0.004559772  24.88574
#> 1971       35               B  other immune           P  0.007649353  24.03160
#> 1675       35               B      CD3_Cell  phospho.S6  0.003580064  23.99755
#> 2008       35               B  other immune      H3K9ac  0.004715040  23.83303
#> 29186       3  Keratin_Tumour            DC          Si -0.005811615 -23.90287
#> 31918       6  Keratin_Tumour   Macrophages HLA_Class_1 -0.003440256 -22.85040
#> 1816       35               B       Dc/Mono      H3K9ac  0.003719413  23.04478
#>                pval           fdr
#> 8674  1.428936e-228 7.233844e-224
#> 8770  1.201376e-214 3.040924e-210
#> 8779  7.955419e-172 1.342450e-167
#> 8626  2.687671e-169 3.401517e-165
#> 1819  1.594459e-165 1.614357e-161
#> 29188 8.811008e-164 7.434141e-160
#> 8629  1.006099e-162 7.276108e-159
#> 1669  3.975468e-130 2.515676e-126
#> 8763  5.321852e-126 2.993483e-122
#> 1813  2.453434e-125 1.133194e-121
#> 8635  2.462298e-125 1.133194e-121
#> 31825 3.421021e-124 1.443215e-120
#> 27641 5.186553e-124 2.019723e-120
#> 2011  8.015475e-123 2.898396e-119
#> 1971  1.969817e-115 6.648001e-112
#> 1675  3.851684e-115 1.218673e-111
#> 2008  9.755540e-114 2.905085e-110
#> 29186 1.296486e-112 3.646296e-109
#> 31918 1.803004e-107 4.803962e-104
#> 1816  4.240208e-107 1.073281e-103

However, this does not mean factoring in contamination into our linear model was ineffective.

Whilst our correction attempts do not rectify every relationship which arises due to contamination, we show that a significant portion of these relationships are rectified. We can show this by plotting a ROC curve of true positives against false positives. In general, cell type specific markers such as CD4, CD8, and CD20 should not change in cells they are not specific to. Therefore, relationships detected to be significant involving these cell type markers are likely false positives and will be treated as such for the purposes of evaluation. Meanwhile, cell state markers are predominantly likely to be true positives.

Plotting the relationship between false positives and true positives, we’d expect the contamination correction to be greatest in the relationships with the top 100 lowest p values, where we indeed see more true positives than false positives with contamination correction.

cellTypeMarkers <- c("CD3", "CD4", "CD8", "CD56", "CD11c", "CD68", "CD45", "CD20")

values <- c("blue", "red")
names(values) <- c("None", "Corrected")

df <- rbind(
  data.frame(TP = cumsum(stateChanges$marker %in% cellTypeMarkers), FP = cumsum(!stateChanges$marker %in% cellTypeMarkers), type = "None"),
  data.frame(TP = cumsum(stateChangesCorrected$marker %in% cellTypeMarkers), FP = cumsum(!stateChangesCorrected$marker %in% cellTypeMarkers), type = "Corrected")
)

ggplot(df, aes(x = TP, y = FP, colour = type)) +
  geom_line() +
  labs(y = "Cell state marker", x = "Cell type marker") +
  scale_colour_manual(values = values)

Here, we zoom in on the ROC curve where the top 100 lowest p values occur, where we indeed see more true positives than false positives with contamination correction.

ggplot(df, aes(x = TP, y = FP, colour = type)) +
  geom_line() +
  xlim(0, 100) +
  ylim(0, 1000) +
  labs(y = "Cell state marker", x = "Cell type marker") +
  scale_colour_manual(values = values)

6.4 Associate continuous state changes with survival outcomes

Similiar to Kontextual, we can run a similar survival analysis using our state changes results. Here, prepMatrix extracts the coefficients, or the coef column of stateChanges by default. To use the t values instead, specify column = "tval" in the prepMatrix function.

# Preparing features for Statial
stateMat <- prepMatrix(stateChanges)

# Ensuring rownames of stateMat match up with rownames of the survival vector
stateMat <- stateMat[names(kerenSurv), ]

# Remove some very small values
stateMat <- stateMat[, colMeans(abs(stateMat) > 0.0001) > .8]

survivalResults <- colTest(stateMat, kerenSurv, type = "survival")

head(survivalResults)
#>                                     coef se.coef  pval adjPval
#> Keratin_Tumour__CD8_Cell__Vimentin 48000    3800 0.000    0.00
#> Keratin_Tumour__Dc/Mono__SMA         700     380 0.065    0.95
#> Keratin_Tumour__other immune__Ki67  1600     880 0.065    0.95
#> Macrophages__CD4_Cell__H3K27me3     1100     600 0.070    0.95
#> Macrophages__CD8_Cell__Ca            960     540 0.077    0.95
#> Macrophages__Keratin_Tumour__P      -170      99 0.079    0.95
#>                                                               cluster
#> Keratin_Tumour__CD8_Cell__Vimentin Keratin_Tumour__CD8_Cell__Vimentin
#> Keratin_Tumour__Dc/Mono__SMA             Keratin_Tumour__Dc/Mono__SMA
#> Keratin_Tumour__other immune__Ki67 Keratin_Tumour__other immune__Ki67
#> Macrophages__CD4_Cell__H3K27me3       Macrophages__CD4_Cell__H3K27me3
#> Macrophages__CD8_Cell__Ca                   Macrophages__CD8_Cell__Ca
#> Macrophages__Keratin_Tumour__P         Macrophages__Keratin_Tumour__P

For our state changes results, Keratin_Tumour__CD4_Cell__Keratin6 is the most significant pairwise relationship which contributes to patient survival. That is, the relationship between HLA class I expression in keratin/tumour cells and their spatial proximity to mesenchymal cells. As there is a negative coeffcient associated with this relationship, which tells us that higher HLA class I expression in keratin/tumour cells nearby mesenchymal cell populations lead to poorer survival outcomes for patients.

# Selecting the most significant relationship
survRelationship <- stateMat[["Keratin_Tumour__CD4_Cell__Keratin6"]]
survRelationship <- ifelse(survRelationship > median(survRelationship), "Higher expression in close cells", "Lower expression in close cells")

# Plotting Kaplan-Meier curve
survfit2(kerenSurv ~ survRelationship) |>
  ggsurvfit() +
  add_pvalue() +
  ggtitle("Keratin_Tumour__CD4_Cell__Keratin6")

7 Region analysis using lisaClust

Next we can cluster areas with similar spatial interactions to identify regions using lisaClust. Here we set k = 5 to identify 5 regions.

set.seed(51773)

# Preparing features for lisaClust
kerenSCE <- lisaClust::lisaClust(kerenSCE, k = 5)

The regions identified by licaClust can be visualised using the hatchingPlot function.

# Use hatching to visualise regions and cell types.
lisaClust::hatchingPlot(kerenSCE,
  useImages = "5",
  line.spacing = 41, # spacing of lines
  nbp = 100 # smoothness of lines
)

8 Marker Means

Statial provides functionality to identify the average marker expression of a given cell type in a given region, using the getMarkerMeans function. Similar to the analysis above, these features can also be used for survival analysis.

cellTypeRegionMeans <- getMarkerMeans(kerenSCE,
  imageID = "imageID",
  cellType = "cellType",
  region = "region"
)

survivalResults <- colTest(cellTypeRegionMeans[names(kerenSurv), ], kerenSurv, type = "survival")

head(survivalResults)
#>                                 coef se.coef    pval adjPval
#> IDO__CD4_Cell__region_4         -140     9.2 0.0e+00 0.0e+00
#> CD20__DC__region_3             -6800   200.0 0.0e+00 0.0e+00
#> p53__CD4_Cell__region_5         -980   160.0 8.7e-10 1.1e-06
#> Lag3__Keratin_Tumour__region_4 -5500   950.0 8.7e-09 7.4e-06
#> CD45RO__Endothelial__region_4   -150    27.0 9.5e-09 7.4e-06
#> CD31__Mono/Neu__region_4        -570   110.0 1.6e-07 1.0e-04
#>                                                       cluster
#> IDO__CD4_Cell__region_4               IDO__CD4_Cell__region_4
#> CD20__DC__region_3                         CD20__DC__region_3
#> p53__CD4_Cell__region_5               p53__CD4_Cell__region_5
#> Lag3__Keratin_Tumour__region_4 Lag3__Keratin_Tumour__region_4
#> CD45RO__Endothelial__region_4   CD45RO__Endothelial__region_4
#> CD31__Mono/Neu__region_4             CD31__Mono/Neu__region_4

9 Patient classification

Finally we demonstrate how we can use ClassifyR to perform patient classification with the features generated from Statial. In addition to the kontextual, state changes, and marker means values, we also calculate cell type proportions and region proportions using the getProp function in spicyR. Here we perform 3 fold cross validation with 10 repeats, using a CoxPH model for survival classification, feature selection is also performed by selecting the top 10 features per fold using a CoxPH model.

# Calculate cell type and region proportions
cellTypeProp <- getProp(kerenSCE,
  feature = "cellType",
  imageID = "imageID"
)
regionProp <- getProp(kerenSCE,
  feature = "region",
  imageID = "imageID"
)

# Combine all the features into a list for classification
featureList <- list(
  states = stateMat,
  kontextual = kontextMat,
  regionMarkerMeans = cellTypeRegionMeans,
  cellTypeProp = cellTypeProp,
  regionProp = regionProp
)

# Ensure the rownames of the features match the order of the survival vector
featureList <- lapply(featureList, function(x) x[names(kerenSurv), ])


set.seed(51773)

kerenCV <- crossValidate(
  measurements = featureList,
  outcome = kerenSurv,
  classifier = "CoxPH",
  selectionMethod = "CoxPH",
  nFolds = 5,
  nFeatures = 10,
  nRepeats = 20,
  nCores = 1
)

Here, we use the performancePlot function to assess the C-index from each repeat of the 3-fold cross-validation. We can see the resulting C-indexes are very variable due to the dataset only containing 10 images.

# Calculate AUC for each cross-validation repeat and plot.
performancePlot(kerenCV,
  characteristicsList = list(x = "Assay Name")
) +
  theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1))

10 References

Appendix

Keren, L., Bosse, M., Marquez, D., Angoshtari, R., Jain, S., Varma, S., Yang, S. R., Kurian, A., Van Valen, D., West, R., Bendall, S. C., & Angelo, M. (2018). A Structured Tumor-Immune Microenvironment in Triple Negative Breast Cancer Revealed by Multiplexed Ion Beam Imaging. Cell, 174(6), 1373-1387.e1319. (DOI)

A sessionInfo

sessionInfo()
#> R version 4.4.1 (2024-06-14)
#> Platform: x86_64-apple-darwin20
#> Running under: macOS Monterey 12.7.6
#> 
#> Matrix products: default
#> BLAS:   /Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources/lib/libRblas.0.dylib 
#> LAPACK: /Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources/lib/libRlapack.dylib;  LAPACK version 3.12.0
#> 
#> locale:
#> [1] C/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
#> 
#> time zone: America/New_York
#> tzcode source: internal
#> 
#> attached base packages:
#> [1] stats4    stats     graphics  grDevices utils     datasets  methods  
#> [8] base     
#> 
#> other attached packages:
#>  [1] treekoR_1.13.0              tibble_3.2.1               
#>  [3] ggsurvfit_1.1.0             ggplot2_3.5.1              
#>  [5] SingleCellExperiment_1.27.2 dplyr_1.1.4                
#>  [7] lisaClust_1.13.3            ClassifyR_3.9.5            
#>  [9] survival_3.7-0              BiocParallel_1.39.0        
#> [11] MultiAssayExperiment_1.31.5 SummarizedExperiment_1.35.4
#> [13] Biobase_2.65.1              GenomicRanges_1.57.2       
#> [15] GenomeInfoDb_1.41.2         IRanges_2.39.2             
#> [17] MatrixGenerics_1.17.0       matrixStats_1.4.1          
#> [19] S4Vectors_0.43.2            BiocGenerics_0.51.3        
#> [21] generics_0.1.3              spicyR_1.17.4              
#> [23] Statial_1.7.1               BiocStyle_2.33.1           
#> 
#> loaded via a namespace (and not attached):
#>   [1] splines_4.4.1               ggplotify_0.1.2            
#>   [3] polyclip_1.10-7             XML_3.99-0.17              
#>   [5] lifecycle_1.0.4             rstatix_0.7.2              
#>   [7] edgeR_4.3.19                doParallel_1.0.17          
#>   [9] lattice_0.22-6              MASS_7.3-61                
#>  [11] backports_1.5.0             magrittr_2.0.3             
#>  [13] limma_3.61.12               plotly_4.10.4              
#>  [15] sass_0.4.9                  rmarkdown_2.28             
#>  [17] jquerylib_0.1.4             yaml_2.3.10                
#>  [19] spatstat.sparse_3.1-0       minqa_1.2.8                
#>  [21] RColorBrewer_1.1-3          ConsensusClusterPlus_1.69.0
#>  [23] multcomp_1.4-26             abind_1.4-8                
#>  [25] zlibbioc_1.51.1             Rtsne_0.17                 
#>  [27] purrr_1.0.2                 yulab.utils_0.1.7          
#>  [29] TH.data_1.1-2               sandwich_3.1-1             
#>  [31] tweenr_2.0.3                circlize_0.4.16            
#>  [33] GenomeInfoDbData_1.2.13     spatstat.utils_3.1-0       
#>  [35] tidytree_0.4.6              pheatmap_1.0.12            
#>  [37] goftest_1.2-3               spatstat.random_3.3-2      
#>  [39] codetools_0.2-20            DelayedArray_0.31.14       
#>  [41] ggforce_0.4.2               tidyselect_1.2.1           
#>  [43] shape_1.4.6.1               aplot_0.2.3                
#>  [45] UCSC.utils_1.1.0            farver_2.1.2               
#>  [47] lme4_1.1-35.5               spatstat.explore_3.3-2     
#>  [49] jsonlite_1.8.9              hopach_2.65.0              
#>  [51] GetoptLong_1.0.5            Formula_1.2-5              
#>  [53] iterators_1.0.14            systemfonts_1.1.0          
#>  [55] foreach_1.5.2               ggnewscale_0.5.0           
#>  [57] tools_4.4.1                 treeio_1.29.1              
#>  [59] Rcpp_1.0.13                 glue_1.8.0                 
#>  [61] SparseArray_1.5.45          xfun_0.48                  
#>  [63] ranger_0.16.0               mgcv_1.9-1                 
#>  [65] scam_1.2-17                 withr_3.0.1                
#>  [67] numDeriv_2016.8-1.1         BiocManager_1.30.25        
#>  [69] fastmap_1.2.0               boot_1.3-31                
#>  [71] fansi_1.0.6                 digest_0.6.37              
#>  [73] gridGraphics_0.5-1          R6_2.5.1                   
#>  [75] colorspace_2.1-1            tensor_1.5                 
#>  [77] spatstat.data_3.1-2         utf8_1.2.4                 
#>  [79] tidyr_1.3.1                 data.table_1.16.2          
#>  [81] class_7.3-22                httr_1.4.7                 
#>  [83] htmlwidgets_1.6.4           S4Arrays_1.5.11            
#>  [85] pkgconfig_2.0.3             gtable_0.3.5               
#>  [87] ComplexHeatmap_2.21.1       RProtoBufLib_2.17.0        
#>  [89] XVector_0.45.0              diffcyt_1.25.0             
#>  [91] htmltools_0.5.8.1           carData_3.0-5              
#>  [93] bookdown_0.41               fftwtools_0.9-11           
#>  [95] clue_0.3-65                 scales_1.3.0               
#>  [97] ggupset_0.4.0               png_0.1-8                  
#>  [99] SpatialExperiment_1.15.1    spatstat.univar_3.0-1      
#> [101] ggfun_0.1.6                 colorRamps_2.3.4           
#> [103] knitr_1.48                  reshape2_1.4.4             
#> [105] rjson_0.2.23                uuid_1.2-1                 
#> [107] curl_5.2.3                  nlme_3.1-166               
#> [109] nloptr_2.1.1                zoo_1.8-12                 
#> [111] cachem_1.1.0                GlobalOptions_0.1.2        
#> [113] stringr_1.5.1               parallel_4.4.1             
#> [115] concaveman_1.1.0            pillar_1.9.0               
#> [117] grid_4.4.1                  vctrs_0.6.5                
#> [119] ggpubr_0.6.0                car_3.1-3                  
#> [121] cytolib_2.17.0              cluster_2.1.6              
#> [123] evaluate_1.0.1              tinytex_0.53               
#> [125] magick_2.8.5                mvtnorm_1.3-1              
#> [127] locfit_1.5-9.10             cli_3.6.3                  
#> [129] compiler_4.4.1              rlang_1.1.4                
#> [131] crayon_1.5.3                ggsignif_0.6.4             
#> [133] labeling_0.4.3              FlowSOM_2.13.0             
#> [135] fs_1.6.4                    flowCore_2.17.0            
#> [137] plyr_1.8.9                  ggiraph_0.8.10             
#> [139] stringi_1.8.4               viridisLite_0.4.2          
#> [141] deldir_2.0-4                lmerTest_3.1-3             
#> [143] munsell_0.5.1               lazyeval_0.2.2             
#> [145] spatstat.geom_3.3-3         V8_6.0.0                   
#> [147] Matrix_1.7-1                patchwork_1.3.0            
#> [149] statmod_1.5.0               highr_0.11                 
#> [151] igraph_2.0.3                broom_1.0.7                
#> [153] bslib_0.8.0                 ggtree_3.13.1              
#> [155] ape_5.8