pycea.tl.ancestral_linkage

pycea.tl.ancestral_linkage#

pycea.tl.ancestral_linkage(tdata, groupby, target=None, aggregate=None, metric='lca', symmetrize='mean', normalize=True, min_size=1, test=None, alternative='one-sided', permutation_mode='non_target', n_permutations=100, n_threads=None, by_tree=False, depth_key='depth', random_state=None, key_added=None, tree=None, copy=False)#

Quantify relatedness of cells in different categories.

For each cell, the tree distance to the nearest cell of each target category is computed. These per-cell distances are then averaged across all cells of the same source category to produce a directional linkage score: a low path distance (or high LCA depth) between two categories means they tend to share recent common ancestors, i.e. they are closely related on the tree.

Pairwise mode (target=None): computes a category × category matrix of mean linkage scores and stores it in tdata.uns['{key_added}_linkage'].

Single-target mode (target=<category>): computes the per-cell distance to the nearest cell of the given category and stores it in tdata.obs['{target}_linkage'].

Parameters:
  • tdata (TreeData) – The TreeData object.

  • groupby (str) – Column in tdata.obs that defines cell categories.

  • target (str | None (default: None)) – If specified, compute the per-cell distance to the nearest cell of this category and store the result in tdata.obs['{target}_linkage']. aggregate is ignored in this mode. If None (default), compute the full pairwise category × category matrix.

  • aggregate (Union[Literal['min', 'max', 'mean'], Callable, None] (default: None)) –

    How per-cell distances to the target category are aggregated into a single per-cell score (pairwise mode only). Defaults to 'min' for metric='path' and 'max' for metric='lca', both of which select the nearest relative:

    • 'min': distance to the closest target cell (natural for 'path').

    • 'max': depth of the deepest (most recent) LCA across target cells (natural for 'lca').

    • 'mean': mean distance across all target cells.

    • A callable f(array) -> float for custom aggregation.

  • metric (Literal['lca', 'path'] (default: 'lca')) –

    How tree distance between two cells is measured:

    • 'lca' (default): depth of the lowest common ancestor dLCA(i,j)d_{\mathrm{LCA}(i,j)}. Larger values mean closer relatives.

    • 'path': branch-length path distance di+dj2dLCA(i,j)d_i + d_j - 2\,d_{\mathrm{LCA}(i,j)}. Smaller values mean closer relatives.

  • symmetrize (Literal['mean', 'max', 'min', False] (default: 'mean')) –

    How to symmetrize the pairwise linkage matrix (pairwise mode only). Because linkage is directional (source → target), the raw matrix is generally asymmetric; symmetrization combines both directions:

    • 'mean' (default): average of M[i,j]M[i,j] and M[j,i]M[j,i].

    • 'max' / 'min': element-wise maximum / minimum.

    • False: leave the matrix asymmetric.

  • normalize (bool (default: True)) – If True (default), subtract the permuted mean from the observed values: pairwise linkage matrix becomes observed - permuted_mean; single-target tdata.obs['{target}_linkage'] becomes cell_score - category_permuted_mean. This works regardless of test: a single permutation is run to estimate the permuted mean when test=None (see n_permutations).

  • min_size (int (default: 1)) – Minimum number of cells a category must have to be included (pairwise mode only). Categories with fewer than min_size cells are dropped before the linkage matrix is computed, so they appear as neither source rows nor target columns and their cells do not contribute to any score. Defaults to 1 (no filtering). A warning lists any retained categories with fewer than 10 cells, whose linkage will be noisy.

  • test (Literal['permutation', None] (default: None)) –

    Optional significance test:

    • 'permutation': randomly shuffle cell-category labels n_permutations times and recompute linkage each time to build a null distribution. Z-scores and p-values are added to the stats table.

  • non

    The alternative hypothesis for the permutation test (ignored when test=None):

    • 'one-sided' (default): one-tailed test in the “more closely related than chance” direction — p-value is the fraction of permutations with LCA depth ≥ observed (metric='lca') or path distance ≤ observed (metric='path').

    • 'two-sided': two-tailed test — p-value is the fraction of permutations whose deviation from the null mean is at least as large as the observed deviation.

  • permutation_mode (Literal['all', 'non_target'] (default: 'non_target')) –

    How category labels are shuffled to build the permutation null (used both for the significance test and for the normalize permuted mean):

    • 'non_target' (default): fix the target category’s leaves at their tree positions and shuffle only the non-target labels. The null therefore reflects “random cells near this specific target cluster,” which removes inflation caused by small, tightly clustered target categories. In pairwise mode each target column gets its own independent null distribution.

    • 'all': shuffle all cell-category labels across all leaves. Tests whether the two categories are more associated on the tree than random, but does not control for the target category’s cluster structure.

  • n_permutations (int (default: 100)) – Number of label permutations used when test='permutation'. When test=None a single permutation is used to estimate the permuted mean for normalize.

  • n_threads (int | None (default: None)) – Number of worker processes for parallel permutation computation. None (default) runs serially. On Linux, parallel execution uses fork-based processes, which copy the parent’s memory without serialisation overhead. On other platforms this argument is ignored.

  • depth_key (str (default: 'depth')) – Node attribute in tdata.obst[tree] that stores each node’s depth.

  • random_state (int | None (default: None)) – Random seed for reproducibility of permutation tests.

  • key_added (str | None (default: None)) – Base key for output storage. Defaults to groupby.

  • tree (str | Sequence[str] | None (default: None)) – The obst key or keys of the trees to use. If None, all trees are used.

  • copy (Literal[True, False] (default: False)) – If True, return the result as a DataFrame.

Return type:

None | DataFrame

Returns:

Returns None if copy=False, otherwise returns a DataFrame.

Sets the following fields:

  • tdata.obs['{target}_linkage']Series (dtype float) – single-target mode only.

    Per-cell distance to the nearest cell of the target category. When normalize=True, replaced by cell_score - category_permuted_mean.

  • tdata.uns['{key_added}_linkage']DataFrame – pairwise mode only.

    Category × category linkage matrix (source rows, target columns). When normalize=True, contains observed - permuted_mean instead of raw distances.

  • tdata.uns['{key_added}_linkage_params']dict – pairwise mode only.

    Parameters used to compute the linkage matrix.

  • tdata.uns['{key_added}_linkage_stats']DataFrame – pairwise mode only.

    Long-form table with one row per (source, target) pair containing value, source_n, target_n, and permuted_value (always, from at least one permutation), plus z_score and p_value when test='permutation'.

Examples

Compute pairwise linkage between all cell types using path distance:

>>> tdata = py.datasets.koblan25()
>>> py.tl.ancestral_linkage(tdata, groupby="celltype")

Compute per-cell distance to the closest cell of type “B” with permutation test:

>>> py.tl.ancestral_linkage(tdata, groupby="celltype", target="B", test="permutation")