Vector fields (velocity / drift overlays)#

Cellucid “vector fields” are per-cell displacement vectors in embedding space.

They enable overlays such as:

  • RNA velocity / flow arrows (scVelo-style, if you already have vectors)

  • CellRank drift vectors derived from a transition matrix

This page documents:


Mental model (beginner-friendly)#

  • You have an embedding like UMAP: X_umap with shape (n_cells, 2) or (n_cells, 3).

  • You compute (or already have) vectors with the same shape.

  • Cellucid visualizes those vectors as an animated overlay on top of the embedding.

Vectors are not “absolute positions”; they are arrows attached to each cell.


Practical path (common workflows)#

1) From a CellRank transition matrix → drift vectors#

from cellucid import compute_transition_drift

drift = compute_transition_drift(T, adata.obsm["X_umap"], normalize_rows=True)

Where:

  • T is (n_cells, n_cells) (dense or sparse)

  • adata.obsm["X_umap"] is (n_cells, dim)

2) Store drift in adata.obsm using Cellucid naming conventions#

from cellucid import add_transition_drift_to_obsm

key = add_transition_drift_to_obsm(
    adata,
    T,
    basis="umap",
    field_prefix="T_fwd",
)
print("Wrote vector field to:", key)

This writes a key like:

  • T_fwd_umap_2d or T_fwd_umap_3d

3) Export the vectors so the viewer can load them#

from cellucid import prepare

prepare(
    latent_space=adata.obsm["X_pca"],
    obs=adata.obs,
    var=adata.var,
    gene_expression=adata.X,
    X_umap_2d=adata.obsm.get("X_umap_2d", adata.obsm["X_umap"]),
    vector_fields={
        # You can pass the new obsm entry directly
        key: adata.obsm[key],
    },
    out_dir="./my_export",
)

Naming conventions (important)#

Cellucid’s recommended keys for vector fields follow:

  • Explicit: <field>_<basis>_<dim>d

    • examples: velocity_umap_2d, T_fwd_umap_3d

  • Implicit: <field>_<basis> (only if you’re sure there’s no ambiguity)

Why explicit keys are recommended:

  • it avoids clashes when you have both 2D and 3D embeddings,

  • it makes export/serve behavior deterministic.


API reference#

cellucid.compute_transition_drift(transition_matrix, embedding, *, normalize_rows=True)[source]#

Compute a per-cell drift vector field from a transition matrix.

The drift is defined as:

drift = E[next_embedding | current_cell] - current_embedding

Where:

E[next_embedding] = T @ embedding

Parameters:
  • transition_matrix (Union[ndarray, spmatrix]) – A (n_cells, n_cells) matrix. Sparse matrices are recommended for large datasets. Typical source: CellRank kernels’ transition matrices.

  • embedding (ndarray) – A (n_cells, dim) array of embedding coordinates (e.g. adata.obsm[‘X_umap’]).

  • normalize_rows (bool) – If True (default), divides each row’s expectation by its row-sum. This makes the drift invariant to a matrix that isn’t strictly row-stochastic.

Returns:

Drift vectors shaped (n_cells, dim) as float32.

Return type:

np.ndarray

cellucid.add_transition_drift_to_obsm(adata, transition_matrix, *, basis='umap', field_prefix='T_fwd', dim=None, explicit_dim_suffix=True, normalize_rows=True, overwrite=False)[source]#

Compute drift vectors from a transition matrix and store them in adata.obsm.

The output key follows Cellucid’s vector field naming convention:
  • Explicit: <field_prefix>_<basis>_<dim>d (default)

  • Implicit: <field_prefix>_<basis>

Examples

  • Forward drift in UMAP (2D): T_fwd_umap_2d

  • Backward drift in UMAP (2D): T_bwd_umap_2d

Parameters:
  • adata (anndata.AnnData) – AnnData object to modify.

  • transition_matrix (Union[ndarray, spmatrix]) – (n_cells, n_cells) matrix, dense or sparse.

  • basis (str) – Embedding basis name (default: “umap”).

  • field_prefix (str) – Prefix for the vector field (default: “T_fwd”). Use e.g. “T_bwd” for backward matrices.

  • dim (Optional[int]) – Embedding dimensionality to use. If None, it is inferred from the best available embedding in obsm: X_{basis}_3dX_{basis}_2dX_{basis}_1dX_{basis} (if 1D/2D/3D).

  • explicit_dim_suffix (bool) – If True, appends _{dim}d to the output key (recommended for clash-safe imports).

  • normalize_rows (bool) – Whether to row-normalize during drift computation (see compute_transition_drift).

  • overwrite (bool) – If False (default), raises if the target key already exists.

Returns:

The obsm key written.

Return type:

str


Edge cases (do not skip)#

Row normalization (transition matrices)#

  • If your transition matrix is not row-stochastic, set normalize_rows=True (default).

  • Rows with sum 0 are handled safely (division-by-zero is avoided), but the resulting drift can be zero/undefined depending on the matrix.

Shape mismatches#

  • transition_matrix must be (n_cells, n_cells)

  • embedding must be (n_cells, dim)

  • The product T @ embedding must produce (n_cells, dim)

Dimension mismatch between export and vectors#

  • If you export X_umap_3d but only provide *_umap_2d vectors, the 3D overlay will not be available.


Troubleshooting (symptom → diagnosis → fix)#

Symptom: “embedding must be 2D”#

Fix:

  • Ensure you pass an array shaped (n_cells, dim) (not a flattened vector).

Symptom: “T @ embedding produced shape …”#

Fix:

  • Confirm T is (n_cells, n_cells) and aligned to the same cell order as the embedding.


Symptom: “Vector overlay is not available / not visible in the viewer”#

Likely causes:

  • You did not export the vector field (or you are serving in AnnData mode without exposing it).

  • The key naming doesn’t match the embedding dimension currently shown (2D vs 3D).

How to confirm:

  • In an exported folder, check that vectors/ exists and contains files.

  • In AnnData mode, confirm the vector field exists in adata.obsm with the expected key.

Fix:

  • Export vectors via prepare(..., vector_fields={...}).

  • Prefer explicit keys like velocity_umap_2d and velocity_umap_3d if you use multiple dimensions.


Symptom: “Drift vectors look backwards”#

Likely causes:

  • You used a backward transition matrix or the wrong convention for field_prefix.

Fix:

  • Compute/label forward vs backward consistently (e.g., field_prefix="T_fwd" vs "T_bwd").

  • Validate by checking a few expected transitions in a small subset.


See also#