Align Module API Reference
Alignment and registration of spatial single-cell data.
Covers three related registration problems: (1) stitching serial 3D tissue slices into a shared coordinate frame, (2) registering data onto a reference (e.g. a common coordinate framework/atlas such as the Allen Institute mouse brain, or a particular dataset within a cohort), and (3) registering across modalities (e.g. Xenium to H&E). All three align in physical (spatial) coordinates — image/tissue pixel or micron space. Batch correction / integration in expression or embedding space (e.g. Harmony, scVI, Seurat anchors) is a different problem and out of scope here, though a shared framework for evaluating alignment quality could plausibly extend to that space later.
Status: initial sketch. Implemented: a three-step serial-slice pipeline,
each step an independently reusable artifact. (1) :func:calc_landmarks
computes landmarks from shared cluster labels (accepting a list of
AnnData or one combined AnnData, no manual per-slice loop needed),
or a manually-placed landmark table in the same shape (a plain
DataFrame, not a GeoDataFrame of shapely geometry, so it stays
trivially disk-portable; e.g. from :class:~celldega.viz.Landmark, a
point-drawing widget pairing with Landscape) can be used instead, or
concatenated alongside it for a semi-manual mix. (2)
:func:calc_alignment_transform fits a rigid Procrustes
(:func:fit_transform_procrustes, always without scaling — see
:mod:celldega.align.serial_slices) or non-rigid thin-plate-spline
(:func:fit_transform_tps) transform per slice from those landmarks —
chain-walking outward from a reference slice against a window of
already-aligned neighbors (alignment_window, never a single distant
reference), optionally weighting landmarks by cell count
(weight_by_adjacent_counts) and recording per-landmark leave-one-out
residuals (:func:leave_one_out_residuals) — and returns a
:class:~celldega.align.serial_slices.SerialAlignmentTransform: a
first-class, reusable object (not a byproduct that only lives inside one
alignment call) that can be applied to other point data tied to the same
slices (segmentation-polygon vertices, transcript coordinates, eventually
raster sampling grids) via .apply_to_points(), and persisted with
.save()/.load() (a plain directory of .npz/.parquet/.json
files, no pickle required, though it's picklable too), and visually
sanity-checked with :func:plot_alignment (also available as
transform.plot()): a 2D before/after scatter of the fitted landmarks,
so a bad fit (or a mislabeled landmark) is visible at a glance rather than
only showing up downstream. (3)
:func:align_serial_slices applies a given transform to a specific set of
AnnData, aligning obsm["spatial"] and assigning a Z coordinate;
landmarks_initial/landmarks_aligned/fit parameters are also recorded in
the output's uns["align_serial_slices"] for at-a-glance provenance.
Planned: a reference/atlas-registration method sharing the same
landmark-based core (star topology onto one fixed reference, vs.
:func:align_serial_slices's neighbor-chain) and image-based
modality-to-modality registration (deferred — unlike the two centroid-based
methods, this operates on raw image data, not single-cell coordinates).
SerialAlignmentTransform
dataclass
A fitted, reusable serial-slice alignment.
Returned by :func:calc_alignment_transform; consumed by
:func:align_serial_slices. Holds one :class:~celldega.align._transform.Transform
per slice (the reference slice's is an identity transform) and provenance
from the fit — no Z information, since Z assignment doesn't affect the
spatial fit at all and is decided when applying the transform (see
:func:align_serial_slices's z_space/z_coord), not when fitting
it. Persist it with :meth:save/:meth:load — everything here is plain
data or a picklable-but-not-pickled RBFInterpolator, so it also
survives a plain :mod:pickle round-trip if that's more convenient.
apply_to_points(slice_id, points)
Apply the fitted transform for slice_id to an (n, 2) array of points.
The general reuse primitive: works identically for cell coordinates, segmentation-polygon vertices, transcript coordinates, or any other point data tied to that slice.
load(path)
classmethod
Load a transform previously saved with :meth:save.
plot(**kwargs)
2D before/after scatter of this alignment — a quick visual check
of fit quality. Pass adatas= (the slices this was fit from) to
overlay the actual cell centroids, which is what really shows whether
the tissue aligns; see :func:~celldega.align.plot.plot_alignment
for all accepted options.
save(path)
Save this transform to a directory of plain files — no pickle.
Layout: metadata.json (fit parameters, slice ids),
transform_log.json (per-slice diagnostics), landmarks_initial
.parquet/landmarks_aligned.parquet, and one transforms/<slice
id>.npz per slice (see :func:~celldega.align._transform.save_transform).
align_serial_slices(adatas, transform, z_space=1.0, z_coord=None, key_added='Z', cell_name_prefix=False)
Apply a fitted :class:SerialAlignmentTransform to a set of AnnData.
Z assignment lives here, not in :func:calc_alignment_transform — it
doesn't affect the spatial fit at all, so the same fitted transform can
be applied with different Z choices without refitting anything.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
adatas
|
AnnData | list[AnnData]
|
Either a list of per-slice |
required |
transform
|
SerialAlignmentTransform
|
A :class: |
required |
z_space
|
float
|
Uniform distance between consecutive slices, applied
outward from |
1.0
|
z_coord
|
list[float] | None
|
Explicit absolute Z value for each slice (length
|
None
|
key_added
|
str
|
Name of the new per-cell |
'Z'
|
cell_name_prefix
|
bool
|
If |
False
|
Returns:
| Type | Description |
|---|---|
AnnData
|
A new |
AnnData
|
x/y columns replaced by the aligned coordinates, a new |
AnnData
|
|
AnnData
|
landmark provenance recorded in |
AnnData
|
(plain, h5ad-safe data — the live |
AnnData
|
not stored there; keep or persist it separately, see |
AnnData
|
meth: |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
calc_alignment_transform(landmarks, slice_attr=None, reference=0, min_shared_landmarks=3, alignment_window=1, method='procrustes', allow_reflection=False, smoothing=0.0, degree=1, area_regularization=0.0, shape_regularization=0.0, weight_by_adjacent_counts=True, manual_landmark_weight='equal', compute_residuals=True)
Fit a serial-slice alignment transform from corresponding landmarks.
Each slice is registered onto a window of its already-aligned neighbors
by fitting a transform between corresponding landmarks. Alignment
proceeds as a chain outward from reference in both directions along
the slice order, so deformation is modeled between nearby physical
neighbors rather than against a single distant reference. This function
only touches landmarks — no cell data — so the returned transform
can be fit once and reused (see :func:align_serial_slices and
:meth:SerialAlignmentTransform.apply_to_points). Z assignment isn't
part of this fit — it doesn't affect the spatial transform at all — see
:func:align_serial_slices's z_space/z_coord instead.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
landmarks
|
DataFrame
|
A plain |
required |
slice_attr
|
str | None
|
The column in |
None
|
reference
|
int
|
Index (into the slice order) of the slice whose transform is the identity; other slices are aligned outward from it. |
0
|
min_shared_landmarks
|
int
|
Minimum number of landmark labels a slice and its neighbor window must share to fit a transform between them. |
3
|
alignment_window
|
int
|
Number of already-aligned neighboring slices (in
the same chain direction) to register each new slice against,
instead of only the single immediately-previous one. A landmark
label's target position is averaged across whichever of those
neighbors have it. Stays a local, neighbor-window operation —
never reaches back to a single distant reference — while
reducing sensitivity to any one neighbor's noise. |
1
|
method
|
str
|
|
'procrustes'
|
allow_reflection
|
bool
|
|
False
|
smoothing
|
float
|
|
0.0
|
degree
|
int
|
|
1
|
area_regularization
|
float
|
|
0.0
|
weight_by_adjacent_counts
|
bool
|
If |
True
|
manual_landmark_weight
|
str
|
How a landmark with no cell count (e.g. one
placed with :class: |
'equal'
|
compute_residuals
|
bool
|
If |
True
|
Returns:
| Type | Description |
|---|---|
SerialAlignmentTransform
|
The fitted :class: |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
calc_landmarks(adatas, cluster_key, slice_attr=None, label_prefix='C-')
Compute one landmark per cluster label, at that cluster's centroid.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
adatas
|
AnnData | list[AnnData]
|
A single |
required |
cluster_key
|
str
|
|
required |
slice_attr
|
str | None
|
For a single combined |
None
|
label_prefix
|
str
|
Prepended to each cluster label to form the landmark
|
'C-'
|
Returns:
| Name | Type | Description |
|---|---|---|
DataFrame
|
A |
|
DataFrame
|
label, as |
|
DataFrame
|
slice's own |
|
DataFrame
|
of cells in that cluster), and |
|
DataFrame
|
here) — plus a |
|
DataFrame
|
slices. This is the shape |
|
DataFrame
|
func: |
|
DataFrame
|
|
|
DataFrame
|
columns, |
|
see |
DataFrame
|
class: |
DataFrame
|
func: |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
fit_transform_procrustes(source, target, weights=None, allow_scaling=True, allow_reflection=False)
Fit the rotation/scale/translation that best maps source onto target.
Solves the classic Procrustes/Umeyama least-squares problem: minimize
sum(weights_i * ||target_i - (scale * rotation @ source_i + translation)||^2)
over a rotation, a single uniform scale, and a translation, given n
point-to-point correspondences (source[i] corresponds to target[i]).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
ndarray
|
|
required |
target
|
ndarray
|
|
required |
weights
|
ndarray | None
|
Optional |
None
|
allow_scaling
|
bool
|
If |
True
|
allow_reflection
|
bool
|
If |
False
|
Returns:
| Type | Description |
|---|---|
SimilarityTransform
|
The fitted :class: |
Raises:
| Type | Description |
|---|---|
ValueError
|
If fewer than 2 point pairs are given, shapes mismatch, or
|
fit_transform_tps(source, target, weights=None, smoothing=0.0, degree=1, normalize=True, area_regularization=0.0, shape_regularization=0.0)
Fit a thin-plate-spline warp that maps source landmarks onto target landmarks.
Unlike :func:fit_transform_procrustes, this fits a smooth non-rigid
deformation: it matches the landmarks locally rather than one global
rotation/scale/translation, so it can recover warps a rigid fit cannot
(e.g. non-uniform section stretching). Points far from every landmark
fall back toward the affine (degree-1 polynomial) component rather than
an arbitrary extrapolation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
ndarray
|
|
required |
target
|
ndarray
|
|
required |
weights
|
ndarray | None
|
Optional |
None
|
smoothing
|
float
|
Bending-energy penalty trading exact landmark matching
( |
0.0
|
degree
|
int
|
Degree of the polynomial term added to the spline ( |
1
|
normalize
|
bool
|
If |
True
|
area_regularization
|
float
|
Penalty in |
0.0
|
shape_regularization
|
float
|
Penalty in Both are applied as a single post-fit correction: the warp's
global affine |
0.0
|
Returns:
| Type | Description |
|---|---|
ThinPlateSplineTransform
|
The fitted :class: |
Raises:
| Type | Description |
|---|---|
ValueError
|
If fewer than 3 point pairs are given, shapes mismatch,
|
leave_one_out_residuals(source, target, fit_transform, weights=None)
Per-landmark leave-one-out residual: how well does the fit implied by the other landmarks predict this one?
In-sample residual is a poor diagnostic for an interpolating fit (e.g.
:func:fit_transform_tps at smoothing=0 matches every landmark
exactly by construction, regardless of whether the landmarks are actually
consistent). Leave-one-out residual instead measures, for each landmark,
whether it agrees with a fit built from everything except it — a large
value flags a landmark that may be mislabeled, noisy, or otherwise
inconsistent with the rest.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
ndarray
|
|
required |
target
|
ndarray
|
|
required |
fit_transform
|
Callable[..., Transform]
|
A |
required |
weights
|
ndarray | None
|
Optional |
None
|
Returns:
| Type | Description |
|---|---|
ndarray
|
An |
ndarray
|
whose leave-one-out refit failed (e.g. too few or degenerate points |
ndarray
|
remained). |
load_transform(path)
Load a transform previously saved with :func:save_transform.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | Path
|
Path to the |
required |
Returns:
| Type | Description |
|---|---|
Transform
|
The reconstructed :class: |
Transform
|
class: |
Transform
|
|
Transform
|
numerically equivalent to the one originally fitted. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the file's |
neighborhood_alignment(shapes, initial_transform, slice_attr='slice_id', cluster_attr='cluster_id', alignment_window=None, n_sweeps=2, rotation_range=10.0, translation_range=0.1, n_rotation_grid=7, distance_weight='inverse', distance_decay=1.0, simplify_tolerance=None, min_shared_clusters=1, compute_diagnostics=True, progress_every=0)
Refine a serial-slice alignment by maximizing neighborhood polygon overlap.
Starts from initial_transform (a cluster-centroid Procrustes fit from
:func:~celldega.align.serial_slices.calc_alignment_transform) and adds,
per slice, a small residual rigid transform (rotation + translation, never
scale) chosen to maximize the summed intersection area of corresponding
neighborhood regions across a window of neighboring slices. This recovers
alignment that region centroids miss — footprint orientation and bilateral
structure a centroid averages away — while staying rigid and reusing the
Procrustes fit as a starting point close enough for the (nonsmooth) overlap
objective to optimize locally. The regions are typically cluster alpha
shapes, but any per-(slice, region) polygons with labels shared across
slices work (manually drawn regions, other domain-ID algorithms).
Optimization is block coordinate descent: the reference slice is held
fixed, and every other slice is optimized in turn against its neighbors'
current estimates, in alternating forward and backward passes over
n_sweeps sweeps. Each per-slice step coarse-searches residual rotation
on a grid, then refines all three parameters with a bounded Powell search
(see :func:_refine_one_slice).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
shapes
|
GeoDataFrame
|
A per-(slice, region) polygon |
required |
initial_transform
|
SerialAlignmentTransform
|
The Procrustes
:class: |
required |
slice_attr
|
str
|
Column in |
'slice_id'
|
cluster_attr
|
str
|
Column in |
'cluster_id'
|
alignment_window
|
int | None
|
Number of neighboring slices on each side to score a
slice's overlap against. |
None
|
n_sweeps
|
int
|
Number of forward+backward coordinate-descent sweeps over the non-reference slices. |
2
|
rotation_range
|
float
|
Bound (in degrees) on each slice's residual rotation,
|
10.0
|
translation_range
|
float
|
Bound on each slice's residual translation, as a
fraction of the overall tissue width (largest x/y extent across all
shapes), applied |
0.1
|
n_rotation_grid
|
int
|
Number of grid points in the coarse residual-rotation
search over |
7
|
distance_weight
|
str
|
How a neighbor's overlap contribution falls off with
slice separation |
'inverse'
|
distance_decay
|
float
|
Falloff scale for the |
1.0
|
simplify_tolerance
|
float | None
|
If given, Douglas-Peucker tolerance applied to each
alpha shape before optimization, to speed up intersection at the
cost of boundary detail. |
None
|
min_shared_clusters
|
int
|
A slice is only refined if it shares at least this many cluster labels with its neighbor window; otherwise its residual stays identity (it keeps its Procrustes transform) and it is marked skipped in the transform log. |
1
|
compute_diagnostics
|
bool
|
If |
True
|
progress_every
|
int
|
If |
0
|
Returns:
| Type | Description |
|---|---|
SerialAlignmentTransform
|
A refined :class: |
SerialAlignmentTransform
|
with |
SerialAlignmentTransform
|
residual delta composed with its initial Procrustes transform, ready to |
SerialAlignmentTransform
|
pass to :func: |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
plot_alignment(transform, adatas=None, slice_attr=None, color_by='slice', max_cells_per_slice=20000, cell_size=1.0, figsize=(12, 6), random_state=0)
Side-by-side 2D scatter of an alignment, before vs. after fitting.
"Before" is each slice in its own native coordinates; "after" is every slice warped into the reference slice's frame by its fitted transform.
Pass adatas (strongly recommended) to overlay each slice's actual
cell centroids underneath the landmarks — that's what actually shows
whether the tissue aligns. Landmarks alone are a poor check: an
interpolating fit lands every landmark exactly on top of its match by
construction, so the landmark-only "after" panel looks perfect
regardless of how well the surrounding tissue really lines up.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
transform
|
SerialAlignmentTransform
|
A fitted :class: |
required |
adatas
|
AnnData | list[AnnData] | None
|
The same slices used to fit |
None
|
slice_attr
|
str | None
|
For a single combined |
None
|
color_by
|
str
|
|
'slice'
|
max_cells_per_slice
|
int
|
Randomly subsample each slice's centroids to at
most this many before plotting ( |
20000
|
cell_size
|
float
|
Marker size for cell centroids (landmarks are drawn larger, on top). |
1.0
|
figsize
|
tuple[float, float]
|
Passed to :func: |
(12, 6)
|
random_state
|
int
|
Seed for the subsampling RNG, so the figure is reproducible. |
0
|
Returns:
| Type | Description |
|---|---|
Figure
|
|
tuple[Axes, Axes]
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
save_transform(transform, path)
Save a fitted :class:SimilarityTransform or :class:ThinPlateSplineTransform
to a plain .npz file — no pickle involved, so the result is portable,
inspectable with any numpy install, and doesn't depend on matching library
versions the way a pickled object graph would. A :class:ThinPlateSplineTransform
is saved as the plain-array inputs (landmark positions, per-point smoothing,
kernel, epsilon, degree, plus the source normalization center/scale) that
reconstruct an equivalent RBFInterpolator, not the fitted object itself.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
transform
|
Transform
|
The transform to save. |
required |
path
|
str | Path
|
Destination |
required |
Raises:
| Type | Description |
|---|---|
TypeError
|
If |
transform_shapes(shapes, transform, slice_attr='slice_id')
Apply a serial-slice transform to an alpha-shape GeoDataFrame.
Moves each shape's geometry by its slice's fitted transform, in place of
recomputing alpha shapes from transformed cell coordinates. Handy for
inspecting or plotting the refined neighborhood shapes (the polygon
companion to
:meth:~celldega.align.serial_slices.SerialAlignmentTransform.apply_to_points).
Any Z coordinate is passed through unchanged, so note it does not restamp
the aligned per-slice Z — for a 3D NeighborhoodCloud, write the shapes with
:func:~celldega.align.write_nbhd_cloud (which recomputes from the aligned
coordinates and stamps Z consistently) rather than feeding transformed
shapes directly. The area column, if present, is recomputed from the
transformed (rigid, so area-preserving) geometry.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
shapes
|
GeoDataFrame
|
Alpha shapes with a |
required |
transform
|
SerialAlignmentTransform
|
A fitted
:class: |
required |
slice_attr
|
str
|
Column in |
'slice_id'
|
Returns:
| Type | Description |
|---|---|
GeoDataFrame
|
A copy of |
GeoDataFrame
|
Rows whose slice id is not in |
write_alignment_point_cloud(adata, dega_files_dir, alignment_name, *, z_key='Z', cluster_key=None, write_genes=None, write_base_metadata=None, overwrite=False)
Write aligned 3D cell centroids to a point-cloud DegaFiles as a named alignment.
Parameters
adata : AnnData
Aligned AnnData. obsm["spatial"] provides x/y (its first two
columns); z is taken from obs[z_key] if present, else from a third
spatial column, else 0. Cell names come from obs_names.
dega_files_dir : str or Path
Target point-cloud DegaFiles directory. If it already contains
landscape_parameters.json the writer appends (positions +
registration only); otherwise it creates a fresh point-cloud
directory.
alignment_name : str
Name of this alignment variant. The positions are written to
cell_metadata_<alignment_name>.parquet and the name is registered in
landscape_parameters.json under "alignments". View it with
Landscape(..., alignment="<alignment_name>").
z_key : str, default "Z"
obs column holding the per-slice z (depth) coordinate.
cluster_key : str, optional
Only used in create mode: obs column to export as clusters (with
colors from uns[f"{cluster_key}_colors"] when available). Ignored in
append mode, where existing clusters are reused.
write_genes : bool, optional
Only used in create mode. Whether to export gene expression to
cbg/ + meta_gene.parquet (keyed by cell name). Defaults to
True when adata carries an expression matrix
(layers['counts'] or X) and False otherwise. Ignored in
append mode, where existing gene data is reused.
write_base_metadata : bool, optional
Whether to also (over)write the base cell_metadata.parquet with these
positions. Defaults to True in create mode and False in append
mode (so an append never clobbers the existing default positions).
overwrite : bool, default False
Allow overwriting an existing cell_metadata_<alignment_name>.parquet.
Returns
pathlib.Path
Path to the written cell_metadata_<alignment_name>.parquet.
write_nbhd_cloud(adata, dega_files_dir, *, cluster_attr='cluster', slice_attr='slice_id', z_attr=None, alphas=(150,), z_jitter=0.1, meta_cluster=None, save_genes=True, progress_every=1, max_cell_scatter=50000, cell_scatter_random_state=0, compute_gene_nbhds=False, gene_list=None, gene_min_expression=2.0, gene_min_cells=4, gene_max_cells=50000, gene_shape_max_cells=50000, gene_random_state=0, gene_z_jitter=None, gene_progress_every=500)
Write a full neighborhood-cloud DegaFiles set from an aligned AnnData.
Computes one real alpha-shape polygon per (slice, cluster) — using
celldega.nbhd's alpha-shape machinery, the same well-tested geometry
code the rest of celldega already relies on, not a separate/duplicated
implementation — and writes the full DegaFile layout
(meta_slice.parquet, shapes/by_slice/, cells/by_cluster/,
meta_neighborhood.parquet, cell_clusters/meta_cluster.parquet,
landscape_parameters.json).
Gene-nbhds (a curated marker-gene list's own alpha shapes, "peppered"
with real expressing-cell centroids) are off by default: a real
alpha shape per gene is expensive enough — and only useful for a
deliberately chosen list of marker genes, not the whole transcriptome —
that auto-computing it isn't the right default. Pass
compute_gene_nbhds=True with an explicit gene_list to also write
shapes/by_gene/ + cells/by_gene/ for those genes.
Works on any AnnData with the right obs/obsm columns, not just the
output of celldega.align specifically — e.g. straight off
align_serial_slices (which already sets obsm["spatial"], obs["Z"],
and carries whatever cluster/slice columns were used for landmarks), or
any other aligned 3D AnnData with equivalent columns.
Parameters
adata : AnnData
Aligned 3D cell-level AnnData: obsm["spatial"] (x, y),
obs[cluster_attr], obs[slice_attr], optionally obs[z_attr].
For gene-nbhds, also needs a real expression matrix in .X
(adata[:, gene].X must be numeric counts/expression, not a
placeholder).
dega_files_dir : str | Path
Output DegaFiles root directory (created if missing). Under the
one-alignment-per-cloud convention this directory name is the
NeighborhoodCloud's identity — write a new alignment to a new directory.
cluster_attr, slice_attr, z_attr : str, str, str | None
See celldega.nbhd.alpha_shape_cell_clusters_by_slice.
alphas : Sequence[float]
Single inverse-alpha resolution used for both cluster shapes and (if
requested) gene shapes — see alpha_shape_cell_clusters_by_slice.
z_jitter : float
Per-cluster Z offset within a slice, to avoid z-fighting between
coplanar cluster polygons (see alpha_shape_cell_clusters_by_slice).
meta_cluster : pd.DataFrame | None
Optional cluster color/metadata lookup. Without it, colors come from
adata.uns[f"{cluster_attr}_colors"] if present (the usual place
scanpy leaves them after sc.tl.leiden/sc.pl.umap), else a fallback
palette (so neighborhoods aren't all black).
save_genes : bool
Whether to write gene-expression data. When True (default),
meta_gene.parquet (the dataset-root per-gene stats the gene search
box/bar graph read) is written; when False, all gene-expression
output is skipped for a lean, clusters-only cloud (useful for
label-only AnnData, or when gene coloring isn't needed). Must be True
to also compute gene-nbhds (compute_gene_nbhds).
progress_every : int
Print a progress line every this many slices while computing cluster
shapes (default 1, i.e. every slice — a real alpha shape per
(slice, cluster) is the slow part of this call on a large aligned
dataset with many slices). 0 disables it.
max_cell_scatter : int | None
Cap (via uniform random subsample) on the cells written per cluster
to cells/by_cluster/cluster_<id>.parquet — this is the same "cap
peppering to a scatter" idea used for genes (see gene_max_cells
below), applied to cluster cells, which today have no other bound.
None writes every cell in the cluster (the original, uncapped
behavior). Default 50_000.
cell_scatter_random_state : int
Seed for max_cell_scatter's subsampling RNG.
compute_gene_nbhds : bool
Whether to also compute and write gene-nbhds. Default False — see
above. Requires gene_list.
gene_list : Sequence[str] | None
Genes to compute shapes for when compute_gene_nbhds=True. Required
in that case — deliberately not auto-selected from the whole gene
panel, since a real alpha shape per gene is too expensive to want by
accident. Ignored otherwise.
gene_min_expression, gene_min_cells, gene_max_cells, gene_progress_every :
Forwarded to celldega.pre.write_gene_shapes_streaming — see its
docstring for min_expression/min_cells/max_cells/
progress_every.
gene_shape_max_cells : int | None
Cap (via uniform random subsample) on the expressing cells that feed
a gene's alpha shape's own geometry computation, forwarded to
write_gene_shapes_streaming. This is what actually bounds the
expensive part of a broadly-expressed gene's shape -- even a small,
curated gene_list (tens to ~100 marker genes) can include a gene
expressed in far more cells than are needed to describe its spatial
footprint. None disables the cap. Default 50_000.
gene_random_state : int
Seed for gene_shape_max_cells's subsampling RNG.
gene_z_jitter : float | None
Per-gene Z offset (see write_gene_shapes_streaming). Defaults to
z_jitter (the same value used for cluster shapes) if not given.
Returns
pathlib.Path
dega_files_dir, as a Path.
Examples
from celldega.align import write_nbhd_cloud write_nbhd_cloud(adata_aligned, "my_dataset_nbhd_cloud")
Later, once you know which marker genes you care about:
write_nbhd_cloud( ... adata_aligned, ... "my_dataset_nbhd_cloud", ... compute_gene_nbhds=True, ... gene_list=["Matn1", "Col2a1", "Col1a1"], ... )