Skills › Knowledge Work Plugins › scvi-tools › rna_velocity_velovi.md
rna_velocity_velovi.md
410 lines · bio-research/skills/scvi-tools/references/rna_velocity_velovi.md at 2574e29
1# RNA Velocity with veloVI
3This reference covers RNA velocity analysis using veloVI, a deep learning approach that improves upon traditional velocity methods.
RNA Velocity with veloVI
This reference covers RNA velocity analysis using veloVI, a deep learning approach that improves upon traditional velocity methods.
Raw lines 1 to 45## Overview
7RNA velocity estimates the future state of cells by modeling:
8- **Unspliced RNA**: Newly transcribed, contains introns
9- **Spliced RNA**: Mature mRNA, introns removed
11The ratio of unspliced to spliced indicates whether a gene is being upregulated or downregulated.
Overview
RNA velocity estimates the future state of cells by modeling:
- Unspliced RNA: Newly transcribed, contains introns
- Spliced RNA: Mature mRNA, introns removed
The ratio of unspliced to spliced indicates whether a gene is being upregulated or downregulated.
Raw lines 5 to 1213## Why veloVI?
15Traditional methods (velocyto, scVelo) have limitations:
16- Assume steady-state or dynamical model
17- Sensitive to noise
18- Don't handle batch effects
20veloVI addresses these with:
21- Probabilistic modeling
22- Better uncertainty quantification
23- Integration with scVI framework
Why veloVI?
Traditional methods (velocyto, scVelo) have limitations:
- Assume steady-state or dynamical model
- Sensitive to noise
- Don't handle batch effects
veloVI addresses these with:
- Probabilistic modeling
- Better uncertainty quantification
- Integration with scVI framework
25## Prerequisites
27```python
28import scvi
29import scvelo as scv
30import scanpy as sc
31import numpy as np
33print(f"scvi-tools version: {scvi.__version__}")
34print(f"scvelo version: {scv.__version__}")
35```
Prerequisites
import scvi
import scvelo as scv
import scanpy as sc
import numpy as np
print(f"scvi-tools version: {scvi.__version__}")
print(f"scvelo version: {scv.__version__}")Raw lines 25 to 36Step 1: Generate Spliced/Unspliced Counts
Raw lines 37 to 3839### From BAM Files (velocyto)
41```bash
42# Run velocyto on Cell Ranger output
43velocyto run10x /path/to/cellranger_output /path/to/genes.gtf
45# Output: velocyto.loom file with spliced/unspliced layers
46```
From BAM Files (velocyto)
# Run velocyto on Cell Ranger output
velocyto run10x /path/to/cellranger_output /path/to/genes.gtf
# Output: velocyto.loom file with spliced/unspliced layersRaw lines 39 to 4748### From kb-python (kallisto|bustools)
50```bash
51# Faster alternative using kallisto
52kb count \
53 --workflow lamanno \
54 -i index.idx \
55 -g t2g.txt \
56 -c1 spliced_t2c.txt \
57 -c2 unspliced_t2c.txt \
58 -x 10xv3 \
59 -o output \
60 R1.fastq.gz R2.fastq.gz
61```
From kb-python (kallisto|bustools)
# Faster alternative using kallisto
kb count \
--workflow lamanno \
-i index.idx \
-g t2g.txt \
-c1 spliced_t2c.txt \
-c2 unspliced_t2c.txt \
-x 10xv3 \
-o output \
R1.fastq.gz R2.fastq.gzRaw lines 48 to 6263## Step 2: Load Velocity Data
65```python
66# Load loom file from velocyto
67adata = scv.read("velocyto_output.loom")
69# Or load from kb-python
70adata = sc.read_h5ad("adata.h5ad")
71# Spliced in adata.layers["spliced"]
72# Unspliced in adata.layers["unspliced"]
74# Check layers
75print("Available layers:", list(adata.layers.keys()))
76print(f"Spliced shape: {adata.layers['spliced'].shape}")
77print(f"Unspliced shape: {adata.layers['unspliced'].shape}")
78```
Step 2: Load Velocity Data
# Load loom file from velocyto
adata = scv.read("velocyto_output.loom")
# Or load from kb-python
adata = sc.read_h5ad("adata.h5ad")
# Spliced in adata.layers["spliced"]
# Unspliced in adata.layers["unspliced"]
# Check layers
print("Available layers:", list(adata.layers.keys()))
print(f"Spliced shape: {adata.layers['spliced'].shape}")
print(f"Unspliced shape: {adata.layers['unspliced'].shape}")Raw lines 63 to 7980### Merge with Existing AnnData
82```python
83# If you have separate loom and h5ad
84ldata = scv.read("velocyto.loom")
85adata = sc.read_h5ad("processed.h5ad")
87# Merge velocity data into processed adata
88adata = scv.utils.merge(adata, ldata)
89```
Merge with Existing AnnData
# If you have separate loom and h5ad
ldata = scv.read("velocyto.loom")
adata = sc.read_h5ad("processed.h5ad")
# Merge velocity data into processed adata
adata = scv.utils.merge(adata, ldata)Raw lines 80 to 9091## Step 3: Preprocessing for Velocity
93```python
94# Filter and normalize
95scv.pp.filter_and_normalize(
96 adata,
97 min_shared_counts=20,
98 n_top_genes=2000
99)
101# Compute moments (for scVelo comparison)
102scv.pp.moments(adata, n_pcs=30, n_neighbors=30)
103```
Step 3: Preprocessing for Velocity
# Filter and normalize
scv.pp.filter_and_normalize(
adata,
min_shared_counts=20,
n_top_genes=2000
)
# Compute moments (for scVelo comparison)
scv.pp.moments(adata, n_pcs=30, n_neighbors=30)Raw lines 91 to 104Step 4: Run veloVI
Raw lines 105 to 106107### Setup AnnData
109```python
110# Setup for veloVI
111scvi.model.VELOVI.setup_anndata(
112 adata,
113 spliced_layer="spliced",
114 unspliced_layer="unspliced"
115)
116```
Setup AnnData
# Setup for veloVI
scvi.model.VELOVI.setup_anndata(
adata,
spliced_layer="spliced",
unspliced_layer="unspliced"
)Raw lines 107 to 117118### Train Model
120```python
121# Create and train veloVI model
122vae = scvi.model.VELOVI(adata)
124vae.train(
125 max_epochs=500,
126 early_stopping=True,
127 batch_size=256
128)
130# Check training
131vae.history["elbo_train"].plot()
132```
Train Model
# Create and train veloVI model
vae = scvi.model.VELOVI(adata)
vae.train(
max_epochs=500,
early_stopping=True,
batch_size=256
)
# Check training
vae.history["elbo_train"].plot()Raw lines 118 to 133134### Get Velocity Estimates
136```python
137# Get latent time
138latent_time = vae.get_latent_time(n_samples=25)
139adata.obs["veloVI_latent_time"] = latent_time
141# Get velocity
142velocities = vae.get_velocity(n_samples=25)
143adata.layers["veloVI_velocity"] = velocities
145# Get expression states
146adata.layers["veloVI_expression"] = vae.get_expression_fit(n_samples=25)
147```
Get Velocity Estimates
# Get latent time
latent_time = vae.get_latent_time(n_samples=25)
adata.obs["veloVI_latent_time"] = latent_time
# Get velocity
velocities = vae.get_velocity(n_samples=25)
adata.layers["veloVI_velocity"] = velocities
# Get expression states
adata.layers["veloVI_expression"] = vae.get_expression_fit(n_samples=25)Raw lines 134 to 148Step 5: Visualize Velocity
Raw lines 149 to 150151### Velocity Streamlines
153```python
154# Compute velocity graph
155scv.tl.velocity_graph(adata, vkey="veloVI_velocity")
157# Plot streamlines on UMAP
158scv.pl.velocity_embedding_stream(
159 adata,
160 basis="umap",
161 vkey="veloVI_velocity",
162 color="cell_type"
163)
164```
Velocity Streamlines
# Compute velocity graph
scv.tl.velocity_graph(adata, vkey="veloVI_velocity")
# Plot streamlines on UMAP
scv.pl.velocity_embedding_stream(
adata,
basis="umap",
vkey="veloVI_velocity",
color="cell_type"
)Raw lines 151 to 165166### Velocity Arrows
168```python
169# Individual cell arrows
170scv.pl.velocity_embedding(
171 adata,
172 basis="umap",
173 vkey="veloVI_velocity",
174 arrow_length=3,
175 arrow_size=2,
176 color="cell_type"
177)
178```
Velocity Arrows
# Individual cell arrows
scv.pl.velocity_embedding(
adata,
basis="umap",
vkey="veloVI_velocity",
arrow_length=3,
arrow_size=2,
color="cell_type"
)Raw lines 166 to 179180### Latent Time
182```python
183# Plot latent time (pseudotime from velocity)
184sc.pl.umap(adata, color="veloVI_latent_time", cmap="viridis")
185```
Latent Time
# Plot latent time (pseudotime from velocity)
sc.pl.umap(adata, color="veloVI_latent_time", cmap="viridis")Raw lines 180 to 186187## Step 6: Compare with scVelo
189```python
190# Run standard scVelo for comparison
191scv.tl.velocity(adata, mode="dynamical")
192scv.tl.velocity_graph(adata)
194# Compare velocity fields
195fig, axes = plt.subplots(1, 2, figsize=(12, 5))
197scv.pl.velocity_embedding_stream(
198 adata, basis="umap", ax=axes[0],
199 title="scVelo", show=False
200)
202scv.pl.velocity_embedding_stream(
203 adata, basis="umap", vkey="veloVI_velocity",
204 ax=axes[1], title="veloVI", show=False
205)
207plt.tight_layout()
208```
Step 6: Compare with scVelo
# Run standard scVelo for comparison
scv.tl.velocity(adata, mode="dynamical")
scv.tl.velocity_graph(adata)
# Compare velocity fields
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
scv.pl.velocity_embedding_stream(
adata, basis="umap", ax=axes[0],
title="scVelo", show=False
)
scv.pl.velocity_embedding_stream(
adata, basis="umap", vkey="veloVI_velocity",
ax=axes[1], title="veloVI", show=False
)
plt.tight_layout()Raw lines 187 to 209Step 7: Gene-Level Analysis
Raw lines 210 to 211212### Velocity Phase Portraits
214```python
215# Plot phase portrait for specific genes
216genes = ["SOX2", "PAX6", "DCX", "NEUROD1"]
218scv.pl.velocity(
219 adata,
220 var_names=genes,
221 vkey="veloVI_velocity",
222 colorbar=True
223)
224```
Velocity Phase Portraits
# Plot phase portrait for specific genes
genes = ["SOX2", "PAX6", "DCX", "NEUROD1"]
scv.pl.velocity(
adata,
var_names=genes,
vkey="veloVI_velocity",
colorbar=True
)Raw lines 212 to 225226### Gene Dynamics
228```python
229# Plot expression over latent time
230for gene in genes:
231 fig, ax = plt.subplots(figsize=(6, 4))
233 sc.pl.scatter(
234 adata,
235 x="veloVI_latent_time",
236 y=gene,
237 color="cell_type",
238 ax=ax,
239 show=False
240 )
241 ax.set_xlabel("Latent Time")
242 ax.set_ylabel(f"{gene} Expression")
243```
Gene Dynamics
# Plot expression over latent time
for gene in genes:
fig, ax = plt.subplots(figsize=(6, 4))
sc.pl.scatter(
adata,
x="veloVI_latent_time",
y=gene,
color="cell_type",
ax=ax,
show=False
)
ax.set_xlabel("Latent Time")
ax.set_ylabel(f"{gene} Expression")Raw lines 226 to 244245### Driver Genes
247```python
248# Find genes driving velocity
249scv.tl.rank_velocity_genes(
250 adata,
251 vkey="veloVI_velocity",
252 groupby="cell_type"
253)
255# Get top genes per cluster
256df = scv.get_df(adata, "rank_velocity_genes/names")
257print(df.head(10))
258```
Driver Genes
# Find genes driving velocity
scv.tl.rank_velocity_genes(
adata,
vkey="veloVI_velocity",
groupby="cell_type"
)
# Get top genes per cluster
df = scv.get_df(adata, "rank_velocity_genes/names")
print(df.head(10))Raw lines 245 to 259260## Step 8: Uncertainty Quantification
262veloVI provides uncertainty estimates:
264```python
265# Get velocity with uncertainty
266velocity_mean, velocity_std = vae.get_velocity(
267 n_samples=100,
268 return_mean=True,
269 return_numpy=True
270)
272# Store uncertainty
273adata.layers["velocity_uncertainty"] = velocity_std
275# Visualize uncertainty
276adata.obs["mean_velocity_uncertainty"] = velocity_std.mean(axis=1)
277sc.pl.umap(adata, color="mean_velocity_uncertainty")
278```
Step 8: Uncertainty Quantification
veloVI provides uncertainty estimates:
# Get velocity with uncertainty
velocity_mean, velocity_std = vae.get_velocity(
n_samples=100,
return_mean=True,
return_numpy=True
)
# Store uncertainty
adata.layers["velocity_uncertainty"] = velocity_std
# Visualize uncertainty
adata.obs["mean_velocity_uncertainty"] = velocity_std.mean(axis=1)
sc.pl.umap(adata, color="mean_velocity_uncertainty")Raw lines 260 to 279280## Complete Pipeline
282```python
283def run_velocity_analysis(
284 adata,
285 spliced_layer="spliced",
286 unspliced_layer="unspliced",
287 n_top_genes=2000,
288 max_epochs=500
289):
290 """
291 Complete RNA velocity analysis with veloVI.
293 Parameters
294 ----------
295 adata : AnnData
296 Data with spliced/unspliced layers
297 spliced_layer : str
298 Layer name for spliced counts
299 unspliced_layer : str
300 Layer name for unspliced counts
301 n_top_genes : int
302 Number of velocity genes
303 max_epochs : int
304 Training epochs
306 Returns
307 -------
308 AnnData with velocity and model
309 """
310 import scvi
311 import scvelo as scv
312 import scanpy as sc
314 adata = adata.copy()
316 # Preprocessing
317 scv.pp.filter_and_normalize(
318 adata,
319 min_shared_counts=20,
320 n_top_genes=n_top_genes
321 )
323 # Compute moments (needed for some visualizations)
324 scv.pp.moments(adata, n_pcs=30, n_neighbors=30)
326 # Setup veloVI
327 scvi.model.VELOVI.setup_anndata(
328 adata,
329 spliced_layer=spliced_layer,
330 unspliced_layer=unspliced_layer
331 )
333 # Train
334 model = scvi.model.VELOVI(adata)
335 model.train(max_epochs=max_epochs, early_stopping=True)
337 # Get results
338 adata.obs["latent_time"] = model.get_latent_time(n_samples=25)
339 adata.layers["velocity"] = model.get_velocity(n_samples=25)
341 # Compute velocity graph for visualization
342 scv.tl.velocity_graph(adata, vkey="velocity")
344 # Compute UMAP if not present
345 if "X_umap" not in adata.obsm:
346 sc.pp.neighbors(adata)
347 sc.tl.umap(adata)
349 return adata, model
351# Usage
352adata_velocity, model = run_velocity_analysis(adata)
354# Visualize
355scv.pl.velocity_embedding_stream(
356 adata_velocity,
357 basis="umap",
358 vkey="velocity",
359 color="cell_type"
360)
362sc.pl.umap(adata_velocity, color="latent_time")
363```
Complete Pipeline
def run_velocity_analysis(
adata,
spliced_layer="spliced",
unspliced_layer="unspliced",
n_top_genes=2000,
max_epochs=500
):
"""
Complete RNA velocity analysis with veloVI.
Parameters
----------
adata : AnnData
Data with spliced/unspliced layers
spliced_layer : str
Layer name for spliced counts
unspliced_layer : str
Layer name for unspliced counts
n_top_genes : int
Number of velocity genes
max_epochs : int
Training epochs
Returns
-------
AnnData with velocity and model
"""
import scvi
import scvelo as scv
import scanpy as sc
adata = adata.copy()
# Preprocessing
scv.pp.filter_and_normalize(
adata,
min_shared_counts=20,
n_top_genes=n_top_genes
)
# Compute moments (needed for some visualizations)
scv.pp.moments(adata, n_pcs=30, n_neighbors=30)
# Setup veloVI
scvi.model.VELOVI.setup_anndata(
adata,
spliced_layer=spliced_layer,
unspliced_layer=unspliced_layer
)
# Train
model = scvi.model.VELOVI(adata)
model.train(max_epochs=max_epochs, early_stopping=True)
# Get results
adata.obs["latent_time"] = model.get_latent_time(n_samples=25)
adata.layers["velocity"] = model.get_velocity(n_samples=25)
# Compute velocity graph for visualization
scv.tl.velocity_graph(adata, vkey="velocity")
# Compute UMAP if not present
if "X_umap" not in adata.obsm:
sc.pp.neighbors(adata)
sc.tl.umap(adata)
return adata, model
# Usage
adata_velocity, model = run_velocity_analysis(adata)
# Visualize
scv.pl.velocity_embedding_stream(
adata_velocity,
basis="umap",
vkey="velocity",
color="cell_type"
)
sc.pl.umap(adata_velocity, color="latent_time")Raw lines 280 to 364365## Advanced: Batch-Aware Velocity
367```python
368# For multi-batch data, include batch in model
369scvi.model.VELOVI.setup_anndata(
370 adata,
371 spliced_layer="spliced",
372 unspliced_layer="unspliced",
373 batch_key="batch"
374)
376model = scvi.model.VELOVI(adata)
377model.train()
378```
Advanced: Batch-Aware Velocity
# For multi-batch data, include batch in model
scvi.model.VELOVI.setup_anndata(
adata,
spliced_layer="spliced",
unspliced_layer="unspliced",
batch_key="batch"
)
model = scvi.model.VELOVI(adata)
model.train()Raw lines 365 to 379Interpreting Results
Raw lines 380 to 381382### Good Velocity Signal
384- Streamlines follow expected differentiation
385- Latent time correlates with known biology
386- Phase portraits show clear dynamics
Good Velocity Signal
- Streamlines follow expected differentiation
- Latent time correlates with known biology
- Phase portraits show clear dynamics
388### Poor Velocity Signal
390- Random/chaotic streamlines
391- No correlation with known markers
392- May indicate:
393 - Insufficient unspliced reads
394 - Cells at steady state
395 - Technical issues
Poor Velocity Signal
- Random/chaotic streamlines
- No correlation with known markers
- May indicate:
- Insufficient unspliced reads
- Cells at steady state
- Technical issues
397## Troubleshooting
399| Issue | Cause | Solution |
400|-------|-------|----------|
401| No velocity signal | Low unspliced counts | Check sequencing depth, use kb-python |
402| Reversed direction | Wrong root assignment | Manually set root cells |
403| Noisy streamlines | Too many genes | Reduce n_top_genes |
404| Memory error | Large dataset | Reduce batch_size |
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| No velocity signal | Low unspliced counts | Check sequencing depth, use kb-python |
| Reversed direction | Wrong root assignment | Manually set root cells |
| Noisy streamlines | Too many genes | Reduce n_top_genes |
| Memory error | Large dataset | Reduce batch_size |
406## Key References
408- Gayoso et al. (2023) "Deep generative modeling of transcriptional dynamics for RNA velocity analysis in single cells"
409- La Manno et al. (2018) "RNA velocity of single cells"
410- Bergen et al. (2020) "Generalizing RNA velocity to transient cell states through dynamical modeling"
Key References
- Gayoso et al. (2023) "Deep generative modeling of transcriptional dynamics for RNA velocity analysis in single cells"
- La Manno et al. (2018) "RNA velocity of single cells"
- Bergen et al. (2020) "Generalizing RNA velocity to transient cell states through dynamical modeling"