PluginBench
Skill
Pass
Audit score 90

scvelo

k-dense-ai/scientific-agent-skills

RNA velocity analysis for single-cell RNA-seq: infer cell state transitions and trajectories from splicing kinetics.

What is scvelo?

scVelo estimates RNA velocity by modeling unspliced/spliced mRNA dynamics in single-cell RNA-seq data, enabling trajectory inference and cell fate prediction without time-course experiments. Use it to reconstruct developmental trajectories, identify driver genes, compute latent time, and visualize directional cell state transitions in snapshot data.

  • Estimate RNA velocity from unspliced/spliced mRNA ratios using stochastic or dynamical models
  • Infer cell state transitions and developmental trajectories without requiring time-course data
  • Compute latent time (pseudotime) from splicing kinetics to order cells along trajectories
  • Identify driver genes whose dynamics best explain observed cell fate transitions
  • Rank velocity genes by confidence and correlation to reveal key regulators
  • Visualize velocity as arrows, streams, or phase portraits on UMAP/tSNE embeddings

How to install scvelo

npx skills add https://github.com/k-dense-ai/scientific-agent-skills --skill scvelo
Prerequisites
  • Python 3.10+ with scvelo (≥0.3.4), scanpy, and anndata installed
  • Count matrices for both unspliced and spliced RNA generated by STARsolo, kallisto|bustools (lamanno mode), velocyto, alevin-fry, or simpleaf
  • AnnData object with layers['spliced'] and layers['unspliced'] and obsm['X_umap'] or other 2D embedding
  • Preprocessed data with computed neighbors (sc.pp.neighbors) for moment calculation
Claude Code
Cursor
Windsurf
Cline

How to use scvelo

  1. 1.Install scVelo: uv pip install scvelo
  2. 2.Load AnnData with spliced/unspliced layers (from loom, h5ad, or merged sources)
  3. 3.Run preprocessing: scv.pp.filter_and_normalize(), sc.pp.log1p(), sc.pp.highly_variable_genes(), sc.pp.neighbors(), scv.pp.moments()
  4. 4.Choose velocity model: scv.tl.recover_dynamics() + scv.tl.velocity(mode='dynamical') for accuracy, or scv.tl.velocity(mode='stochastic') for speed
  5. 5.Compute velocity graph and downstream analyses: scv.tl.velocity_graph(), scv.tl.latent_time(), scv.tl.rank_velocity_genes(), scv.tl.velocity_confidence()
  6. 6.Visualize with scv.pl.velocity_embedding_stream(), scv.pl.scatter(), or scv.pl.velocity() for phase portraits

Use cases

Good for
  • Reconstruct hematopoiesis or neurogenesis trajectories from bone marrow or neural progenitor scRNA-seq
  • Predict cell fate decisions and identify progenitor cells in epithelial-to-mesenchymal transition studies
  • Discover genes driving differentiation by ranking velocity fit and correlation in specific cell clusters
  • Order cells along pseudotime in developmental datasets to model gene expression kinetics
  • Validate trajectory direction inferred from Scanpy/PAGA with velocity-informed confidence scores
Who it's for
  • Computational biologists analyzing developmental or differentiation scRNA-seq data
  • Bioinformaticians building trajectory inference pipelines with Scanpy
  • Researchers studying cell fate decisions and progenitor-to-progeny relationships
  • Single-cell genomics groups using STARsolo, kallisto|bustools, or velocyto for spliced/unspliced quantification

scvelo FAQ

What is the difference between stochastic and dynamical velocity models?

Stochastic mode is fast and suitable for exploratory analysis but less accurate. Dynamical mode fits full splicing kinetics (10–30 min for 10K cells) and enables latent time computation; it is more accurate and recommended for publication-quality results.

What data do I need to run scVelo?

You need count matrices for both unspliced (pre-mRNA) and spliced (mature mRNA) RNA, typically generated by STARsolo, kallisto|bustools with lamanno mode, velocyto, or alevin-fry. Data must be in an AnnData object with layers['spliced'], layers['unspliced'], and a 2D embedding (obsm['X_umap']).

Can I use scVelo with my existing Scanpy-processed data?

Yes. If your processed AnnData lacks spliced/unspliced layers, merge it with a velocyto loom file using scv.utils.merge(). Ensure your data has neighbors computed (sc.pp.neighbors) before calling scv.pp.moments().

What do latent_time and velocity_pseudotime represent?

latent_time (from dynamical model) is a shared pseudotime derived from splicing kinetics across the dataset. velocity_pseudotime is an alternative metric based on velocity direction. Both order cells along trajectories; latent_time is generally preferred when the dynamical model is used.

How do I identify driver genes in my trajectory?

Use scv.tl.rank_velocity_genes(groupby='leiden') to rank genes by velocity fit per cluster, and scv.tl.velocity_confidence() to compute speed and coherence. Visualize phase portraits with scv.pl.velocity() for specific genes to see splicing dynamics.

Full instructions (SKILL.md)

Source of truth, from k-dense-ai/scientific-agent-skills.


name: scvelo description: RNA velocity analysis with scVelo. Estimate cell state transitions from unspliced/spliced mRNA dynamics, infer trajectory directions, compute latent time, and identify driver genes in single-cell RNA-seq data. Complements Scanpy/scVI-tools for trajectory inference. license: BSD-3-Clause compatibility: Requires Python 3.10+ with scvelo, scanpy, and anndata. Verified against scvelo 0.3.4, whose dynamical model and pl.scatter need pandas<3 and whose stochastic estimator needs numpy<2; the deterministic estimator works on current releases. metadata: version: "1.2" skill-author: Kuan-lin Huang

scVelo — RNA Velocity Analysis

Overview

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.

Installation: uv pip install scvelo

Key resources:

When to Use This Skill

Use scVelo when:

  • Trajectory inference from snapshot data: Determine which direction cells are differentiating
  • Cell fate prediction: Identify progenitor cells and their downstream fates
  • Driver gene identification: Find genes whose dynamics best explain observed trajectories
  • Developmental biology: Model hematopoiesis, neurogenesis, epithelial-to-mesenchymal transitions
  • Latent time estimation: Order cells along a pseudotime derived from splicing dynamics
  • Complement to Scanpy: Add directional information to UMAP embeddings

Prerequisites

scVelo requires count matrices for both unspliced and spliced RNA. These are generated by:

  1. STARsolo or kallisto|bustools with lamanno mode
  2. velocyto CLI: velocyto run10x / velocyto run
  3. alevin-fry / simpleaf with spliced/unspliced output

Data is stored in an AnnData object with layers["spliced"] and layers["unspliced"].

Standard RNA Velocity Workflow

1. Setup and Data Loading

import scvelo as scv
import scanpy as sc
import numpy as np
import matplotlib.pyplot as plt

# Configure settings
scv.settings.verbosity = 3       # Show computation steps
scv.settings.presenter_view = True
scv.settings.set_figure_params('scvelo')

# Load data (AnnData with spliced/unspliced layers)
# Option A: Load from loom (velocyto output)
adata = scv.read("cellranger_output.loom", cache=True)

# Option B: Merge velocyto loom with Scanpy-processed AnnData
adata_processed = sc.read_h5ad("processed.h5ad")  # Has UMAP, clusters
adata_velocity = scv.read("velocyto.loom")
adata = scv.utils.merge(adata_processed, adata_velocity)

# Verify layers
print(adata)
# obs × var: N × G
# layers: 'spliced', 'unspliced' (required)
# obsm['X_umap'] (required for visualization)

2. Preprocessing

# Filter and normalize. As of scVelo 0.3, filter_and_normalize() only filters
# genes and normalizes per cell -- it no longer takes n_top_genes and no longer
# log-transforms, so the log step and HVG selection come from Scanpy.
scv.pp.filter_and_normalize(
    adata,
    min_shared_counts=20    # Minimum counts in spliced+unspliced
)
sc.pp.log1p(adata)
sc.pp.highly_variable_genes(adata, n_top_genes=2000, subset=True)

# Compute first and second order moments (means and variances)
# knn_connectivities must be computed first
sc.pp.neighbors(adata, n_neighbors=30, n_pcs=30)
scv.pp.moments(
    adata,
    n_pcs=30,
    n_neighbors=30
)

3. Velocity Estimation — Stochastic Model

The stochastic model is fast and suitable for exploratory analysis:

# Stochastic velocity (faster, less accurate)
scv.tl.velocity(adata, mode='stochastic')
scv.tl.velocity_graph(adata)

# Visualize
scv.pl.velocity_embedding_stream(
    adata,
    basis='umap',
    color='leiden',
    title="RNA Velocity (Stochastic)"
)

4. Velocity Estimation — Dynamical Model (Recommended)

The dynamical model fits the full splicing kinetics and is more accurate:

# Recover dynamics (computationally intensive; ~10-30 min for 10K cells)
scv.tl.recover_dynamics(adata, n_jobs=4)

# Compute velocity from dynamical model
scv.tl.velocity(adata, mode='dynamical')
scv.tl.velocity_graph(adata)

5. Latent Time

The dynamical model enables computation of a shared latent time (pseudotime):

# Compute latent time
scv.tl.latent_time(adata)

# Visualize latent time on UMAP
scv.pl.scatter(
    adata,
    color='latent_time',
    color_map='gnuplot',
    size=80,
    title='Latent time'
)

# Identify top genes ordered by latent time
top_genes = adata.var['fit_likelihood'].sort_values(ascending=False).index[:300]
scv.pl.heatmap(
    adata,
    var_names=top_genes,
    sortby='latent_time',
    col_color='leiden',
    n_convolve=100
)

6. Driver Gene Analysis

# Identify genes with highest velocity fit
scv.tl.rank_velocity_genes(adata, groupby='leiden', min_corr=0.3)
df = scv.DataFrame(adata.uns['rank_velocity_genes']['names'])
print(df.head(10))

# Speed and coherence
scv.tl.velocity_confidence(adata)
scv.pl.scatter(
    adata,
    c=['velocity_length', 'velocity_confidence'],
    cmap='coolwarm',
    perc=[5, 95]
)

# Phase portraits for specific genes
scv.pl.velocity(adata, ['Cpe', 'Gnao1', 'Ins2'],
               ncols=3, figsize=(16, 4))

7. Velocity Arrows and Pseudotime

# Arrow plot on UMAP
scv.pl.velocity_embedding(
    adata,
    arrow_length=3,
    arrow_size=2,
    color='leiden',
    basis='umap'
)

# Stream plot (cleaner visualization)
scv.pl.velocity_embedding_stream(
    adata,
    basis='umap',
    color='leiden',
    smooth=0.8,
    min_mass=4
)

# Velocity pseudotime (alternative to latent time)
scv.tl.velocity_pseudotime(adata)
scv.pl.scatter(adata, color='velocity_pseudotime', cmap='gnuplot')

8. PAGA Trajectory Graph

# PAGA graph with velocity-informed transitions
scv.tl.paga(adata, groups='leiden')
df = scv.get_df(adata, 'paga/transitions_confidence', precision=2).T
df.style.background_gradient(cmap='Blues').format('{:.2g}')

# Plot PAGA with velocity
scv.pl.paga(
    adata,
    basis='umap',
    size=50,
    alpha=0.1,
    min_edge_width=2,
    node_size_scale=1.5
)

Complete Workflow Script

import scvelo as scv
import scanpy as sc

def run_rna_velocity(adata, n_top_genes=2000, mode='dynamical', n_jobs=4):
    """
    Complete RNA velocity workflow.

    Args:
        adata: AnnData with 'spliced' and 'unspliced' layers, UMAP in obsm
        n_top_genes: Number of top HVGs for velocity
        mode: 'stochastic' (fast) or 'dynamical' (accurate)
        n_jobs: Parallel jobs for dynamical model

    Returns:
        Processed AnnData with velocity information
    """
    scv.settings.verbosity = 2

    # 1. Preprocessing (scVelo 0.3 dropped log/HVG from filter_and_normalize)
    scv.pp.filter_and_normalize(adata, min_shared_counts=20)
    sc.pp.log1p(adata)
    sc.pp.highly_variable_genes(adata, n_top_genes=n_top_genes, subset=True)

    if 'neighbors' not in adata.uns:
        sc.pp.neighbors(adata, n_neighbors=30)

    scv.pp.moments(adata, n_pcs=30, n_neighbors=30)

    # 2. Velocity estimation
    if mode == 'dynamical':
        scv.tl.recover_dynamics(adata, n_jobs=n_jobs)

    scv.tl.velocity(adata, mode=mode)
    scv.tl.velocity_graph(adata)

    # 3. Downstream analyses
    if mode == 'dynamical':
        scv.tl.latent_time(adata)
        scv.tl.rank_velocity_genes(adata, groupby='leiden', min_corr=0.3)

    scv.tl.velocity_confidence(adata)
    scv.tl.velocity_pseudotime(adata)

    return adata

Key Output Fields in AnnData

After running the workflow, the following fields are added:

LocationKeyDescription
adata.layersvelocityRNA velocity per gene per cell
adata.layersfit_tFitted latent time per gene per cell
adata.obsmvelocity_umap2D velocity vectors on UMAP
adata.obsvelocity_pseudotimePseudotime from velocity
adata.obslatent_timeLatent time from dynamical model
adata.obsvelocity_lengthSpeed of each cell
adata.obsvelocity_confidenceConfidence score per cell
adata.varfit_likelihoodGene-level model fit quality
adata.varfit_alphaTranscription rate
adata.varfit_betaSplicing rate
adata.varfit_gammaDegradation rate
adata.unsvelocity_graphCell-cell transition probability matrix

Velocity Models Comparison

ModelSpeedAccuracyWhen to Use
stochasticFastModerateExploratory; large datasets
deterministicMediumModerateSimple linear kinetics
dynamicalSlowHighPublication-quality; identifies driver genes

Best Practices

  • Start with stochastic mode for exploration; switch to dynamical for final analysis
  • Need good coverage of unspliced reads: Short reads (< 100 bp) may miss intron coverage
  • Minimum 2,000 cells: RNA velocity is noisy with fewer cells
  • Velocity should be coherent: Arrows should follow known biology; randomness indicates issues
  • k-NN bandwidth matters: Too few neighbors → noisy velocity; too many → oversmoothed
  • Sanity check: Root cells (progenitors) should have high unspliced/spliced ratios for marker genes
  • Dynamical model requires distinct kinetic states: Works best for clear differentiation processes

Troubleshooting

ProblemSolution
Missing unspliced layerRe-run velocyto or use STARsolo with --soloFeatures Gene Velocyto
Very few velocity genesLower min_shared_counts; check sequencing depth
Random-looking arrowsTry different n_neighbors or velocity model
Memory error with dynamicalSet n_jobs=1; reduce n_top_genes
Negative velocity everywhereCheck that spliced/unspliced layers are not swapped

Additional Resources