# Install the package from Bioconductor
if (!requireNamespace("BiocManager", quietly = TRUE)) {
install.packages("BiocManager")
}
BiocManager::install("Statial")
# 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
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
.
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):
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.
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"
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
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
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")
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.
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
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.
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)
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")
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
)
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
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))
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)
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