{"slug":"alterlab-scvelo","title":"alterlab-scvelo","summary":"Run RNA velocity analysis with scVelo on single-cell RNA-seq data — estimate cell-state transitions from spliced/unspliced mRNA dynamics, infer trajectory direction, compute latent time, and identify driver genes. Use when adding directionality to trajectories or studying differe","platform":"Claude","tags":[],"authorName":"LLM Mart","authorSlug":"llm-mart","score":0,"source":"github","price":null,"verified":false,"createdAt":"2026-09-23T18:56:57.821279Z","repo":{"url":"https://github.com/AlterLab-IEU/AlterLab-Academic-Skills","stars":68,"forks":13,"license":"MIT","updatedAt":"2026-09-23T13:42:59Z"},"bodyHtml":"<hr>\n<h2>name: alterlab-scvelo\ndescription: Run RNA velocity analysis with scVelo on single-cell RNA-seq data — estimate cell-state transitions from spliced/unspliced mRNA dynamics, infer trajectory direction, compute latent time, and identify driver genes. Use when adding directionality to trajectories or studying differentiation dynamics from spliced/unspliced layers (velocyto/STARsolo output); for the general QC, clustering, UMAP, and differential-expression analysis pipeline prefer alterlab-scanpy instead, and for .h5ad data-structure I/O and layer wrangling prefer alterlab-anndata instead. Part of the AlterLab Academic Skills suite.\nlicense: MIT\nallowed-tools: Read Write Edit Bash(python:<em>) Bash(uv:</em>)\ncompatibility: \"Self-contained — runs under <code>uv run python</code> with the skill's Python package installed; no API key or account required. scVelo 0.3.4 (released 2026-02) is the current and effectively final upstream release; it is minimally maintained, so pin it and validate results rather than expecting fixes.\"\nmetadata:\nskill-author: AlterLab\nversion: \"1.1.0\"\nlast_updated: \"2026-09-23\"</h2>\n<h1>scVelo — RNA Velocity Analysis</h1>\n<h2>Overview</h2>\n<p>scVelo is the leading Python package for RNA velocity analysis in single-cell RNA-seq data. It infers cell state transitions by modeling the kinetics of mRNA splicing — using the ratio of unspliced (pre-mRNA) to spliced (mature mRNA) abundances to determine whether a gene is being upregulated or downregulated in each cell. This allows reconstruction of developmental trajectories and identification of cell fate decisions without requiring time-course data.</p>\n<p><strong>Installation:</strong> <code>uv pip install \"scvelo==0.3.4\"</code> — 0.3.4 (uploaded 2026-02-24) is the\ncurrent release and the package is now only minimally maintained: its published release\nnotes stop at 0.2.5, upstream CI still targets Python 3.9/3.10, and its dependency\ndeclarations (<code>numpy&gt;=1.17</code>, <code>scanpy&gt;=1.5</code>, <code>anndata&gt;=0.7.5</code>) have no upper bounds, so pip\nwill happily build an environment scVelo was never tested against.</p>\n<p>Practical consequence: install it in its own environment with a pinned NumPy rather than\ninto a current scverse stack. If you hit dtype or scalar-assignment errors under NumPy 2 —\nmost often in <code>mode=\"stochastic\"</code>, whose second-moment regression assigns a one-element\nleast-squares result to a scalar — pin <code>numpy&lt;2</code> (e.g. <code>numpy==1.26.4</code>) and re-run. Check\nvelocity results for silent breakage rather than assuming they are fine: compare the\nstochastic and dynamical modes, and confirm the streamlines agree with known biology.</p>\n<p><strong>Key resources:</strong></p>\n<ul>\n<li>Documentation: <a href=\"https://scvelo.readthedocs.io/\">https://scvelo.readthedocs.io/</a></li>\n<li>GitHub: <a href=\"https://github.com/theislab/scvelo\">https://github.com/theislab/scvelo</a></li>\n<li>Paper: Bergen et al. (2020) Nature Biotechnology. PMID: 32747759</li>\n</ul>\n<h2>When to Use This Skill</h2>\n<p>Use scVelo when:</p>\n<ul>\n<li><strong>Trajectory inference from snapshot data</strong>: Determine which direction cells are differentiating</li>\n<li><strong>Cell fate prediction</strong>: Identify progenitor cells and their downstream fates</li>\n<li><strong>Driver gene identification</strong>: Find genes whose dynamics best explain observed trajectories</li>\n<li><strong>Developmental biology</strong>: Model hematopoiesis, neurogenesis, epithelial-to-mesenchymal transitions</li>\n<li><strong>Latent time estimation</strong>: Order cells along a pseudotime derived from splicing dynamics</li>\n<li><strong>Complement to Scanpy</strong>: Add directional information to UMAP embeddings</li>\n</ul>\n<h3>Does NOT Trigger</h3>\n<table>\n<thead>\n<tr>\n<th>Scenario</th>\n<th>Use Instead</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>QC, normalization, PCA/UMAP, Leiden clustering, marker genes (no velocity)</td>\n<td><code>alterlab-scanpy</code></td>\n</tr>\n<tr>\n<td>Reading/wrangling the <code>.h5ad</code> object, layers, and concatenation</td>\n<td><code>alterlab-anndata</code></td>\n</tr>\n<tr>\n<td>Probabilistic integration / batch correction / model-based DE (scVI, scANVI, veloVI)</td>\n<td><code>alterlab-scvi-tools</code></td>\n</tr>\n<tr>\n<td>Producing the spliced/unspliced count layers from FASTQ in the first place</td>\n<td><code>alterlab-rnaseq-quant</code></td>\n</tr>\n<tr>\n<td>Inferring a gene-regulatory network from expression</td>\n<td><code>alterlab-arboreto</code></td>\n</tr>\n</tbody>\n</table>\n<h2>Prerequisites</h2>\n<p>scVelo requires count matrices for both <strong>unspliced</strong> and <strong>spliced</strong> RNA. These are generated by:</p>\n<ol>\n<li><strong>STARsolo</strong> or <strong>kallisto|bustools</strong> with <code>lamanno</code> mode</li>\n<li><strong>velocyto</strong> CLI: <code>velocyto run10x</code> / <code>velocyto run</code></li>\n<li><strong>alevin-fry</strong> / <strong>simpleaf</strong> with spliced/unspliced output</li>\n</ol>\n<p>Data is stored in an <code>AnnData</code> object with <code>layers[\"spliced\"]</code> and <code>layers[\"unspliced\"]</code>.</p>\n<h2>Standard RNA Velocity Workflow</h2>\n<h3>1. Setup and Data Loading</h3>\n<pre><code>import scvelo as scv\nimport scanpy as sc\nimport numpy as np\nimport matplotlib.pyplot as plt\n\n# Configure settings\nscv.settings.verbosity = 3       # Show computation steps\nscv.settings.presenter_view = True\nscv.settings.set_figure_params('scvelo')\n\n# Load data (AnnData with spliced/unspliced layers)\n# Option A: Load from loom (velocyto output)\nadata = scv.read(\"cellranger_output.loom\", cache=True)\n\n# Option B: Merge velocyto loom with Scanpy-processed AnnData\nadata_processed = sc.read_h5ad(\"processed.h5ad\")  # Has UMAP, clusters\nadata_velocity = scv.read(\"velocyto.loom\")\nadata = scv.utils.merge(adata_processed, adata_velocity)\n\n# Verify layers\nprint(adata)\n# obs × var: N × G\n# layers: 'spliced', 'unspliced' (required)\n# obsm['X_umap'] (required for visualization)\n</code></pre>\n<h3>2. Preprocessing</h3>\n<pre><code># Filter and normalize (follows Scanpy conventions)\nscv.pp.filter_and_normalize(\n    adata,\n    min_shared_counts=20,   # Minimum counts in spliced+unspliced\n    n_top_genes=2000        # Top highly variable genes\n)\n\n# Compute first and second order moments (means and variances).\n# scv.pp.moments runs PCA + a kNN graph internally if they're absent, so\n# you do NOT need a separate sc.pp.neighbors call here. Calling moments with\n# n_pcs/n_neighbors and ALSO running sc.pp.neighbors first just recomputes the\n# graph with possibly mismatched params — let moments own it on a fresh object.\nscv.pp.moments(\n    adata,\n    n_pcs=30,\n    n_neighbors=30\n)\n</code></pre>\n<h3>3. Velocity Estimation — Stochastic Model</h3>\n<p>The stochastic model is fast and suitable for exploratory analysis:</p>\n<pre><code># Stochastic velocity (faster, less accurate)\nscv.tl.velocity(adata, mode='stochastic')\nscv.tl.velocity_graph(adata)\n\n# Visualize\nscv.pl.velocity_embedding_stream(\n    adata,\n    basis='umap',\n    color='leiden',\n    title=\"RNA Velocity (Stochastic)\"\n)\n</code></pre>\n<h3>4. Velocity Estimation — Dynamical Model (Recommended)</h3>\n<p>The dynamical model fits the full splicing kinetics and is more accurate:</p>\n<pre><code># Recover dynamics (computationally intensive; ~10-30 min for 10K cells)\nscv.tl.recover_dynamics(adata, n_jobs=4)\n\n# Compute velocity from dynamical model\nscv.tl.velocity(adata, mode='dynamical')\nscv.tl.velocity_graph(adata)\n</code></pre>\n<h3>5. Latent Time</h3>\n<p>The dynamical model enables computation of a shared latent time (pseudotime):</p>\n<pre><code># Compute latent time\nscv.tl.latent_time(adata)\n\n# Visualize latent time on UMAP\nscv.pl.scatter(\n    adata,\n    color='latent_time',\n    color_map='gnuplot',\n    size=80,\n    title='Latent time'\n)\n\n# Identify top genes ordered by latent time\ntop_genes = adata.var['fit_likelihood'].sort_values(ascending=False).index[:300]\nscv.pl.heatmap(\n    adata,\n    var_names=top_genes,\n    sortby='latent_time',\n    col_color='leiden',\n    n_convolve=100\n)\n</code></pre>\n<h3>6. Driver Gene Analysis</h3>\n<pre><code># Identify genes with highest velocity fit\nscv.tl.rank_velocity_genes(adata, groupby='leiden', min_corr=0.3)\ndf = scv.DataFrame(adata.uns['rank_velocity_genes']['names'])\nprint(df.head(10))\n\n# Speed and coherence\nscv.tl.velocity_confidence(adata)\nscv.pl.scatter(\n    adata,\n    c=['velocity_length', 'velocity_confidence'],\n    cmap='coolwarm',\n    perc=[5, 95]\n)\n\n# Phase portraits for specific genes\nscv.pl.velocity(adata, ['Cpe', 'Gnao1', 'Ins2'],\n               ncols=3, figsize=(16, 4))\n</code></pre>\n<h3>7. Velocity Arrows and Pseudotime</h3>\n<pre><code># Arrow plot on UMAP\nscv.pl.velocity_embedding(\n    adata,\n    arrow_length=3,\n    arrow_size=2,\n    color='leiden',\n    basis='umap'\n)\n\n# Stream plot (cleaner visualization)\nscv.pl.velocity_embedding_stream(\n    adata,\n    basis='umap',\n    color='leiden',\n    smooth=0.8,\n    min_mass=4\n)\n\n# Velocity pseudotime (alternative to latent time)\nscv.tl.velocity_pseudotime(adata)\nscv.pl.scatter(adata, color='velocity_pseudotime', cmap='gnuplot')\n</code></pre>\n<h3>8. PAGA Trajectory Graph</h3>\n<pre><code># PAGA graph with velocity-informed transitions\nscv.tl.paga(adata, groups='leiden')\ndf = scv.get_df(adata, 'paga/transitions_confidence', precision=2).T\ndf.style.background_gradient(cmap='Blues').format('{:.2g}')\n\n# Plot PAGA with velocity\nscv.pl.paga(\n    adata,\n    basis='umap',\n    size=50,\n    alpha=0.1,\n    min_edge_width=2,\n    node_size_scale=1.5\n)\n</code></pre>\n<h2>Complete Workflow Script</h2>\n<pre><code>import scvelo as scv\nimport scanpy as sc\n\ndef run_rna_velocity(adata, n_top_genes=2000, mode='dynamical', n_jobs=4):\n    \"\"\"\n    Complete RNA velocity workflow.\n\n    Args:\n        adata: AnnData with 'spliced' and 'unspliced' layers, UMAP in obsm\n        n_top_genes: Number of top HVGs for velocity\n        mode: 'stochastic' (fast) or 'dynamical' (accurate)\n        n_jobs: Parallel jobs for dynamical model\n\n    Returns:\n        Processed AnnData with velocity information\n    \"\"\"\n    scv.settings.verbosity = 2\n\n    # 1. Preprocessing\n    scv.pp.filter_and_normalize(adata, min_shared_counts=20, n_top_genes=n_top_genes)\n\n    if 'neighbors' not in adata.uns:\n        sc.pp.neighbors(adata, n_neighbors=30)\n\n    scv.pp.moments(adata, n_pcs=30, n_neighbors=30)\n\n    # 2. Velocity estimation\n    if mode == 'dynamical':\n        scv.tl.recover_dynamics(adata, n_jobs=n_jobs)\n\n    scv.tl.velocity(adata, mode=mode)\n    scv.tl.velocity_graph(adata)\n\n    # 3. Downstream analyses\n    if mode == 'dynamical':\n        scv.tl.latent_time(adata)\n        scv.tl.rank_velocity_genes(adata, groupby='leiden', min_corr=0.3)\n\n    scv.tl.velocity_confidence(adata)\n    scv.tl.velocity_pseudotime(adata)\n\n    return adata\n</code></pre>\n<h2>Key Output Fields in AnnData</h2>\n<p>After running the workflow, the following fields are added:</p>\n<table>\n<thead>\n<tr>\n<th>Location</th>\n<th>Key</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>adata.layers</code></td>\n<td><code>velocity</code></td>\n<td>RNA velocity per gene per cell</td>\n</tr>\n<tr>\n<td><code>adata.layers</code></td>\n<td><code>fit_t</code></td>\n<td>Fitted latent time per gene per cell</td>\n</tr>\n<tr>\n<td><code>adata.obsm</code></td>\n<td><code>velocity_umap</code></td>\n<td>2D velocity vectors on UMAP</td>\n</tr>\n<tr>\n<td><code>adata.obs</code></td>\n<td><code>velocity_pseudotime</code></td>\n<td>Pseudotime from velocity</td>\n</tr>\n<tr>\n<td><code>adata.obs</code></td>\n<td><code>latent_time</code></td>\n<td>Latent time from dynamical model</td>\n</tr>\n<tr>\n<td><code>adata.obs</code></td>\n<td><code>velocity_length</code></td>\n<td>Speed of each cell</td>\n</tr>\n<tr>\n<td><code>adata.obs</code></td>\n<td><code>velocity_confidence</code></td>\n<td>Confidence score per cell</td>\n</tr>\n<tr>\n<td><code>adata.var</code></td>\n<td><code>fit_likelihood</code></td>\n<td>Gene-level model fit quality</td>\n</tr>\n<tr>\n<td><code>adata.var</code></td>\n<td><code>fit_alpha</code></td>\n<td>Transcription rate</td>\n</tr>\n<tr>\n<td><code>adata.var</code></td>\n<td><code>fit_beta</code></td>\n<td>Splicing rate</td>\n</tr>\n<tr>\n<td><code>adata.var</code></td>\n<td><code>fit_gamma</code></td>\n<td>Degradation rate</td>\n</tr>\n<tr>\n<td><code>adata.uns</code></td>\n<td><code>velocity_graph</code></td>\n<td>Cell-cell transition probability matrix</td>\n</tr>\n</tbody>\n</table>\n<h2>Velocity Models Comparison</h2>\n<table>\n<thead>\n<tr>\n<th>Model</th>\n<th>Speed</th>\n<th>Accuracy</th>\n<th>When to Use</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>stochastic</code></td>\n<td>Fast</td>\n<td>Moderate</td>\n<td>Exploratory; large datasets</td>\n</tr>\n<tr>\n<td><code>deterministic</code></td>\n<td>Medium</td>\n<td>Moderate</td>\n<td>Simple linear kinetics</td>\n</tr>\n<tr>\n<td><code>dynamical</code></td>\n<td>Slow</td>\n<td>High</td>\n<td>Publication-quality; identifies driver genes</td>\n</tr>\n</tbody>\n</table>\n<h2>Best Practices</h2>\n<ul>\n<li><strong>Start with stochastic mode</strong> for exploration; switch to dynamical for final analysis</li>\n<li><strong>Need good coverage of unspliced reads</strong>: Short reads (&lt; 100 bp) may miss intron coverage</li>\n<li><strong>Minimum 2,000 cells</strong>: RNA velocity is noisy with fewer cells</li>\n<li><strong>Velocity should be coherent</strong>: Arrows should follow known biology; randomness indicates issues</li>\n<li><strong>k-NN bandwidth matters</strong>: Too few neighbors → noisy velocity; too many → oversmoothed</li>\n<li><strong>Sanity check</strong>: Root cells (progenitors) should have high unspliced/spliced ratios for marker genes</li>\n<li><strong>Dynamical model requires distinct kinetic states</strong>: Works best for clear differentiation processes</li>\n</ul>\n<h2>Troubleshooting</h2>\n<table>\n<thead>\n<tr>\n<th>Problem</th>\n<th>Solution</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Missing unspliced layer</td>\n<td>Re-run velocyto or use STARsolo with <code>--soloFeatures Gene Velocyto</code></td>\n</tr>\n<tr>\n<td>Very few velocity genes</td>\n<td>Lower <code>min_shared_counts</code>; check sequencing depth</td>\n</tr>\n<tr>\n<td>Random-looking arrows</td>\n<td>Try different <code>n_neighbors</code> or velocity model</td>\n</tr>\n<tr>\n<td>Memory error with dynamical</td>\n<td>Set <code>n_jobs=1</code>; reduce <code>n_top_genes</code></td>\n</tr>\n<tr>\n<td>Negative velocity everywhere</td>\n<td>Check that spliced/unspliced layers are not swapped</td>\n</tr>\n</tbody>\n</table>\n<h2>Additional Resources</h2>\n<ul>\n<li><strong>scVelo documentation</strong>: <a href=\"https://scvelo.readthedocs.io/\">https://scvelo.readthedocs.io/</a></li>\n<li><strong>Tutorial notebooks</strong>: <a href=\"https://scvelo.readthedocs.io/en/stable/VelocityBasics.html\">https://scvelo.readthedocs.io/en/stable/VelocityBasics.html</a></li>\n<li><strong>GitHub</strong>: <a href=\"https://github.com/theislab/scvelo\">https://github.com/theislab/scvelo</a></li>\n<li><strong>Paper</strong>: Bergen V et al. (2020) Nature Biotechnology. PMID: 32747759</li>\n<li><strong>velocyto</strong> (preprocessing): <a href=\"http://velocyto.org/\">http://velocyto.org/</a></li>\n<li><strong>CellRank 2</strong> (fate prediction; consumes a velocity kernel but also works without RNA\nvelocity, via pseudotime / real-time / metabolic-labelling kernels — the actively\nmaintained route for fate probabilities): <a href=\"https://cellrank.readthedocs.io/\">https://cellrank.readthedocs.io/</a></li>\n<li><strong>dynamo</strong> (metabolic labeling alternative): <a href=\"https://dynamo-release.readthedocs.io/\">https://dynamo-release.readthedocs.io/</a></li>\n</ul>\n","files":[{"path":"evals/evals.json","sizeBytes":4354,"isText":true},{"path":"references/velocity_models.md","sizeBytes":5010,"isText":true},{"path":"scripts/rna_velocity_workflow.py","sizeBytes":8166,"isText":true},{"path":"SKILL.md","sizeBytes":12924,"isText":true}],"reviewScore":null,"reviewSummary":null,"trust":{"provenance":"trusted-source-unreviewed","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow.","bodySource":null},"bodyLocked":false,"purchaseUrl":null,"sourceUrl":null,"report":{"provenance":"trusted-source-unreviewed","screen":{"ran":true,"outcome":"clean","suspicious":0,"notes":0,"hiddenCharacters":false},"virusScan":{"engine":"clamav","status":"clean","scannedAt":"2026-09-23T18:57:56.962528Z","sha256":"2450B924714C5DD44A9C160AE8F0F4DF5F0860302513C065E48478533EEFBDB5","sizeBytes":11896},"review":null,"source":{"repositoryUrl":"https://github.com/AlterLab-IEU/AlterLab-Academic-Skills","path":"skills/bioinformatics/alterlab-scvelo","license":"MIT","commit":"e4836c08a20da195a11f30f203a8cf23ec30aa95","subtreeSha":"33C5F7C91F0E69C4AF2560226BF2C9CDC4304961F89F66B7EB76D4056CF9BF59","lastSyncedAt":"2026-09-23T18:56:52.297238Z"},"reviewedAt":"2026-09-23T18:59:50.108Z","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow."},"install":[{"target":"skills-cli","command":"npx skills add https://github.com/AlterLab-IEU/AlterLab-Academic-Skills/tree/main/skills/bioinformatics/alterlab-scvelo"},{"target":"claude-code","command":"claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install alterlab-ieu-alterlab-academic-skills@llmmart"},{"target":"git","command":"git clone https://github.com/AlterLab-IEU/AlterLab-Academic-Skills.git"}]}