Skip to content

Quickstart

This page runs one small dataset all the way through: raw single-cell quantification in, cell types and spatial statistics out. It uses the demo data — a single exemplar image with 11,201 cells and 9 markers.

If you would rather follow along cell by cell, the same material is available as executable notebooks under Tutorials.

1. Build a cell table

mcmicro-style quantification CSVs become an AnnData in one call:

import scimappro as sp

adata = sp.pp.mcmicro_to_scimap("quantification/quant.csv")

The result carries expression in .X, marker names in .var_names, and X_centroid, Y_centroid, Area, CellID, and imageid in .obs.

Already have an .h5ad? Every function accepts the path directly, so there is nothing to load first.

2. Rescale expression to a common scale

Gates convert raw intensities to a 0–1 scale where 0.5 is the positive/negative boundary, which is what phenotyping expects:

adata = sp.pp.rescale(adata, gate="manual_gates.csv")

With no gate, scimappro fits a two-component Gaussian mixture per marker per image and uses the crossing point. Pass a CSV (or a DataFrame) with markers and one column per image to override that with manual gates.

3. Assign cell types

Phenotyping is driven by a hierarchical workflow CSV: rows are cell types, columns are markers, cells hold pos, neg, allpos, allneg, anypos, or anyneg.

adata = sp.tl.phenotype(adata, phenotype="phenotype_workflow.csv")
adata.obs["phenotype"].value_counts()
ECAD+                  7112
Other myeloid cells    2419
Dendritic cells         863
SMA+                    509
Treg                    216
Unknown                  46
NK cells                 35

Prefer unsupervised labels? tl.cluster runs kmeans, leiden, or dbscan over the same matrix.

4. Look at what you got

sp.pl.heatmap(adata, groupBy="phenotype", standardScale="column")
sp.pl.spatialScatterPlot(adata, colorBy="phenotype", s=3)
sp.pl.pie(adata, phenotype="phenotype")

5. Measure spatial relationships

Average distance from every cell type to every other:

adata = sp.tl.spatialDistance(adata, phenotype="phenotype")
sp.pl.spatialDistanceHeatmap(adata, phenotype="phenotype")

Which cell types sit next to each other more often than chance, tested against a permutation null:

adata = sp.tl.spatialCooccurrence(adata, phenotype="phenotype", permutation=1000)
sp.pl.spatialCooccurrence(adata)
sp.pl.spatialInteractionNetwork(adata)

6. Find recurrent neighbourhoods

Count each cell's neighbours by type, then cluster those count vectors to get recurrent cellular neighbourhoods:

adata = sp.tl.neighCount(adata, phenotype="phenotype", method="radius", radius=30)
adata = sp.tl.cluster(adata, mode="spatial", layer="neighCount", method="kmeans", k=6,
                      label="neighbourhood")   # (1)!
sp.pl.spatialScatterPlot(adata, colorBy="neighbourhood", s=3)
  1. In mode="spatial", layer names a key in adata.uns, not adata.layers — it is where tl.neighCount and friends leave their count matrix.

tl.neighLDA and tl.neighNMF give the same idea as soft, overlapping motifs instead of hard assignments.

7. Save

adata.write("analysis.h5ad")
sp.pp.scimapToCsv(adata, outputDir=".", fileName="analysis.csv")

Or pass outputDir to any function and it writes rather than returns:

sp.tl.spatialDistance("analysis.h5ad", phenotype="phenotype", outputDir="results")

The whole thing

import scimappro as sp

adata = sp.pp.mcmicro_to_scimap("quantification/quant.csv")
adata = sp.pp.rescale(adata, gate="manual_gates.csv")
adata = sp.tl.phenotype(adata, phenotype="phenotype_workflow.csv")
adata = sp.tl.spatialDistance(adata, phenotype="phenotype")
adata = sp.tl.spatialCooccurrence(adata, phenotype="phenotype", permutation=1000)

sp.pl.heatmap(adata, groupBy="phenotype", standardScale="column")
sp.pl.spatialCooccurrence(adata)

adata.write("analysis.h5ad")

Next steps