Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Infer developmental trajectories and pseudotime from single-cell RNA-seq data using Monocle3, Slingshot, and scVelo for RNA velocity analysis. Use when inferring developmental trajectories or pseudotime.
.claude/skills/bio-single-cell-trajectory-inference/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-21 | ✗→✓ | ▲ Improved | — | — |
| case-16 | ✓→✓ | = Same ✓ | — | — |
| case-10 | ✓→✓ | = Same ✓ | — | — |
| case-15 | ✓→✓ | = Same ✓ | — | — |
| case-01 | ✗→✗ | = Same ✗ | — | — |
Reference examples tested with: Cell Ranger 8.0+, scanpy 1.10+
Before using code patterns, verify installed versions match. If versions differ:
pip show <package> then help(module.function) to check signaturespackageVersion('<pkg>') then ?function_name to verify parameters<tool> --version then <tool> --help to confirm flagsIf code throws ImportError, AttributeError, or TypeError, introspect the installed package and adapt the example to match the actual API rather than retrying.
Goal: Infer developmental trajectories and pseudotime ordering using Monocle3's principal graph approach.
Approach: Learn a principal graph through the data manifold, order cells along the graph from a root state, and extract pseudotime values.
"Find the developmental trajectory in my data" → Construct a tree-like graph through the cell embedding, assign pseudotime from a root population, and identify branch points.
rlibrary(monocle3) # Create cell_data_set from Seurat cds <- as.cell_data_set(seurat_obj) # Preprocess (if not already done) cds <- preprocess_cds(cds, num_dim = 50) cds <- reduce_dimension(cds, reduction_method = 'UMAP') # Cluster cells cds <- cluster_cells(cds) # Learn trajectory graph cds <- learn_graph(cds) # Order cells (select root interactively or programmatically) cds <- order_cells(cds, root_cells = root_cell_ids) # Plot trajectory with pseudotime plot_cells(cds, color_cells_by = 'pseudotime', label_branch_points = TRUE, label_leaves = TRUE) # Get pseudotime values pseudotime <- pseudotime(cds)
Goal: Automatically select the trajectory root node based on a known progenitor cluster.
Approach: Identify the principal graph node closest to cells in the specified progenitor cluster and use it as the root for pseudotime ordering.
r# Find root by marker gene expression get_earliest_principal_node <- function(cds, cluster_name) { cell_ids <- which(colData(cds)$seurat_clusters == cluster_name) closest_vertex <- cds@principal_graph_aux[['UMAP']]$pr_graph_cell_proj_closest_vertex closest_vertex <- as.matrix(closest_vertex[cell_ids, ]) root_pr_nodes <- igraph::V(principal_graph(cds)[['UMAP']])$name[as.numeric(names(which.max(table(closest_vertex))))] root_pr_nodes } cds <- order_cells(cds, root_pr_nodes = get_earliest_principal_node(cds, 'stem_cluster'))
Goal: Infer smooth lineage trajectories and pseudotime using Slingshot's minimum spanning tree and principal curves.
Approach: Build a minimum spanning tree through cluster centroids to define lineage structure, then fit smooth principal curves for per-lineage pseudotime.
rlibrary(slingshot) library(SingleCellExperiment) # From Seurat object sce <- as.SingleCellExperiment(seurat_obj) reducedDims(sce)$UMAP <- Embeddings(seurat_obj, 'umap') # Run slingshot sce <- slingshot(sce, clusterLabels = 'seurat_clusters', reducedDim = 'UMAP') # Get pseudotime for each lineage pseudotime_mat <- slingPseudotime(sce) # Get lineage curves curves <- slingCurves(sce) # Plot trajectories plot(reducedDims(sce)$UMAP, col = sce$seurat_clusters, pch = 16) lines(SlingshotDataSet(sce), lwd = 2)
r# Specify starting cluster sce <- slingshot(sce, clusterLabels = 'seurat_clusters', reducedDim = 'UMAP', start.clus = 'HSC') # Specify start and end sce <- slingshot(sce, clusterLabels = 'seurat_clusters', reducedDim = 'UMAP', start.clus = 'HSC', end.clus = c('Erythroid', 'Myeloid'))
Goal: Estimate RNA velocity to predict future cell states from spliced/unspliced transcript ratios.
Approach: Model the dynamics of splicing using stochastic or dynamical models, compute velocity vectors, and project directional flow onto UMAP.
pythonimport scvelo as scv import scanpy as sc # Load data with spliced/unspliced counts adata = scv.read('data.h5ad') # Or merge loom files from velocyto ldata = scv.read('velocyto_output.loom') adata = scv.utils.merge(adata, ldata) # Preprocess scv.pp.filter_and_normalize(adata, min_shared_counts=20, n_top_genes=2000) scv.pp.moments(adata, n_pcs=30, n_neighbors=30) # Compute velocity (stochastic model) scv.tl.velocity(adata, mode='stochastic') scv.tl.velocity_graph(adata) # Visualize velocity streams scv.pl.velocity_embedding_stream(adata, basis='umap', color='clusters')
python# More accurate but slower scv.tl.recover_dynamics(adata, n_jobs=8) scv.tl.velocity(adata, mode='dynamical') scv.tl.velocity_graph(adata) # Latent time (pseudotime) scv.tl.latent_time(adata) scv.pl.scatter(adata, color='latent_time', cmap='gnuplot') # Velocity confidence scv.tl.velocity_confidence(adata) scv.pl.scatter(adata, color=['velocity_confidence', 'velocity_length'])
r# Monocle3: Find genes varying over pseudotime graph_test_res <- graph_test(cds, neighbor_graph = 'principal_graph', cores = 4) sig_genes <- graph_test_res %>% filter(q_value < 0.05) %>% arrange(desc(morans_I)) # Plot gene expression over pseudotime plot_genes_in_pseudotime(cds[rownames(cds) %in% top_genes, ], color_cells_by = 'cluster')
python# scVelo: Top likelihood genes scv.tl.rank_velocity_genes(adata, groupby='clusters', min_corr=0.3) top_genes = adata.uns['rank_velocity_genes']['names'] # Plot phase portraits scv.pl.velocity(adata, var_names=['gene1', 'gene2'], basis='umap')
r# Monocle3: Genes differentially expressed at branch points branch_genes <- graph_test(cds, neighbor_graph = 'principal_graph', cores = 4) # Slingshot + tradeSeq for branch analysis library(tradeSeq) sce <- fitGAM(sce, nknots = 6) branch_res <- earlyDETest(sce, knots = c(3, 4))
bash# Generate loom file with spliced/unspliced counts velocyto run10x -m repeat_mask.gtf /path/to/cellranger_output annotation.gtf # For SmartSeq2 velocyto run_smartseq2 -o output -m repeat_mask.gtf -e sample bam_files/*.bam annotation.gtf
pythonimport scanpy as sc # Compute PAGA sc.tl.paga(adata, groups='leiden') sc.pl.paga(adata, color='leiden', threshold=0.03) # PAGA-initialized UMAP sc.tl.draw_graph(adata, init_pos='paga') sc.pl.draw_graph(adata, color='leiden') # Diffusion pseudotime adata.uns['iroot'] = np.flatnonzero(adata.obs['leiden'] == 'root_cluster')[0] sc.tl.dpt(adata) sc.pl.draw_graph(adata, color='dpt_pseudotime')
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-16 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-19 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-02 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-24 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-23 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-10 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-22 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-15 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-17 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-12 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-09 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-21 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 24 cases were attempted. The headline lift of +4 percentage points is the difference between those two pass rates over the 24 comparable cases.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
Other measured skills in the registry, with their headline benchmark lift.