Analyze Imaging Mass Cytometry data

This tutorial shows how to apply Squidpy to Imaging Mass Cytometry data.

The data used here comes from a recent paper from . We provide a pre-processed subset of the data, in anndata.AnnData format. For details on how it was pre-processed, please refer to the original paper.

See Analyze seqFISH data for additional analysis examples.

Import packages & data

To run the notebook locally, create a conda environment as conda env create -f environment.yml using this environment.yml.

import scanpy as sc
import squidpy as sq

print(f"squidpy=={sq.__version__}")



Out:

scanpy==1.9.1 anndata==0.8.0 umap==0.5.3 numpy==1.21.6 scipy==1.8.0 pandas==1.4.2 scikit-learn==1.1.0 statsmodels==0.13.2 python-igraph==0.9.10 pynndescent==0.5.7
squidpy==1.2.1

0%|          | 0.00/1.50M [00:00<?, ?B/s]
3%|3         | 48.0k/1.50M [00:00<00:04, 351kB/s]
9%|9         | 144k/1.50M [00:00<00:02, 551kB/s]
36%|###5      | 552k/1.50M [00:00<00:00, 1.64MB/s]
100%|##########| 1.50M/1.50M [00:00<00:00, 3.14MB/s]


First, let’s visualize the cluster annotation in spatial context with scanpy.pl.spatial().

sc.pl.spatial(adata, color="cell type", spot_size=10)


We can appreciate how the majority of the tissue seems to consist of apoptotic tumor cells. There also seem to be other cell types scattered across the tissue, annotated as T cells, Macrophages and different types of Stromal cells. We can also appreciate how a subset of tumor cell, basal CK tumor cells seems to be located in the lower part of the tissue.

Co-occurrence across spatial dimensions

We can visualize cluster co-occurrence in spatial dimensions using the original spatial coordinates. The co-occurrence score is defined as:

$\frac{p(exp|cond)}{p(exp)}$

where $$p(exp|cond)$$ is the conditional probability of observing a cluster $$exp$$ conditioned on the presence of a cluster $$cond$$, whereas $$p(exp)$$ is the probability of observing $$exp$$ in the radius size of interest. The score is computed across increasing radii size around each cell in the tissue.

We can compute this score with squidpy.gr.co_occurrence() and set the cluster annotation for the conditional probability with the argument clusters. Then, we visualize the results with squidpy.pl.co_occurrence(). We visualize the result for two conditional groups, namely basal CK tumor cell and T cells.

sq.gr.co_occurrence(adata, cluster_key="cell type")
sq.pl.co_occurrence(
cluster_key="cell type",
clusters=["basal CK tumor cell", "T cells"],
figsize=(15, 4),
)


Out:

  0%|          | 0/1 [00:00<?, ?/s]
100%|##########| 1/1 [00:02<00:00,  2.72s/]
100%|##########| 1/1 [00:02<00:00,  2.72s/]
/home/runner/work/squidpy_notebooks/squidpy_notebooks/.tox/docs/lib/python3.9/site-packages/seaborn/cm.py:1582: UserWarning: Trying to register the cmap 'rocket' which already exists.
mpl_cm.register_cmap(_name, _cmap)
/home/runner/work/squidpy_notebooks/squidpy_notebooks/.tox/docs/lib/python3.9/site-packages/seaborn/cm.py:1583: UserWarning: Trying to register the cmap 'rocket_r' which already exists.
mpl_cm.register_cmap(_name + "_r", _cmap_r)
/home/runner/work/squidpy_notebooks/squidpy_notebooks/.tox/docs/lib/python3.9/site-packages/seaborn/cm.py:1582: UserWarning: Trying to register the cmap 'mako' which already exists.
mpl_cm.register_cmap(_name, _cmap)
/home/runner/work/squidpy_notebooks/squidpy_notebooks/.tox/docs/lib/python3.9/site-packages/seaborn/cm.py:1583: UserWarning: Trying to register the cmap 'mako_r' which already exists.
mpl_cm.register_cmap(_name + "_r", _cmap_r)
/home/runner/work/squidpy_notebooks/squidpy_notebooks/.tox/docs/lib/python3.9/site-packages/seaborn/cm.py:1582: UserWarning: Trying to register the cmap 'icefire' which already exists.
mpl_cm.register_cmap(_name, _cmap)
/home/runner/work/squidpy_notebooks/squidpy_notebooks/.tox/docs/lib/python3.9/site-packages/seaborn/cm.py:1583: UserWarning: Trying to register the cmap 'icefire_r' which already exists.
mpl_cm.register_cmap(_name + "_r", _cmap_r)
/home/runner/work/squidpy_notebooks/squidpy_notebooks/.tox/docs/lib/python3.9/site-packages/seaborn/cm.py:1582: UserWarning: Trying to register the cmap 'vlag' which already exists.
mpl_cm.register_cmap(_name, _cmap)
/home/runner/work/squidpy_notebooks/squidpy_notebooks/.tox/docs/lib/python3.9/site-packages/seaborn/cm.py:1583: UserWarning: Trying to register the cmap 'vlag_r' which already exists.
mpl_cm.register_cmap(_name + "_r", _cmap_r)
/home/runner/work/squidpy_notebooks/squidpy_notebooks/.tox/docs/lib/python3.9/site-packages/seaborn/cm.py:1582: UserWarning: Trying to register the cmap 'flare' which already exists.
mpl_cm.register_cmap(_name, _cmap)
/home/runner/work/squidpy_notebooks/squidpy_notebooks/.tox/docs/lib/python3.9/site-packages/seaborn/cm.py:1583: UserWarning: Trying to register the cmap 'flare_r' which already exists.
mpl_cm.register_cmap(_name + "_r", _cmap_r)
/home/runner/work/squidpy_notebooks/squidpy_notebooks/.tox/docs/lib/python3.9/site-packages/seaborn/cm.py:1582: UserWarning: Trying to register the cmap 'crest' which already exists.
mpl_cm.register_cmap(_name, _cmap)
/home/runner/work/squidpy_notebooks/squidpy_notebooks/.tox/docs/lib/python3.9/site-packages/seaborn/cm.py:1583: UserWarning: Trying to register the cmap 'crest_r' which already exists.
mpl_cm.register_cmap(_name + "_r", _cmap_r)


We can observe that T cells seems to co-occur with endothelial and vimentin hi stromal cells, whereas basal CK tumor cell seem to largely cluster together, except for the presence of a type of stromal cells (small elongated stromal cell) at close distance.

Neighborhood enrichment

A similar analysis that can inform on the neighbor structure of the tissue is the neighborhood enrichment test. You can compute such score with the following function: squidpy.gr.nhood_enrichment(). In short, it’s an enrichment score on spatial proximity of clusters: if spots belonging to two different clusters are often close to each other, then they will have a high score and can be defined as being enriched. On the other hand, if they are far apart, the score will be low and they can be defined as depleted. This score is based on a permutation-based test, and you can set the number of permutations with the n_perms argument (default is 1000).

Since the function works on a connectivity matrix, we need to compute that as well. This can be done with squidpy.gr.spatial_neighbors(). Please see Building spatial neighbors graph for more details of how this function works.

Finally, we visualize the results with squidpy.pl.nhood_enrichment().

sq.gr.spatial_neighbors(adata)


Out:

  0%|          | 0/1000 [00:00<?, ?/s]
10%|9         | 96/1000 [00:00<00:00, 958.02/s]
27%|##6       | 268/1000 [00:00<00:00, 1402.70/s]
42%|####2     | 425/1000 [00:00<00:00, 1478.93/s]
59%|#####8    | 590/1000 [00:00<00:00, 1546.04/s]
74%|#######4  | 745/1000 [00:00<00:00, 1490.99/s]
90%|########9 | 895/1000 [00:00<00:00, 1465.66/s]
100%|##########| 1000/1000 [00:00<00:00, 1596.66/s]


Interestingly, T cells shows an enrichment with stromal and endothelial cells, as well as macrophages. Another interesting result is that apoptotic tumor cells, being uniformly spread across the tissue area, show a neighbor depletion against any other cluster (but a strong enrichment for itself). This is a correct interpretation from a permutation based approach, because the cluster annotation, being uniformly spread across the tissue, and in high number, it’s more likely to be enriched with cell types from the same class, rather than different one.

Interaction matrix and network centralities

Squidpy provides other descriptive statistics of the spatial graph. For instance, the interaction matrix, which counts the number of edges that each cluster share with all the others. This score can be computed with the function squidpy.gr.interaction_matrix(). We can visualize the results with squidpy.pl.interaction_matrix().

sq.gr.interaction_matrix(adata, cluster_key="cell type")


Finally, similar to the previous analysis, we can investigate properties of the spatial graph by computing different network centralities:

• degree_centrality.

• average_clustering.

• closeness_centrality.

Squidpy provides a convenient function for all of them: squidpy.gr.centrality_scores() and squidpy.pl.centrality_scores() for visualization.

sq.gr.centrality_scores(

You can familiarize yourself with network centralities from the excellent networkx documentation . For the purpose of this analysis, we can appreciate that the apoptotic tumor cell clusters shows high closeness centrality, indicating that nodes belonging to that group are often close to each other in the spatial graph.