Skip to content

neighCount

neighCount

neighCount(
    data,
    xCoordinate="X_centroid",
    yCoordinate="Y_centroid",
    zCoordinate=None,
    phenotype="phenotype",
    method="radius",
    radius=30,
    knn=10,
    imageId="imageid",
    subset=None,
    verbose=True,
    label="neighCount",
    maxWorkers=None,
    outputDir=None,
    streamData=False,
    inferenceMode="phenotype_driven",
    layer=None,
    subsetMarker=None,
    sdataTable=None,
)

Count each cell's neighbours by type.

For every cell, find its neighbours and count how many fall into each category, normalised to proportions. The result — a cell x category matrix — goes to uns[label], and is what you cluster to get recurrent cellular neighbourhoods:

adata = sp.tl.neighCount(adata, phenotype="phenotype")
adata = sp.tl.cluster(adata, mode="spatial", layer="neighCount", method="kmeans", k=6)

Neighbourhoods are found per image with a BallTree and accumulated with np.add.at over integer category codes, so there is no Python loop over cells.

Two modes: 'phenotype_driven' counts neighbours by their phenotype label; 'marker_driven' counts them by the markers they express, which lets you build neighbourhoods before you have called any cell types.

Parameters:

Name Type Description Default
data (AnnData | SpatialData | str, required)

The cell table. An AnnData, a path to an .h5ad file, a SpatialData object, or a path to a .zarr SpatialData store.

required
xCoordinate str

Column in obs holding x positions.

'X_centroid'
yCoordinate str

Column in obs holding y positions.

'Y_centroid'
zCoordinate str

Column in obs holding z positions. Leave as None for 2D data; set it and the neighbourhood search becomes three-dimensional.

None
phenotype str

Column in obs holding the cell type labels. Used in 'phenotype_driven' mode.

'phenotype'
method str

How neighbourhoods are defined. 'radius' takes every cell within radius pixels; 'knn' takes the knn nearest cells regardless of distance.

'radius'
radius float

Neighbourhood radius in the units of the coordinate columns, normally pixels. Used when method='radius'.

30
knn int

Number of nearest neighbours. Used when method='knn'.

10
imageId str

Column in obs holding image identifiers. Neighbourhoods never cross images.

'imageid'
subset str

Process only this image.

None
label str

Key in uns the count matrix is written to.

'neighCount'
maxWorkers int

Cap on parallel workers. Defaults to max(1, cpu_count() - 1). Lower it to cut peak memory.

None
inferenceMode str

'phenotype_driven' counts neighbours by their phenotype label. 'marker_driven' counts them by which markers they express, reading the matrix selected by layer and binarising it if it is not already binary — use it when you have no phenotype calls yet.

'phenotype_driven'
layer str

Which matrix to read for inferenceMode='marker_driven'. None uses .X.

None
subsetMarker list

Restrict 'marker_driven' counting to these markers.

None
verbose bool

Print progress messages.

True
outputDir str

Directory to write the updated object to. With None the object is returned instead.

None
streamData bool

Run out of core against an .h5ad path with CAP-AnnData, reading only the sections this function needs and writing back only what it changes. Requires data to be a path; the file is updated in place and None is returned.

False
sdataTable str

Which SpatialData table to work on. Ignored for AnnData input, and optional when the store has exactly one table.

None

Returns:

Name Type Description
adata AnnData | SpatialData | None

The updated object with the cell x category count matrix in uns[label]. None in streaming mode.

Raises:

Type Description
ValueError

When inferenceMode is neither 'phenotype_driven' nor 'marker_driven'.

Example
# Neighbourhood composition, then recurrent 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",
)
sp.pl.spatialScatterPlot(adata, colorBy="neighbourhood", s=3)

# Before phenotyping: build neighbourhoods from marker expression.
adata = sp.tl.neighCount(
    adata, inferenceMode="marker_driven", layer="rescaled", method="knn", knn=10
)