Distance measurement¶
The simplest quantitative statement about tissue organisation: how far is each cell type from each other cell type?
from pathlib import Path
import matplotlib.pyplot as plt
import pandas as pd
import scimappro as sp
# The demo data is not shipped with the package (it is 247 MB). Point DATA at
# wherever you unpacked it; this looks in the repository root first, then in the
# path the docs are built from.
DATA = next(
path for path in (Path("example_data"), Path("../../../example_data"))
if path.exists()
)
DATA
PosixPath('../../../example_data')
import anndata as ad
adata = ad.read_h5ad(DATA / "adata_scimap.h5ad")
adata = sp.pp.rescale(adata, gate=str(DATA / "manual_gates.csv"), verbose=False)
adata = sp.tl.phenotype(adata, phenotype=str(DATA / "phenotype_workflow.csv"), verbose=False)
adata.obs["phenotype"].value_counts()
phenotype ECAD+ 7112 Other myeloid cells 2419 Dendritic cells 863 SMA+ 509 Treg 216 Unknown 46 NK cells 35 Immune 1 Name: count, dtype: int64
Computing distances¶
[sp.tl.spatialDistance][scimappro.tl.spatialDistance] measures, for every
cell, the distance to the nearest cell of each phenotype. Distances are in the
units of the coordinate columns — pixels, for an mcmicro quantification — and
are never computed across images.
adata = sp.tl.spatialDistance(adata, phenotype="phenotype", verbose=False)
adata.uns["spatial_distance"].head()
| Dendritic cells | ECAD+ | Immune | NK cells | Other myeloid cells | SMA+ | Treg | Unknown | |
|---|---|---|---|---|---|---|---|---|
| obs | ||||||||
| quant_1 | 554.265017 | 508.856310 | 0.000000 | 2053.512851 | 532.311179 | 505.979757 | 575.266490 | 2086.992073 |
| quant_2 | 114.340629 | 10.602318 | 776.673240 | 1287.763826 | 25.455242 | 27.967658 | 225.297772 | 1311.182079 |
| quant_3 | 122.911429 | 10.602318 | 771.606273 | 1292.597225 | 24.200678 | 34.714545 | 226.977790 | 1315.778661 |
| quant_4 | 11.936186 | 13.731018 | 890.139838 | 1178.924606 | 39.819156 | 36.240050 | 168.802786 | 1204.626638 |
| quant_5 | 135.256707 | 15.790519 | 755.938553 | 1308.403525 | 40.006555 | 58.347658 | 215.734114 | 1331.056574 |
One row per cell, one column per phenotype. A cell's distance to its own phenotype is the distance to the nearest other cell of that type, not zero.
The summary view¶
Averaging within each phenotype gives the cell-type by cell-type matrix. The colormap is reversed, so warm means near.
sp.pl.spatialDistanceHeatmap(adata, phenotype="phenotype")
The distribution behind the means¶
Means hide bimodality. The box plots show the spread — a cell type that is sometimes touching and sometimes far away looks very different here from one that is uniformly at middle distance.
sp.pl.spatialDistanceDistribution(adata, phenotype="phenotype", distanceFrom="Treg")
Asking a narrower question¶
distanceFrom and distanceTo cut the matrix down to the comparison you care
about — here, how far the immune populations sit from tumour.
sp.pl.spatialDistanceHeatmap(
adata,
phenotype="phenotype",
distanceFrom=["Treg", "NK cells", "Dendritic cells"],
distanceTo=["ECAD+"],
)
Using the numbers directly¶
returnData=True hands back the table instead of drawing, which is what you
want for a statistical test across samples.
table = sp.pl.spatialDistanceHeatmap(adata, phenotype="phenotype",
returnData=True, show=False)
table.round(1)
| Dendritic cells | ECAD+ | Immune | NK cells | Other myeloid cells | SMA+ | Treg | Unknown | |
|---|---|---|---|---|---|---|---|---|
| phenotype | ||||||||
| Dendritic cells | 17.9 | 23.7 | 1594.1 | 805.2 | 24.8 | 74.4 | 55.4 | 1246.9 |
| ECAD+ | 146.8 | 15.2 | 1889.4 | 562.3 | 37.7 | 65.7 | 94.2 | 1214.2 |
| Immune | 554.3 | 508.9 | 0.0 | 2053.5 | 532.3 | 506.0 | 575.3 | 2087.0 |
| NK cells | 127.8 | 33.0 | 2336.4 | 51.0 | 85.0 | 93.9 | 152.8 | 311.4 |
| Other myeloid cells | 88.4 | 18.4 | 1766.7 | 642.5 | 17.6 | 58.6 | 63.8 | 1194.6 |
| SMA+ | 141.1 | 18.4 | 1969.2 | 544.9 | 30.1 | 36.1 | 90.0 | 1219.1 |
| Treg | 101.1 | 19.0 | 1980.2 | 534.8 | 20.2 | 54.9 | 62.8 | 1193.7 |
| Unknown | 127.9 | 30.6 | 2398.3 | 63.6 | 89.5 | 102.1 | 195.6 | 19.6 |
Log scale¶
When one pair of cell types is far apart it compresses everything else. log
puts the matrix on a log1p scale.
sp.pl.spatialDistanceHeatmap(adata, phenotype="phenotype", log=True)
Streaming¶
Only four obs columns are needed, so this is one of the cheapest functions to
run out of core against a file that will not fit in memory:
sp.tl.spatialDistance("big.h5ad", phenotype="phenotype", streamData=True)
Next¶
- Co-occurrence analysis — distance tells you how far apart; co-occurrence tells you whether that is more than chance.
- Proximity scores — how much of a sample is in an interaction zone.