Skill Tracer
Skills › Scientific Agent Skills › scvelo

scvelo

skill · 219 lines · BSD-3-Clause · skills/scvelo/SKILL.md

The facts

Its description

Performs RNA velocity analysis with scVelo from spliced and unspliced single-cell RNA counts. Fits deterministic or dynamical models, examines gene phase portraits, builds velocity graphs, estimates relative latent time, and ranks velocity-associated genes. Use for directional trajectory hypotheses and kinetic-model diagnostics alongside Scanpy; velocity alone does not establish cell fate or causal drivers.

The description the agent reads to decide when to use it.

From its settingsline 3

Runs scripts

bin/python, scripts/rna_velocity_workflow.py

Scripts the text names. Found by matching words, so treat them as pointers.

Found by matching wordsline 34 line 89 line 93 line 98

Files it mentions

references/velocity_models.md, scripts/rna_velocity_workflow.py

Files the text names, such as ones to read or write. Found by matching words, so treat them as pointers.

Found by matching wordsline 18 line 89

Licence

BSD-3-Clause, from the skill file

The terms its author shares it under, which let it be shown here.

From its settingsline 4 licence text

Other settings

compatibility: Requires Python 3.13 for the tested stack with scvelo 0.3.4, scanpy 1.12.4, anndata 0.13.4, numpy 2.5.3 and pandas 2.3.3. Loom import requires loompy. Local H5AD analysis needs no network or credentials. The default stochastic solver is incompatible with NumPy 2. metadata: version: "2.0" skill-author: Kuan-lin Huang last-reviewed: "2026-10-01"

More settings at the top of the file, shown as written.

From its settingsline 5 line 6
Other files in this skill (2) Close

The text

Open on GitHub

Most marks come from matching words in the file, so treat them as pointers.

1---
2name: scvelo
3description: Performs RNA velocity analysis with scVelo from spliced and unspliced single-cell RNA counts. Fits deterministic or dynamical models, examines gene phase portraits, builds velocity graphs, estimates relative latent time, and ranks velocity-associated genes. Use for directional trajectory hypotheses and kinetic-model diagnostics alongside Scanpy; velocity alone does not establish cell fate or causal drivers.
4license: BSD-3-Clause
5compatibility: Requires Python 3.13 for the tested stack with scvelo 0.3.4, scanpy 1.12.4, anndata 0.13.4, numpy 2.5.3 and pandas 2.3.3. Loom import requires loompy. Local H5AD analysis needs no network or credentials. The default stochastic solver is incompatible with NumPy 2.
6metadata:
7 version: "2.0"
8 skill-author: Kuan-lin Huang
9 last-reviewed: "2026-10-01"
10---
12# scVelo RNA Velocity

scVelo RNA Velocity

Raw lines 1 to 13
14## When to use
16Use for kinetic analysis of aligned spliced/unspliced RNA counts, directional
17trajectory hypotheses, gene-level phase portraits, and dynamical latent time.
18Use the [model reference](references/velocity_models.md) for assumptions,
19transition probabilities, and an optional CellRank handoff.
21RNA velocity estimates an expression derivative under a model. Smooth arrows
22are not observed cell movement, lineage tracing, causal drivers, or proof of
23future fate. The dynamical estimator is not automatically more accurate than
24a steady-state estimator on every dataset.

When to use

Use for kinetic analysis of aligned spliced/unspliced RNA counts, directional trajectory hypotheses, gene-level phase portraits, and dynamical latent time. Use the model reference for assumptions, transition probabilities, and an optional CellRank handoff.

RNA velocity estimates an expression derivative under a model. Smooth arrows are not observed cell movement, lineage tracing, causal drivers, or proof of future fate. The dynamical estimator is not automatically more accurate than a steady-state estimator on every dataset.

Raw lines 14 to 25
26## Tested environment and compatibility
28The maintained workflow targets scVelo **0.3.4**, Scanpy **1.12.4**, AnnData
29**0.13.4**, NumPy **2.5.3**, pandas **2.3.3**, SciPy **1.18.1**, Matplotlib
30**3.11.2**, and loompy **3.0.8** on Python 3.13. Use an isolated environment:
32```bash
33uv venv --python 3.13 .venv-velocity
34uv pip install --python .venv-velocity/bin/python \
35 scvelo==0.3.4 scanpy==1.12.4 anndata==0.13.4 numpy==2.5.3 \
36 pandas==2.3.3 scipy==1.18.1 matplotlib==3.11.2 loompy==3.0.8
37```
39The packages above were resolved from cache and tested in an isolated `uv`
40environment; shell paths above use POSIX syntax. Installation requires network
41access unless packages are cached. No API key is required.
43- **Deterministic and dynamical models:** native synthetic fitting, graphs,
44 relative times, plots and H5AD round trips are tested on this stack.
45- **Stochastic model:** scVelo 0.3.4's default GLS scalar assignment fails with
46 NumPy 2. Scanpy 1.12 requires NumPy 2, so installing `numpy<2` beside current
47 Scanpy is not a solution. A separately validated legacy Scanpy/NumPy environment
48 is required; that legacy stack was not executed in this review. The helper
49 rejects this combination before modifying data, without changing the model.
50- **pandas 3:** scVelo 0.3.4 dynamical fitting and some plots use operations no
51 longer supported by pandas 3. Keep the explicit pandas pin.
52- The upstream stable tutorial still contains removed `scv.read` and older
53 preprocessing calls. Check the [0.3.4 source](https://github.com/theislab/scvelo/tree/v0.3.4)
54 when a tutorial disagrees with the installed API.

Tested environment and compatibility

The maintained workflow targets scVelo 0.3.4, Scanpy 1.12.4, AnnData 0.13.4, NumPy 2.5.3, pandas 2.3.3, SciPy 1.18.1, Matplotlib 3.11.2, and loompy 3.0.8 on Python 3.13. Use an isolated environment:

uv venv --python 3.13 .venv-velocity
uv pip install --python .venv-velocity/bin/python \
  scvelo==0.3.4 scanpy==1.12.4 anndata==0.13.4 numpy==2.5.3 \
  pandas==2.3.3 scipy==1.18.1 matplotlib==3.11.2 loompy==3.0.8

The packages above were resolved from cache and tested in an isolated uv environment; shell paths above use POSIX syntax. Installation requires network access unless packages are cached. No API key is required.

  • Deterministic and dynamical models: native synthetic fitting, graphs, relative times, plots and H5AD round trips are tested on this stack.
  • Stochastic model: scVelo 0.3.4's default GLS scalar assignment fails with NumPy 2. Scanpy 1.12 requires NumPy 2, so installing numpy<2 beside current Scanpy is not a solution. A separately validated legacy Scanpy/NumPy environment is required; that legacy stack was not executed in this review. The helper rejects this combination before modifying data, without changing the model.
  • pandas 3: scVelo 0.3.4 dynamical fitting and some plots use operations no longer supported by pandas 3. Keep the explicit pandas pin.
  • The upstream stable tutorial still contains removed scv.read and older preprocessing calls. Check the 0.3.4 source when a tutorial disagrees with the installed API.
Raw lines 26 to 55
Runs code bin/python :34
56## Input contract
581. Obtain spliced and unspliced counts from a velocity-aware quantifier. Keep
59 quantifier/version, reference annotation, counting mode, sample IDs and cell
60 barcode mapping. Upstream quantification is outside the bundled script.
612. Supply **cells by genes** matrices in `adata.layers['spliced']` and
62 `adata.layers['unspliced']`, with identical cell/gene ordering and unique IDs.
63 Counts must be finite and nonnegative; fractional count estimates are allowed.
64 Never use logged, scaled, residualized, or batch-corrected values as counts.
65 Numerical inspection alone cannot establish that data are raw.
663. Inspect per-library/cluster coverage, doublets, ambient RNA, zero-count cells,
67 and annotation compatibility before fitting. Preserve the original full-gene
68 count file. Retained-gene backups do not preserve filtered-out genes.
694. Resolve barcode prefixes and gene identifiers explicitly when combining
70 files. Do not silently strip library IDs, intersect away most cells, or make
71 duplicated biological IDs unique without understanding why they repeat.
73Read H5AD through AnnData. scVelo 0.3.4 has no `scv.read` or `scv.DataFrame`:
75```python
76import anndata as ad
77adata = ad.read_h5ad("velocity_counts.h5ad")
78```
80For legacy loom input use `ad.io.read_loom('counts.loom', X_name='spliced',
81sparse=True)`. AnnData 0.13 deprecates loom; convert to H5AD for further work.
82Its `layers[None]` aliases `X`: do not delete it or iterate all layers as if every
83key were a string. The helper accesses only named velocity layers. Native loom
84reading and exact, reordered metadata alignment are covered by the tests;
85AnnData 0.13 `write_loom` is not used.

Input contract

  1. Obtain spliced and unspliced counts from a velocity-aware quantifier. Keep quantifier/version, reference annotation, counting mode, sample IDs and cell barcode mapping. Upstream quantification is outside the bundled script.
  2. Supply cells by genes matrices in adata.layers['spliced'] and adata.layers['unspliced'], with identical cell/gene ordering and unique IDs. Counts must be finite and nonnegative; fractional count estimates are allowed. Never use logged, scaled, residualized, or batch-corrected values as counts. Numerical inspection alone cannot establish that data are raw.
  3. Inspect per-library/cluster coverage, doublets, ambient RNA, zero-count cells, and annotation compatibility before fitting. Preserve the original full-gene count file. Retained-gene backups do not preserve filtered-out genes.
  4. Resolve barcode prefixes and gene identifiers explicitly when combining files. Do not silently strip library IDs, intersect away most cells, or make duplicated biological IDs unique without understanding why they repeat.

Read H5AD through AnnData. scVelo 0.3.4 has no scv.read or scv.DataFrame:

import anndata as ad
adata = ad.read_h5ad("velocity_counts.h5ad")

For legacy loom input use ad.io.read_loom('counts.loom', X_name='spliced', sparse=True). AnnData 0.13 deprecates loom; convert to H5AD for further work. Its layers[None] aliases X: do not delete it or iterate all layers as if every key were a string. The helper accesses only named velocity layers. Native loom reading and exact, reordered metadata alignment are covered by the tests; AnnData 0.13 write_loom is not used.

Raw lines 56 to 86
87## Run the maintained workflow
89The [bundled script](scripts/rna_velocity_workflow.py) accepts local files and
90never downloads a demonstration dataset on startup:
92```bash
93MPLBACKEND=Agg .venv-velocity/bin/python scripts/rna_velocity_workflow.py \
94 velocity_counts.h5ad --mode dynamical --groupby clusters \
95 --n-top-genes 2000 --n-neighbors 30 --n-jobs 1 --output-dir velocity_results
97# Import a loom and align its raw counts to an existing annotation file:
98MPLBACKEND=Agg .venv-velocity/bin/python scripts/rna_velocity_workflow.py \
99 counts.loom --processed-h5ad annotated.h5ad --groupby clusters \
100 --mode deterministic --output-dir velocity_check
101```
103Run these from the skill directory. Input filenames and biological annotations
104are illustrative; the same CLI and functions are tested on small generated
105kinetic fixtures. Omit `--groupby` when annotations are unavailable. Use
106`--no-plots` for analysis without computing UMAP.
108The helper performs these steps:
1101. Validate layers, IDs, model compatibility and grouping; refuse previously
111 generated moments/velocity so preprocessing cannot silently run twice.
1122. Back up retained raw layers as `spliced_counts` and `unspliced_counts` and
113 rebuild `X` from raw spliced counts. Filter genes and normalize `X` and the
114 two count layers on a linear scale.
1153. Apply `sc.pp.log1p` **only to X**, select highly variable genes with Scanpy,
116 and rebuild PCA and neighbors after subsetting. Reusing stale PCA/neighbors
117 from an unrelated feature set can silently change the velocity model.
1184. Compute `Ms` and `Mu` with `scv.pp.moments(adata, n_neighbors=None)` from the
119 explicit Scanpy graph. Moments are dense: budget memory for multiple
120 cells-by-genes arrays, not only the sparse input.
1215. For dynamical mode, call `recover_dynamics(var_names='all')` on the selected
122 genes before `velocity(mode='dynamical')`; otherwise fit the selected model.
123 Require usable genes and a nonempty velocity graph.
1246. Compute velocity coherence and velocity pseudotime; add latent time only for
125 dynamical fits. Rank velocity-associated genes only with at least two groups
126 and at least two cells per group. Record/warn about nonfinite or constant
127 time/coherence outputs in `uns['velocity_workflow']['diagnostics']`; a
128 constant pseudotime does not support a trajectory ordering. Ranking is exploratory.
1297. For plots, recompute UMAP from the rebuilt graph, project velocities, save
130 PNGs directly to the requested directory, then save H5AD with package versions
131 and selected parameters. Existing labels remain annotations, not validated
132 cell identities.
134The Python function mutates its argument in place; pass `adata.copy()` to keep
135the original object. The CLI writes to `output_dir/adata_velocity.h5ad`.
136Version 2 changes the former helper's behavior deliberately: raw layer geometry
137is rebuilt, missing requested labels are errors, and plots/parallelism can be
138controlled explicitly. Runtime depends on cells, genes and fit difficulty;
139there is no universal 10–30 minute expectation.

Run the maintained workflow

The bundled script accepts local files and never downloads a demonstration dataset on startup:

MPLBACKEND=Agg .venv-velocity/bin/python scripts/rna_velocity_workflow.py \
  velocity_counts.h5ad --mode dynamical --groupby clusters \
  --n-top-genes 2000 --n-neighbors 30 --n-jobs 1 --output-dir velocity_results

# Import a loom and align its raw counts to an existing annotation file:
MPLBACKEND=Agg .venv-velocity/bin/python scripts/rna_velocity_workflow.py \
  counts.loom --processed-h5ad annotated.h5ad --groupby clusters \
  --mode deterministic --output-dir velocity_check

Run these from the skill directory. Input filenames and biological annotations are illustrative; the same CLI and functions are tested on small generated kinetic fixtures. Omit --groupby when annotations are unavailable. Use --no-plots for analysis without computing UMAP.

The helper performs these steps:

  1. Validate layers, IDs, model compatibility and grouping; refuse previously generated moments/velocity so preprocessing cannot silently run twice.
  2. Back up retained raw layers as spliced_counts and unspliced_counts and rebuild X from raw spliced counts. Filter genes and normalize X and the two count layers on a linear scale.
  3. Apply sc.pp.log1p only to X, select highly variable genes with Scanpy, and rebuild PCA and neighbors after subsetting. Reusing stale PCA/neighbors from an unrelated feature set can silently change the velocity model.
  4. Compute Ms and Mu with scv.pp.moments(adata, n_neighbors=None) from the explicit Scanpy graph. Moments are dense: budget memory for multiple cells-by-genes arrays, not only the sparse input.
  5. For dynamical mode, call recover_dynamics(var_names='all') on the selected genes before velocity(mode='dynamical'); otherwise fit the selected model. Require usable genes and a nonempty velocity graph.
  6. Compute velocity coherence and velocity pseudotime; add latent time only for dynamical fits. Rank velocity-associated genes only with at least two groups and at least two cells per group. Record/warn about nonfinite or constant time/coherence outputs in uns['velocity_workflow']['diagnostics']; a constant pseudotime does not support a trajectory ordering. Ranking is exploratory.
  7. For plots, recompute UMAP from the rebuilt graph, project velocities, save PNGs directly to the requested directory, then save H5AD with package versions and selected parameters. Existing labels remain annotations, not validated cell identities.

The Python function mutates its argument in place; pass adata.copy() to keep the original object. The CLI writes to output_dir/adata_velocity.h5ad. Version 2 changes the former helper's behavior deliberately: raw layer geometry is rebuilt, missing requested labels are errors, and plots/parallelism can be controlled explicitly. Runtime depends on cells, genes and fit difficulty; there is no universal 10–30 minute expectation.

Raw lines 87 to 140
Runs code bin/python :93
Runs code scripts/rna_velocity_workflow.py :93
141## Inspect the evidence before interpretation
143After a dynamical run (dataset-specific gene selection is illustrative):
145```python
146import pandas as pd
147import scvelo as scv
149# Candidate kinetic genes, not experimentally established drivers.
150candidates = adata.var['fit_likelihood'].dropna().nlargest(6).index.tolist()
151scv.pl.velocity(adata, var_names=candidates, basis='umap', show=False)
153# This ranks group-associated velocities, not causal influence or condition DE.
154scv.tl.rank_velocity_genes(adata, groupby='clusters', min_corr=0.3)
155ranked = pd.DataFrame(adata.uns['rank_velocity_genes']['names'])
157scv.tl.velocity_confidence(adata)
158scv.pl.scatter(adata, color=['velocity_length', 'velocity_confidence'], show=False)
159```
161Inspect spliced-versus-unspliced phase portraits, coverage across induction and
162repression, fitted parameters, failed/NaN fits, and branch-specific kinetics.
163Check sensitivity to gene set, neighbors, subsampling and model assumptions.
164Use time-course labels, perturbations, lineage tracing or labeling experiments
165as independent directional evidence where available. A plausible UMAP alone
166cannot validate a fit, and tuning until arrows match a desired story is not a
167validation strategy.
169There is no general minimum of 2,000 cells, universal unspliced fraction, or
170rule that root cells must have the highest unspliced/spliced ratio. Coverage of
171relevant kinetic states and measurement quality matter. Negative velocity can
172represent repression or model misspecification; it is not by itself evidence
173that layers were swapped.

Inspect the evidence before interpretation

After a dynamical run (dataset-specific gene selection is illustrative):

import pandas as pd
import scvelo as scv

# Candidate kinetic genes, not experimentally established drivers.
candidates = adata.var['fit_likelihood'].dropna().nlargest(6).index.tolist()
scv.pl.velocity(adata, var_names=candidates, basis='umap', show=False)

# This ranks group-associated velocities, not causal influence or condition DE.
scv.tl.rank_velocity_genes(adata, groupby='clusters', min_corr=0.3)
ranked = pd.DataFrame(adata.uns['rank_velocity_genes']['names'])

scv.tl.velocity_confidence(adata)
scv.pl.scatter(adata, color=['velocity_length', 'velocity_confidence'], show=False)

Inspect spliced-versus-unspliced phase portraits, coverage across induction and repression, fitted parameters, failed/NaN fits, and branch-specific kinetics. Check sensitivity to gene set, neighbors, subsampling and model assumptions. Use time-course labels, perturbations, lineage tracing or labeling experiments as independent directional evidence where available. A plausible UMAP alone cannot validate a fit, and tuning until arrows match a desired story is not a validation strategy.

There is no general minimum of 2,000 cells, universal unspliced fraction, or rule that root cells must have the highest unspliced/spliced ratio. Coverage of relevant kinetic states and measurement quality matter. Negative velocity can represent repression or model misspecification; it is not by itself evidence that layers were swapped.

Raw lines 141 to 174
175## Output interpretation
177| Field | Interpretation |
178| --- | --- |
179| `layers['velocity']` | Model-estimated derivative in processed gene-expression space |
180| `var['velocity_genes']` | Genes selected for the velocity graph; distinct from all HVGs |
181| `layers['Ms']`, `layers['Mu']` | Neighbor-averaged linear-scale spliced/unspliced expression |
182| `layers['fit_t']` | Gene-specific fitted time coordinates, dynamical model only |
183| `var['fit_alpha/beta/gamma']` | Fitted rates on the model's inferred scale; not calibrated physical rates |
184| `var['fit_likelihood']` | Relative model-fit diagnostic; not a posterior probability of biological truth |
185| `uns['velocity_graph']` | Sparse positive cosine correlations for candidate transitions; not row-stochastic |
186| `obsm['velocity_umap']` | Projected vectors, only after embedding computation/plotting |
187| `obs['velocity_pseudotime']` | Graph-based relative ordering |
188| `obs['latent_time']` | Coupled dynamical ordering; normally scaled 0–1, not elapsed hours |
189| `obs['velocity_length']` | Processed-space vector magnitude; not physical cell speed |
190| `obs['velocity_confidence']` | Neighbor velocity coherence, not calibrated uncertainty |
192For fate probabilities use an explicitly normalized transition kernel and a
193validated terminal-state definition; see the optional CellRank example in the
194reference. PAGA requires optional igraph and compatible Scanpy internals; it is
195not part of the tested core workflow or a substitute for fate inference.

Output interpretation

FieldInterpretation
layers['velocity']Model-estimated derivative in processed gene-expression space
var['velocity_genes']Genes selected for the velocity graph; distinct from all HVGs
layers['Ms'], layers['Mu']Neighbor-averaged linear-scale spliced/unspliced expression
layers['fit_t']Gene-specific fitted time coordinates, dynamical model only
var['fit_alpha/beta/gamma']Fitted rates on the model's inferred scale; not calibrated physical rates
var['fit_likelihood']Relative model-fit diagnostic; not a posterior probability of biological truth
uns['velocity_graph']Sparse positive cosine correlations for candidate transitions; not row-stochastic
obsm['velocity_umap']Projected vectors, only after embedding computation/plotting
obs['velocity_pseudotime']Graph-based relative ordering
obs['latent_time']Coupled dynamical ordering; normally scaled 0–1, not elapsed hours
obs['velocity_length']Processed-space vector magnitude; not physical cell speed
obs['velocity_confidence']Neighbor velocity coherence, not calibrated uncertainty

For fate probabilities use an explicitly normalized transition kernel and a validated terminal-state definition; see the optional CellRank example in the reference. PAGA requires optional igraph and compatible Scanpy internals; it is not part of the tested core workflow or a substitute for fate inference.

Raw lines 175 to 196
197## Troubleshooting
199| Problem | Action |
200| --- | --- |
201| Missing or mismatched layers | Revisit quantification and explicit ID alignment; X cannot substitute for unspliced counts |
202| Very few velocity genes | Inspect depth, state coverage and phase portraits before altering thresholds |
203| Smooth but implausible arrows | Check model assumptions, batch geometry and individual genes; compare independent evidence |
204| Nonfinite fit or empty graph | Stop interpretation and investigate degenerate features/coverage |
205| Excessive memory or fitting time | Use a justified gene set and `n_jobs=1`; account for dense moments and fit arrays |
206| Stochastic failure on NumPy 2 | Use a separately validated compatible legacy stack or explicitly reconsider the model |
207| pandas `unique` error in fit/plot | Use the tested pandas 2.3.3 pin |

Troubleshooting

ProblemAction
Missing or mismatched layersRevisit quantification and explicit ID alignment; X cannot substitute for unspliced counts
Very few velocity genesInspect depth, state coverage and phase portraits before altering thresholds
Smooth but implausible arrowsCheck model assumptions, batch geometry and individual genes; compare independent evidence
Nonfinite fit or empty graphStop interpretation and investigate degenerate features/coverage
Excessive memory or fitting timeUse a justified gene set and n_jobs=1; account for dense moments and fit arrays
Stochastic failure on NumPy 2Use a separately validated compatible legacy stack or explicitly reconsider the model
pandas unique error in fit/plotUse the tested pandas 2.3.3 pin
Raw lines 197 to 208
209## Sources and verification scope
211Reviewed 2026-10-01 against the [released source](https://github.com/theislab/scvelo/tree/v0.3.4),
212[API](https://scvelo.readthedocs.io/en/latest/api.html),
213[kinetic-model caveats](https://scvelo.readthedocs.io/en/latest/perspectives/Perspectives.html),
214and [AnnData loom reader](https://anndata.readthedocs.io/en/stable/generated/anndata.io.read_loom.html).
215The main paper is [Bergen et al., 2020](https://doi.org/10.1038/s41587-020-0591-3).
216Synthetic execution tests verify software contracts and file/figure production;
217they do not establish biological accuracy, identifiability or dataset-specific
218parameter recovery. Optional CellRank/PAGA analyses remain source-reviewed,
219illustrative extensions. No authenticated remote service is used.

Sources and verification scope

Reviewed 2026-10-01 against the released source, API, kinetic-model caveats, and AnnData loom reader. The main paper is Bergen et al., 2020. Synthetic execution tests verify software contracts and file/figure production; they do not establish biological accuracy, identifiability or dataset-specific parameter recovery. Optional CellRank/PAGA analyses remain source-reviewed, illustrative extensions. No authenticated remote service is used.

Raw lines 209 to 219

From K-Dense-AI/scientific-agent-skills at 5f2bc03, 2026-10-26. Not read by a model yet. Summary and techniques by Claude Opus 5.5 on 2026-11-06.