Skip to content

Neighborhood Module API Reference

Module for performing neighborhood analysis.

NeighborhoodCollection

Bases: CelldegaCollection

Neighborhood-level or spatial-region MuData collection.

Observations are neighborhoods or spatial regions. Feature modalities live in mod and global observation relations live in relations/mdata.obsp. Geometry is kept as a live GeoDataFrame in memory; durable geometry storage can be layered on later with WKB columns or GeoParquet sidecars.

geometry property

Neighborhood geometry. Alias of :attr:gdf (single source of truth).

__init__(obs=None, mod=None, mdata=None, gdf=None, nbhd_type=None, data_dir=None, source=None, name=None, meta=None, nbhd_col='name', geometry=None, relations=None, provenance=None, uns=None, memberships=None, transformation_matrix=None)

Build a neighborhood / spatial-region collection.

The observation axis (one row per neighborhood) is established from a neighborhood GeoDataFrame (gdf — the usual path, produced by alpha_shape / generate_hextile / etc.), from an explicit obs table paired with geometry, or from a pre-built mdata. When built from gdf, per-neighborhood area/area_um2 and centroid columns are derived and the neighborhood-id column is normalized.

Parameters:

Name Type Description Default
obs DataFrame | None

Pre-built neighborhood observation table (use with geometry, not with gdf).

None
mod dict[str, AnnData] | None

Feature-space modalities to attach up front.

None
mdata MuData | None

Pre-built MuData to wrap (e.g. from read).

None
gdf GeoDataFrame | None

Neighborhood geometry; each row becomes an observation. Mutually exclusive with obs/geometry.

None
nbhd_type str | None

Label for how the neighborhoods were made (e.g. "hextile", "alpha_shape"); defaults to "neighborhood".

None
data_dir str | None

DegaFiles/instrument directory used as the default source for the transcript- and transform-loading methods.

None
source str | dict[str, Any] | None

Source descriptor recorded in provenance.

None
name str | None

Optional collection name.

None
meta dict[str, Any] | None

Extra metadata merged into uns["celldega"].

None
nbhd_col str

Column in gdf identifying each neighborhood (falls back to neighborhood_id / nbhd_id).

'name'
geometry GeoDataFrame | None

Neighborhood geometry paired with an explicit obs (alternative to gdf).

None
relations dict[str, spmatrix] | None

Square neighborhood-by-neighborhood matrices for mdata.obsp.

None
provenance dict[str, Any] | None

Free-form provenance metadata.

None
uns dict[str, Any] | None

Extra Celldega metadata.

None
memberships dict[str, spmatrix] | None

Membership matrices (e.g. cell-to-neighborhood); kept in memory only (not persisted by write).

None
transformation_matrix Any | None

Optional micron-to-pixel affine (see :meth:set_transformation_matrix).

None

Raises:

Type Description
ValueError

If gdf is combined with obs or geometry.

calc_bordering(metric='border_ratio', key='bordering', category='leiden')

Calculate a neighborhood-by-neighborhood bordering relation.

Computes pairwise border relationships between neighborhoods and stores the square matrix in relations[key] (mdata.obsp).

Parameters:

Name Type Description Default
metric str

Border metric (e.g. "border_ratio", "binary").

'border_ratio'
key str

Name for the relation in relations.

'bordering'
category str

Neighborhood category recorded on the computed result.

'leiden'

Returns:

Type Description
spmatrix

The stored sparse relation matrix.

Raises:

Type Description
ValueError

If geometry is not set.

calc_gradient(obs_name, direction='both', bin_width=10, max_dist=50, nbhd_type='gradient', *, technology=None, scale_um_per_pixel=None, is_pixel_space=False, clip_boundary=None, clip_reference=None, clip_alpha=100, **kwargs)

Calculate a gradient collection around one neighborhood in this collection.

Picks the neighborhood identified by obs_name and grows fixed-width bands outward from and/or inward into it (see the gradient engine), returning a new gradient NeighborhoodCollection — one neighborhood (observation) per ring, ordered inner-most to outer-most. From there the usual calc_nbhd_by_* methods summarize cell composition or expression per ring, profiling how the tissue changes with distance from that neighborhood's boundary. A gradient is therefore always anchored to a concrete neighborhood (e.g. a per-cluster alpha shape) rather than a loose geometry.

Parameters:

Name Type Description Default
obs_name str

Identifier of the source neighborhood to anchor the gradient on — matched against the collection's observation index (and, as a fallback, the name column). Its geometry is the ROI.

required
direction str

"outward", "inward", or "both" (default).

'both'
bin_width float

Width of each ring in microns (default 10).

10
max_dist float

Maximum distance from the neighborhood boundary in microns (default 50).

50
nbhd_type str

Label recorded on the new collection (default "gradient").

'gradient'
technology str | None

Imaging platform used to look up scale_um_per_pixel for pixel-space geometry (e.g. "Xenium").

None
scale_um_per_pixel float | None

Microns per pixel; required (directly or via technology) when is_pixel_space=True.

None
is_pixel_space bool

True if this collection's geometry is in pixel units; False (default) if already in microns.

False
clip_boundary Any | None

Optional precomputed tissue boundary to clip outward rings to (takes precedence over clip_reference).

None
clip_reference Any | None

Optional source of cell positions (an AnnData, a GeoDataFrame/GeoSeries of cells, or an (N, 2) array) from which a tissue alpha shape is computed on the fly to clip outward rings.

None
clip_alpha float

Inverse-alpha for the on-the-fly alpha shape (default 100).

100
**kwargs Any

Forwarded to the new :class:NeighborhoodCollection (e.g. name, data_dir).

{}

Returns:

Type Description
NeighborhoodCollection

A new NeighborhoodCollection whose observations are the gradient

NeighborhoodCollection

rings around obs_name.

Raises:

Type Description
ValueError

If this collection has no geometry.

KeyError

If obs_name is not found.

Examples:

>>> # nbhd holds one alpha-shape neighborhood per cell-type cluster
>>> grad_nbhd = nbhd.calc_gradient(obs_name="9", direction="both",
...                                bin_width=50, max_dist=200)
>>> grad_nbhd.calc_signature(adata, by="cell")
>>> grad_nbhd.obs[["direction", "dist_start_um"]].head(3)

calc_overlap(metric='iou', key='overlap', category='leiden')

Calculate a neighborhood-by-neighborhood overlap relation.

Computes pairwise geometric overlap between neighborhoods and stores the square matrix in relations[key] (mdata.obsp).

Parameters:

Name Type Description Default
metric str

Overlap metric — "iou" (intersection over union), "ioa" (intersection over the row neighborhood's area), or "intersection" (raw intersection area).

'iou'
key str

Name for the relation in relations.

'overlap'
category str

Neighborhood category recorded on the computed result.

'leiden'

Returns:

Type Description
spmatrix

The stored sparse relation matrix.

Raises:

Type Description
ValueError

If geometry is not set.

calc_population(adata, category='leiden', modality_name='population', output='proportion', min_cells=5, drop_missing=True)

Calculate a neighborhood-by-population modality and attach it to self.mod.

Spatially assigns cells to neighborhoods and, per neighborhood, counts cells per category value to form a neighborhood (rows) by population (columns) feature matrix.

Parameters:

Name Type Description Default
adata AnnData

Cell-level AnnData with spatial coordinates in obsm["spatial"] and category in obs.

required
category str

obs column naming the population/cell-type/cluster.

'leiden'
modality_name str

Key for the modality in self.mod.

'population'
output str

"proportion" (within-neighborhood fractions) or "counts".

'proportion'
min_cells int

Minimum cells for a neighborhood to be included.

5
drop_missing bool

When True (default), neighborhoods with fewer than min_cells cells are removed from the collection entirely so the observation axis only contains neighborhoods with data. When False, the collection keeps all neighborhoods and the modality is attached with zero-filled rows for those that fall below min_cells.

True

Returns:

Type Description
None

None — the modality is attached to self.mod[modality_name].

calc_signature(adata=None, by='cell', modality_name=None, min_cells=1, data_dir=None, drop_missing=True, include_variance=False)

Calculate a neighborhood-by-gene modality and attach it to self.mod.

Builds per-neighborhood gene expression — mean expression of contained cells (by="cell") or transcript counts (by="cell-free").

Parameters:

Name Type Description Default
adata AnnData | None

Cell-level AnnData (required when by="cell"); needs spatial coordinates in obsm["spatial"].

None
by str

"cell" for cell-derived mean expression or "cell-free" for transcript counts.

'cell'
modality_name str | None

Key for the modality; defaults to "gene" (cell-derived) or "gene_cell_free" (transcript-derived).

None
min_cells int

Minimum cells/transcripts for a neighborhood to be kept.

1
data_dir str | None

Transcript directory for by="cell-free"; defaults to self.data_dir.

None
drop_missing bool

When True (default), neighborhoods with fewer than min_cells cells (or transcripts) are removed from the collection entirely. When False, the collection keeps all neighborhoods and the modality is attached with zero-filled rows for those that fall below min_cells.

True
include_variance bool

When by="cell", also compute per-neighborhood, per-gene expression variance and store it in the modality's layers["variance"] (same shape as X). Not supported for by="cell-free".

False

Returns:

Type Description
None

None — the modality is attached to self.mod.

Raises:

Type Description
ValueError

If adata is missing for by="cell", or data_dir is missing for by="cell-free", or include_variance=True is combined with by="cell-free".

calc_transcript_assignment(data_dir=None)

Add per-neighborhood transcript-assignment columns to obs.

From transcripts.parquet in data_dir, adds three obs columns (on the underlying MuData) for each neighborhood:

  • total_transcripts — transcripts falling inside the neighborhood.
  • unassigned_transcripts — those with cell_id == "UNASSIGNED".
  • transcript_assignment_proportion — assigned / total (0.0 when the neighborhood has no transcripts).

Assumption: the transcript-to-cell assignment is not computed here — it must already be present in the instrument data, with unassigned transcripts marked by the "UNASSIGNED" sentinel (Xenium convention). Only transcripts are needed — no adata or cell polygons.

Parameters:

Name Type Description Default
data_dir str | None

Directory containing transcripts.parquet; defaults to self.data_dir.

None

Returns:

Type Description
None

None — the three columns are added to self.obs.

Raises:

Type Description
ValueError

If geometry or a usable data_dir is missing, or the transcripts lack a cell_id column. A complete absence of the "UNASSIGNED" sentinel only warns.

from_gdf(gdf, nbhd_type='neighborhood', **kwargs) classmethod

Create a NeighborhoodCollection from a neighborhood GeoDataFrame.

Convenience wrapper for NeighborhoodCollection(gdf=gdf, nbhd_type=nbhd_type, **kwargs).

Parameters:

Name Type Description Default
gdf GeoDataFrame

Neighborhood geometry; each row becomes an observation.

required
nbhd_type str

Label for how the neighborhoods were made.

'neighborhood'
**kwargs Any

Forwarded to the constructor.

{}

Returns:

Type Description
NeighborhoodCollection

A new NeighborhoodCollection.

load_transformation_matrix(data_dir=None)

Load the micron-to-pixel transformation matrix from DegaFiles.

Reads micron_to_image_transform.csv and stores it via :meth:set_transformation_matrix. Later this matrix can instead be supplied directly (e.g. from SpatialData).

Parameters:

Name Type Description Default
data_dir str | None

Directory containing the transform CSV; defaults to self.data_dir.

None

Returns:

Type Description
ndarray

The loaded matrix as a float ndarray.

Raises:

Type Description
ValueError

If no data_dir is available.

set_transformation_matrix(matrix)

Set the micron-to-pixel affine transformation matrix.

Parameters:

Name Type Description Default
matrix Any

Affine mapping micron coordinates (the geometry's native space) to image/pixel space, as a (2, 3) or (3, 3) array.

required

Returns:

Type Description
ndarray

The stored matrix as a float ndarray. It is also mirrored into

ndarray

uns so it round-trips through write/read.

to_pixel_gdf()

Return the neighborhood geometry ready for pixel-space visualization.

Adds a geometry_pixel column (micron geometry transformed to image space via the stored transformation matrix) and leaves the original micron geometry intact. The result can be passed straight to Landscape(nbhd=...), which renders geometry_pixel directly when present rather than applying its own transform.

Returns:

Type Description
GeoDataFrame

A copy of gdf with an added geometry_pixel column.

Raises:

Type Description
ValueError

If geometry or the transformation matrix is not set.

alpha_shape_cell_clusters(adata, cat='cluster', alphas=(100, 150, 200, 250, 300, 350), meta_cluster=None)

Compute alpha shapes for each cluster in the cell metadata.

Parameters

adata : AnnData AnnData object with cell metadata in obs and spatial coordinates in obsm["spatial"]. cat : str Column name in adata.obs containing cluster/category labels. alphas : Sequence[float] List of inverse alpha values to compute shapes for. meta_cluster : pd.DataFrame | None Optional DataFrame with cluster metadata including 'color' column. If not provided, colors will be extracted from adata.uns[f'{cat}_colors'] if available, otherwise defaults to black.

Returns

gpd.GeoDataFrame GeoDataFrame with alpha shapes for each cluster at each alpha value.

alpha_shape_cell_clusters_by_slice(adata, cluster_attr='cluster', slice_attr='slice_id', z_attr=None, alphas=(150,), meta_cluster=None, z_jitter=0.1, progress_every=0)

Compute one alpha shape per (slice, cluster) pair, each stamped with its slice's Z.

Mirrors the approach validated in notebooks/Serial_Slice_Alpha_Shapes_3D_Demo.ipynb: for each slice, builds a throwaway per-slice AnnData and reuses alpha_shape_cell_clusters / filter_alpha_shapes at a single alpha resolution, then stamps every resulting polygon with that slice's Z (from z_attr, or 0.0 if not given) plus a small per-cluster jitter (via _stamp_z) so overlapping, coplanar cluster polygons within a slice don't z-fight.

Parameters

adata : AnnData Cell-level AnnData with spatial coordinates in obsm["spatial"] and cluster_attr / slice_attr (and, if given, z_attr) columns in obs. cluster_attr : str Column in adata.obs with cluster/category labels. slice_attr : str Column in adata.obs identifying each slice. z_attr : str | None Column in adata.obs with each cell's Z coordinate (expected to be one constant value per slice, e.g. from celldega.align.serial_slices). If None, every slice is stamped at Z=0 (e.g. for a future 2D "neighborhood-scape" use where "slice" means "dataset"). alphas : Sequence[float] Must contain exactly one inverse-alpha resolution (see alpha_shape) — this function computes a single resolution per (slice, cluster), unlike alpha_shape_cell_clusters which can sweep several. meta_cluster : pd.DataFrame | None Optional cluster color/metadata lookup, forwarded to alpha_shape_cell_clusters. z_jitter : float Per-cluster Z offset within a slice, to avoid z-fighting between coplanar cluster polygons that would otherwise sit at the exact same Z. progress_every : int Print a progress line every this many slices processed (0, the default, disables it). Each real alpha shape involves a Delaunay triangulation plus verification, so this is the slow part of a neighborhood-cloud pre-processing run on a large aligned dataset with many slices -- worth visibility into, unlike a quick synthetic test.

Returns

gpd.GeoDataFrame One row per (slice, cluster) neighborhood, with columns name (f"{slice_id}__{cluster_id}"), cluster_id, slice_id, geometry (3D), color, area, inv_alpha, cell_count.

alpha_shape_gene_expression_by_slice(adata, gene_list, slice_attr='slice_id', z_attr=None, alphas=(150,), min_expression=2.0, min_cells=4, z_jitter=0.1, shape_max_cells=50000, random_state=0)

Compute one alpha shape per (slice, gene) pair, built from expressing cells.

Generalizes alpha_shape_cell_clusters_by_slice — instead of grouping cells by a cluster label, each shape is built from whichever cells have adata[:, gene].X >= min_expression within that slice. A thin eager wrapper around iter_gene_alpha_shapes_by_slice that collects every gene's shapes into one GeoDataFrame before returning — convenient for interactive/analysis use on a bounded gene list (a curated marker panel), but holds every gene's shapes for every slice in memory at once. For a whole-transcriptome gene list (~40k genes), use iter_gene_alpha_shapes_by_slice directly, or celldega.pre.nbhd_cloud.write_gene_shapes_streaming to write results to DegaFiles as they're produced instead of materializing all of them.

Parameters

adata : AnnData Cell-level AnnData with spatial coordinates in obsm["spatial"], slice_attr (and, if given, z_attr) columns in obs, and gene_list present in adata.var_names. gene_list : Sequence[str] Genes to compute shapes for. Must all be present in adata.var_names. slice_attr : str Column in adata.obs identifying each slice. z_attr : str | None Column in adata.obs with each cell's Z coordinate. If None, every slice is stamped at Z=0. alphas : Sequence[float] Must contain exactly one inverse-alpha resolution — one resolution per (slice, gene), same restriction as alpha_shape_cell_clusters_by_slice. min_expression : float A cell counts as "expressing" a gene when its value is at least this (default: 2 counts). min_cells : int Minimum number of expressing cells required to compute a shape for a (slice, gene) pair; pairs with fewer are silently skipped. z_jitter : float Small per-gene Z offset, to avoid z-fighting between coplanar gene shapes that would otherwise sit at the exact same Z (see _gene_z_offset). shape_max_cells : int | None Cap (via uniform random subsample) on the expressing cells that feed the alpha shape's own geometry computation, within one (slice, gene) pair -- see iter_gene_alpha_shapes. None disables it. Even a curated marker panel of 50-100 genes can include broadly-expressed genes with far more expressing cells than needed to describe their spatial footprint, so this caps the actual cost driver regardless of how many genes are in gene_list. random_state : int Seed for the subsampling RNG.

Returns

gpd.GeoDataFrame One row per (slice, gene) shape, with columns name (f"{slice_id}__{gene}"), gene, slice_id, geometry (3D), area, inv_alpha, cell_count, mean_expression, and max_expression (whole-tissue single-cell max for that gene, repeated across every row of that gene — see write_gene_shapes).

filter_alpha_shapes(gdf_alpha, alpha, min_area=0, clean_names=True)

Filter alpha shapes by a specific alpha value and optionally clean up names.

Alpha shapes computed by alpha_shape_cell_clusters have names in the format {category}_{alpha} (e.g., "cluster_0_150"). This function filters to a specific alpha value and removes the trailing _{alpha} suffix from names.

Parameters

gdf_alpha : gpd.GeoDataFrame GeoDataFrame of alpha shapes with 'inv_alpha', 'area', 'name', and 'cat' columns. Typically the output of alpha_shape_cell_clusters. alpha : float The inverse alpha value to filter for (must match values in 'inv_alpha' column). min_area : float, default 0 Minimum area threshold. Shapes with area <= min_area are excluded. clean_names : bool, default True If True, removes the trailing _{alpha} suffix from the 'name' column, leaving just the category name (e.g., "cluster_0" instead of "cluster_0_150").

Returns

gpd.GeoDataFrame Filtered GeoDataFrame with optionally cleaned names.

Examples

gdf_alpha = dega.nbhd.alpha_shape_cell_clusters(adata, cat="leiden") gdf_filtered = dega.nbhd.filter_alpha_shapes(gdf_alpha, alpha=150)

Names are now just category names without the alpha suffix

print(gdf_filtered["name"].tolist()[:3]) ['0', '1', '2']

generate_hextile(adata, diameter=100)

Generate a hexagonal grid over the bounding box of cell spatial coordinates.

Parameters

adata : AnnData AnnData object with spatial coordinates in obsm["spatial"]. diameter : float, default 100 Diameter of each hexagon in the same units as the spatial coordinates (typically microns).

Returns

gpd.GeoDataFrame GeoDataFrame with hexagon geometries covering the spatial extent. Columns: "name" (hex_0, hex_1, ...), "geometry" (Polygon).

Examples

gdf_hex = dega.nbhd.generate_hextile(adata, diameter=100) gdf_hex.shape (1234, 2)

hextile_niche(gdf_hex, adata_hex, category='leiden', dissolve=True)

Create niche polygons from hextiles based on clustering results.

Takes hexagon geometries and assigns them to niches based on clustering (e.g., Leiden clustering of hexagon population distributions). Optionally dissolves adjacent hexagons of the same niche into unified polygons.

Parameters

gdf_hex : gpd.GeoDataFrame GeoDataFrame of hexagon geometries. Must be indexed by hexagon name (matching adata_hex.obs.index). adata_hex : AnnData AnnData object containing clustering results in obs[category]. The index must match the hexagon names in gdf_hex. Colors can be provided in uns[f"{category}_colors"]. category : str, default "leiden" Column name in adata_hex.obs containing the niche/cluster assignment. dissolve : bool, default True If True, dissolve adjacent hexagons of the same niche into unified MultiPolygon geometries. If False, return individual hexagons with their niche assignment.

Returns

gpd.GeoDataFrame If dissolve=True: GeoDataFrame with dissolved niche polygons. Columns: "name", "cat", "geometry", "color", "area". If dissolve=False: GeoDataFrame with individual hexagons and niche assignment. Columns: "name", "cat", "geometry", "color".

Examples

Generate hexagons and compute population distribution

gdf_hex = dega.nbhd.generate_hextile(adata, diameter=100) nbhd = dega.nbhd.NeighborhoodCollection(gdf=gdf_hex) nbhd.calc_population(adata, category="leiden")

Cluster hexagons by population similarity (e.g., using scanpy)

import scanpy as sc sc.pp.pca(adata_hex) sc.pp.neighbors(adata_hex) sc.tl.leiden(adata_hex)

Create dissolved niche polygons

gdf_niche = dega.nbhd.hextile_niche(gdf_hex, adata_hex, category="leiden")

Or keep individual hexagons with niche assignment

gdf_hex_niche = dega.nbhd.hextile_niche(gdf_hex, adata_hex, dissolve=False)

iter_gene_alpha_shapes(coords, slice_ids, z_values, gene_expression, cell_ids=None, alphas=(150,), min_expression=2.0, min_cells=4, z_jitter=0.1, max_cells=50000, shape_max_cells=50000, random_state=0)

Yield (gene, gdf_gene, df_cells) one gene at a time, from a caller-supplied stream of (gene, expression_array) pairs — no AnnData involved.

This is the AnnData-free core iter_gene_alpha_shapes_by_slice wraps: that function exists for the interactive-analysis case where you already have (or are willing to build) an AnnData with every gene in gene_list loaded into .X. This function instead takes a plain per-cell coordinate/slice/Z array plus an iterable of (gene, expression_array) — the caller decides how each gene's expression array is produced, which matters when the source isn't already a combined multi-gene matrix: a plain generator can produce one gene's array at a time and let the caller's reference to it drop before the next gene is read, without ever assembling a combined multi-gene matrix (dense or sparse) or an AnnData wrapper around it — pure overhead when nothing downstream ever needs more than one gene's expression at a time.

Alongside each gene's shape, also selects up to max_cells of that gene's own highest-expressing cells (same min_expression threshold as the shape) — real cell centroids to "pepper" the alpha shape with in the frontend, grounding the coarse polygon in actual single-cell positions. Capped rather than exhaustive because an unbounded per-gene cell file would scale with how many cells express the gene, not with dataset size in general — a broadly-expressed gene could otherwise mean a cell file sized like the whole dataset.

Parameters

coords : np.ndarray Per-cell spatial coordinates, shape (n_cells, >=2) — only the first two columns are used for the alpha shape itself. slice_ids : np.ndarray Per-cell slice identifier, shape (n_cells,). z_values : np.ndarray Per-cell Z coordinate, shape (n_cells,) (all zeros for a 2D run). Expected constant within a slice, like elsewhere in this module. gene_expression : Iterable[tuple[str, np.ndarray]] Yields (gene, expr) pairs, expr a per-cell array aligned to coords/slice_ids/z_values (same order, same length). Genes are processed in the order this iterable yields them (and that order is what _gene_z_offset derives each gene's Z bucket from). cell_ids : np.ndarray | None Per-cell identifier, shape (n_cells,), aligned like coords. If None, df_cells is always empty (no cell selection performed) -- e.g. when a caller only wants shapes and doesn't have cell ids handy. alphas, min_expression, min_cells, z_jitter : Same meaning as alpha_shape_gene_expression_by_slice. max_cells : int Cap on the number of top-expressing cells selected per gene (across all slices combined). 0 (or cell_ids=None) skips cell selection entirely, yielding an always-empty df_cells. shape_max_cells : int | None Cap on the number of expressing cells (within one slice) that feed the alpha shape's own geometry computation -- a real alpha shape's cost scales with this count, and a broadly-expressed gene can have far more expressing cells than are needed to describe its spatial footprint. Above this many, a uniform random subsample is used instead (see _subsample_mask_for_shape for why random rather than top-expressing). None disables the cap (use every expressing cell). Does not affect mean_expression/cell_count, which are always computed from the full expressing population, or df_cells (governed by max_cells instead). random_state : int Seed for the subsampling RNG, so results are reproducible run to run for the same inputs.

Yields

tuple[str, gpd.GeoDataFrame, pd.DataFrame] (gene, gdf_gene, df_cells)gdf_gene as documented on iter_gene_alpha_shapes_by_slice. df_cells has columns cell_id, gene, slice_id, x, y, z, expression — one row per selected cell, empty (zero rows, same columns) if no cells were selected (no cell_ids, max_cells <= 0, or no expressing cells).

iter_gene_alpha_shapes_by_slice(adata, gene_list, slice_attr='slice_id', z_attr=None, alphas=(150,), min_expression=2.0, min_cells=4, z_jitter=0.1, max_cells=50000, shape_max_cells=50000, random_state=0)

Yield (gene, gdf_gene, df_cells) one gene at a time, instead of building the whole (slice, gene) result set in memory before returning anything.

An AnnData-sourcing wrapper around iter_gene_alpha_shapes: for each gene in gene_list, densifies that one column from adata.X and hands it to iter_gene_alpha_shapes (along with adata.obs_names as cell ids). Convenient when you already have (or are willing to build) an AnnData with every gene in gene_list loaded — alpha_shape_gene_expression_by_slice (a thin eager wrapper around this generator) is the interactive/analysis entry point for that case.

Parameters

adata, slice_attr, z_attr, alphas, min_expression, min_cells, z_jitter : Same meaning as alpha_shape_gene_expression_by_slice. gene_list : Sequence[str] Genes to compute shapes for, in the order they'll be processed (and the order _gene_z_offset derives each gene's Z bucket from). max_cells : int Forwarded to iter_gene_alpha_shapes — cap on top-expressing cells selected per gene (0 skips cell selection). shape_max_cells, random_state : Forwarded to iter_gene_alpha_shapes — cap (via uniform random subsample) on the expressing cells that feed the alpha shape's own geometry computation, and the seed for that subsampling.

Yields

tuple[str, gpd.GeoDataFrame, pd.DataFrame] (gene, gdf_gene, df_cells)gdf_gene has columns name (f"{slice_id}__{gene}"), gene, slice_id, geometry (3D), area, inv_alpha, cell_count, mean_expression, max_expression (whole-tissue single-cell max for that gene). Empty (zero rows, same columns) if the gene produced no usable shape in any slice -- below min_cells everywhere, or every candidate shape failed verification / GEOS choked (see alpha_shape). df_cells has columns cell_id, gene, slice_id, x, y, z, expression — see iter_gene_alpha_shapes.