Complete workflow for single-cell RNA-seq analysis using Scanpy and the scverse ecosystem. Process raw data through quality control, normalization, clustering, and cell type annotation with publication-ready visualizations.
Scanned 9/4/2026
Install to Claude Code
npx -y skills add gabrielmoreira/agent-skills-mirror --skill scrnaseq-scanpy-core-analysis --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Scrnaseq Scanpy Core Analysis?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/gabrielmoreira-scrnaseq-scanpy-core-analysis)More formats (shields.io, HTML) on the badges page.
---
id: scrnaseq-scanpy-core-analysis
name: Single-Cell RNA-seq Core Analysis (Scanpy)
category: transcriptomics
short-description: "Complete single-cell RNA-seq analysis using Scanpy from raw data to cell type annotation with clustering and visualization."
detailed-description: "Complete single-cell RNA-seq analysis using Scanpy from raw data to cell type annotation. Use when you have 10X Chromium, Drop-seq, or other scRNA-seq data requiring QC, normalization, clustering, and visualization. Implements current best practices including ambient RNA correction (CellBender), batch-aware adaptive QC (MAD), doublet detection (Scrublet), standard or Pearson residuals normalization, batch integration (scVI/Harmony), multi-resolution Leiden clustering, and pseudobulk differential expression for condition comparisons. Best for human or mouse data with 500+ cells per sample. Produces publication-ready plots and annotated AnnData objects."
starting-prompt: "Analyze single-cell RNA-seq data with Scanpy from QC through cell type annotation. Generate a PDF report with an intro, methods, results, conclusions and figures from all of the analyses you perform."
---
# Single-Cell RNA-seq Core Analysis (Scanpy)
Complete workflow for single-cell RNA-seq analysis using Scanpy and the scverse ecosystem. Process raw data through quality control, normalization, clustering, and cell type annotation with publication-ready visualizations.
## When to Use This Skill
- **Analyze 10X Chromium data** (CellRanger output, H5 files, raw/filtered matrices)
- **Process Drop-seq, Smart-seq2, or inDrop** single-cell RNA-seq data
- **Integrate multi-batch data** using scVI, scANVI, or Harmony
- **Annotate cell types** manually or with automated reference-based methods
- **Compare conditions** using pseudobulk differential expression (multi-sample data)
**Don't use for:** Bulk RNA-seq (use bulk-rnaseq-counts-to-de-deseq2), R-based scRNA-seq (use scrnaseq-seurat-core-analysis), Spatial transcriptomics (coming soon)
## Installation
| Package | Version | License | Commercial Use | Installation |
|---------|---------|---------|----------------|--------------|
| scanpy | ≥1.9 | BSD-3-Clause | Permitted | `pip install scanpy` |
| anndata | ≥0.8 | BSD-3-Clause | Permitted | `pip install anndata` |
| numpy | ≥1.20 | BSD-3-Clause | Permitted | `pip install numpy` |
| pandas | ≥1.3 | BSD-3-Clause | Permitted | `pip install pandas` |
| matplotlib | ≥3.4 | PSF | Permitted | `pip install matplotlib` |
| seaborn | ≥0.12 | BSD-3-Clause | Permitted | `pip install seaborn` |
| adjustText | ≥0.8 | MIT | Permitted | `pip install adjustText` |
| scrublet | ≥0.2.3 | MIT | Permitted | `pip install scrublet` |
| scvi-tools | ≥1.0 | BSD-3-Clause | Permitted | `pip install scvi-tools` |
| harmonypy | ≥0.0.9 | GPL-3 | Permitted | `pip install harmonypy` |
| celltypist | ≥1.0 | MIT | Permitted | `pip install celltypist` |
| pydeseq2 | ≥0.4 | MIT | Permitted | `pip install pydeseq2` |
**Install all:** `pip install scanpy anndata numpy pandas matplotlib seaborn adjustText scrublet`
**Minimum versions:** Python ≥3.8, scanpy ≥1.9, anndata ≥0.8
## Inputs
**Required:**
- **Raw or filtered count matrix:** CellRanger output (`filtered_feature_bc_matrix/`), H5 files (`.h5`), AnnData (`.h5ad`), or count matrices (CSV/TSV)
**Optional:** Sample metadata (CSV/TSV) with sample IDs, conditions, batches, donor IDs
**Data requirements:** Min 500 cells/sample (1000+ recommended), Human or Mouse, UMI-based or read counts. See [references/qc_guidelines.md](references/qc_guidelines.md) for tissue-specific thresholds.
## Outputs
**Analysis objects:**
- `adata_processed.h5ad` - Complete annotated AnnData object
- **Load with:** `import scanpy as sc; adata = sc.read_h5ad('adata_processed.h5ad')`
- Contains: raw counts, normalized data, QC metrics, clusters, cell types, UMAP/PCA
- **Note:** `adata.X` contains log-normalized (not scaled) data. Scaled data is used internally for PCA but not stored in .X. This is correct for downstream analysis (DE, visualization).
- **Required for:** trajectory inference, cell-cell communication, downstream analyses
**Reports:**
- `scrna_analysis_report.pdf` - Agent-generated comprehensive PDF with Methods, Results, Figures, Conclusions
- `analysis_summary.txt` - Text summary of dataset, QC, clustering, integration (generated by `export_anndata_results()`)
**⚠️ PDF style rules:**
- **US Letter page size (8.5 × 11 in)** — always set page dimensions explicitly; do not rely on library defaults
- **No Unicode superscripts** — use `3.36e-06` or `3.36 × 10^(-6)`, not Unicode superscript chars (they render as ■ in PDF fonts)
- **No half-empty pages** — group headings with their content; only page-break before major sections (Results, Conclusions)
- **Figures ≥80% page width** — multi-panel figures must be large enough to read; never embed below 50% width
**Tables:** `cell_metadata.csv`, `expression_matrix_counts.csv`, `expression_matrix_normalized.csv`, `pca_coordinates.csv`, `umap_coordinates.csv`, `cluster_markers_all.csv`, `{celltype}_deseq2_results.csv`
**Visualizations (PNG + SVG at 300 DPI):** QC violins, UMAP plots, marker heatmaps, dot plots, volcano/MA plots
## Clarification Questions
**Default settings (use unless user specifies otherwise):**
- Format: Filtered 10X CellRanger output | Species: Human | Tissue: PBMC
- Normalization: Standard (target sum + log1p) | Clustering: Test 0.4, 0.6, 0.8, 1.0
### 1. **Input Files** (ASK THIS FIRST):
- Do you have specific single-cell data file(s) to analyze?
- Expected: CellRanger output, H5, H5AD, or count matrix
- **Or use example/demo data?** (PBMC 3k dataset available)
### 2. **Data format and species:**
- *(If using your own data)* What format? Filtered 10X (default), Raw 10X, H5, H5AD? Human or Mouse? Tissue type?
- *(If using example data)* Defaults apply: filtered 10X, human, PBMC — skip to Q3
### 3. **Batch structure:**
- a) Single sample (no integration needed)
- b) Multiple batches (scVI recommended, Harmony for speed)
### 4. **Analysis scope:**
- a) Standard: QC + normalize + cluster + annotate (recommended)
- b) Standard + pseudobulk DE (requires ≥2 samples per condition)
- c) Custom: specify which steps
### 5. **Clustering granularity:**
- a) Coarse (0.3-0.5) — major cell types only
- b) Standard (0.6-0.8) — recommended
- c) Fine (1.0-1.5) — subtypes
- d) Test multiple: 0.4, 0.6, 0.8, 1.0 (recommended)
## Standard Workflow
> **Note:** Run from the OmicsClaw root directory and add the workflow scripts to `sys.path`:
> ```python
> import sys; import os; sys.path.insert(0, os.path.abspath('knowledge_base/scripts/scrnaseq-scanpy-core-analysis'))
> ```
🚨 **MANDATORY: USE SCRIPTS EXACTLY AS SHOWN - DO NOT WRITE INLINE CODE** 🚨
**Detailed step-by-step code:** [references/workflow-details.md](references/workflow-details.md)
**CRITICAL - DO NOT:**
- Write inline analysis code → **STOP: Use the script functions**
- Write custom export code → **STOP: Use `export_anndata_results()`**
- Skip verification messages → **STOP: Check for "✓" messages after each step**
**IF SCRIPTS FAIL - Script Failure Hierarchy:**
1. **Fix and Retry (90%)** - Install missing package, re-run script
2. **Modify Script (5%)** - Edit the script file itself, document changes
3. **Use as Reference (4%)** - Read script, adapt approach, cite source
4. **Write from Scratch (1%)** - Only if genuinely impossible, explain why
**NEVER skip directly to writing inline code without trying the script first.**
---
**Step 1 — Load and QC** | [knowledge_base/scripts/scrnaseq-scanpy-core-analysis/setup_and_import.py](knowledge_base/scripts/scrnaseq-scanpy-core-analysis/setup_and_import.py), [scripts/qc_metrics.py](knowledge_base/scripts/scrnaseq-scanpy-core-analysis/qc_metrics.py), [scripts/filter_cells.py](knowledge_base/scripts/scrnaseq-scanpy-core-analysis/filter_cells.py)
```python
# Load data (Option A: example, Option B: your data)
from load_example_data import load_example_data
adata = load_example_data("pbmc3k")
# QC metrics + adaptive filtering + doublet detection
from qc_metrics import calculate_qc_metrics, batch_mad_outlier_detection
from filter_cells import run_scrublet_detection, filter_by_mad_outliers
adata = calculate_qc_metrics(adata, species="human")
adata = batch_mad_outlier_detection(adata, batch_key="batch") # creates 'outlier' column
adata = run_scrublet_detection(adata, batch_key="batch")
adata = filter_by_mad_outliers(adata, remove_doublets=True)
```
**DO NOT write inline QC code.** Doublet rate auto-scales per batch (~0.8% per 1,000 cells). Aim for >70% cell retention. For raw data, prepend ambient RNA correction: [references/ambient_rna_correction.md](references/ambient_rna_correction.md)
**✅ VERIFICATION:** `"✓ Data loaded successfully!"` → QC metrics added → filtering summary with retention %.
**Step 2 — Normalize, reduce, integrate** | [scripts/normalize_data.py](knowledge_base/scripts/scrnaseq-scanpy-core-analysis/normalize_data.py), [scripts/scale_and_pca.py](knowledge_base/scripts/scrnaseq-scanpy-core-analysis/scale_and_pca.py), [scripts/integrate_scvi.py](knowledge_base/scripts/scrnaseq-scanpy-core-analysis/integrate_scvi.py)
```python
from normalize_data import run_standard_normalization
from find_variable_genes import find_highly_variable_genes
from scale_and_pca import scale_data, run_pca_analysis
adata = run_standard_normalization(adata, target_sum=1e4)
adata = find_highly_variable_genes(adata, n_top_genes=2000)
adata = scale_data(adata, vars_to_regress=["total_counts", "pct_counts_mt"])
adata = run_pca_analysis(adata, n_pcs=50)
# Multi-batch only: integration + diagnostics
from integrate_scvi import run_scvi_integration
from integration_diagnostics import compute_lisi_scores
adata = run_scvi_integration(adata, batch_key="batch", condition_key="condition")
lisi = compute_lisi_scores(adata, batch_key="batch", use_rep="X_scVI")
```
**DO NOT write inline normalization or integration code.** The integration script auto-detects batch-condition confounding. [Details →](references/integration_methods.md)
**✅ VERIFICATION:**
- Normalization: `"✓ Normalization complete"`
- PCA: `"PCA loadings verified: N HVG rows have non-zero loadings"` — if you see a WARNING about zero loadings, re-run PCA with `use_highly_variable=True`
- After PCA, call `suggest_n_pcs(adata)` to get recommended PC count for Step 3
- Integration (multi-batch): LISI scores printed
**Step 3 — Cluster, annotate, visualize** | [scripts/cluster_cells.py](knowledge_base/scripts/scrnaseq-scanpy-core-analysis/cluster_cells.py), [scripts/find_markers.py](knowledge_base/scripts/scrnaseq-scanpy-core-analysis/find_markers.py), [scripts/annotate_celltypes.py](knowledge_base/scripts/scrnaseq-scanpy-core-analysis/annotate_celltypes.py)
```python
from cluster_cells import build_neighbor_graph, cluster_leiden_multiple_resolutions
from run_umap import run_umap_reduction
from find_markers import find_all_cluster_markers
from plot_dimreduction import plot_umap_clusters
use_rep = "X_scVI" if "X_scVI" in adata.obsm else "X_pca"
# Default n_pcs=30 is standard. NEVER use <15 PCs.
adata = build_neighbor_graph(adata, use_rep=use_rep, n_neighbors=10, n_pcs=30)
adata = cluster_leiden_multiple_resolutions(adata, resolutions=[0.4, 0.6, 0.8, 1.0])
adata = run_umap_reduction(adata)
markers = find_all_cluster_markers(adata, cluster_key="leiden_0.8")
plot_umap_clusters(adata, cluster_key="leiden_0.8", output_dir="results/umap")
# Annotate (manual or CellTypist)
from annotate_celltypes import annotate_clusters_manual
annotations = {"0": "CD4 T cells", "1": "CD14+ Monocytes", ...}
adata = annotate_clusters_manual(adata, annotations, cluster_key="leiden_0.8")
```
**DO NOT write inline clustering or annotation code.** CellTypist validates labels post-hoc (flags suspect ILC/HSC, contamination, low-complexity). [Markers →](references/marker_gene_database.md)
**⚠️ n_pcs for neighbor graph:** Default is 30 PCs (standard). Using <15 PCs risks collapsing distinct populations. If you used `suggest_n_pcs()` in Step 2, pass that value here.
For **pseudobulk DE** (multi-sample, ≥2 replicates/condition): [scripts/pseudobulk_de.py](knowledge_base/scripts/scrnaseq-scanpy-core-analysis/pseudobulk_de.py). Script blocks with N=1. [Details →](references/pseudobulk_de_guide.md)
**✅ VERIFICATION:**
- Cluster counts → UMAP plots saved → marker genes identified → cell type annotations added
- After `find_all_cluster_markers()`: verify the returned DataFrame columns and print `markers.head()` to confirm values are sensible before saving to CSV.
**Step 4 — Export results** | [scripts/export_results.py](knowledge_base/scripts/scrnaseq-scanpy-core-analysis/export_results.py)
```python
from export_results import export_anndata_results
export_anndata_results(adata, output_dir="results", cluster_key="cell_type")
```
**DO NOT write custom export code. Use export_anndata_results().**
Exports: H5AD, expression matrices (raw + normalized CSV), cell metadata, UMAP/PCA coordinates, text summary.
**✅ VERIFICATION:** You MUST see:
```
=== Export Complete ===
```
**If you don't see "Export Complete":** The export did not complete. Re-run the export function.
**⚠️ Large datasets (>20k cells):** H5AD export may take 10-60 seconds depending on dataset size. The script prints progress updates (size estimate, compression method, elapsed time). If export appears stuck for >2 minutes, interrupt and retry with `save_h5ad(adata, path, compression=None)` to skip compression.
## Decision Guide
| Decision | Quick Guide | Reference |
|----------|-------------|-----------|
| **Ambient RNA** | Skip for filtered/PBMC. CellBender for raw/high-soup (brain, lung, tumor) | [ambient_rna_correction.md](references/ambient_rna_correction.md) |
| **QC Strategy** | MAD (multi-batch). Fixed (single batch, tissue-specific) | [qc_guidelines.md](references/qc_guidelines.md) |
| **Normalization** | Standard (most data). Pearson (heteroscedastic) | [scanpy_best_practices.md](references/scanpy_best_practices.md) |
| **Integration** | scVI (complex batches). Harmony (fast, simple) | [integration_methods.md](references/integration_methods.md) |
| **Resolution** | Test 0.4, 0.6, 0.8, 1.0. Choose by biology and stability | [scanpy_best_practices.md](references/scanpy_best_practices.md) |
| **Annotation** | Manual (accurate). CellTypist (fast). Both (validate) | [marker_gene_database.md](references/marker_gene_database.md) |
**Complete working examples:** [references/common-patterns.md](references/common-patterns.md)
## Common Issues
| Issue | Cause | Solution |
|-------|-------|----------|
| `ImportError: No module named 'scanpy'` | Not installed | `pip install scanpy anndata numpy pandas matplotlib seaborn` |
| Low cell retention (<70%) | Strict QC thresholds | Use MAD (nmads=5→7) or tissue-specific thresholds |
| Out of memory | Large dataset (>50k cells) | Use backed mode or subsample |
| Clusters driven by batch | Insufficient integration | Use scVI, increase n_latent, check confounding |
| Poor UMAP separation | Wrong parameters | Check PCA elbow, use 20-40 PCs, adjust n_neighbors |
| High MT% in all cells | Degradation or tissue-specific | Check distribution — bimodal: stricter filter; uniform: may be biological |
| `FileNotFoundError: barcodes.tsv.gz` | Wrong directory | Verify 10X output files present. Use `import_h5_data()` for .h5 |
| H5AD export hangs or is very slow | Large file write with compression | Normal for >50 MB files. Script uses fast `lzf` compression. If still slow, pass `compression=None` to `save_h5ad()`. |
| `NameError` after export interruption | Kernel restart lost variables | Re-run from Step 1 to restore `adata`. Export is idempotent — safe to re-run. |
**Expected warnings (not errors):**
| Warning | Meaning | Action |
|---------|---------|--------|
| SVG export failed | Optional SVG dependency unavailable | Normal — PNG always generated. Both created in most environments. |
| Detected doublet rate differs from expected | Scrublet threshold or pre-filtering | Inspect `adata.obs['doublet_score'].hist()`. Adjust threshold if needed. |
| Pseudobulk DE blocked: N=1 | Insufficient replicates | Need ≥2 per condition. Use cell-level Wilcoxon for exploratory only. |
| Batch-condition confounding | Condition has 1 sample | Clustering valid. Composition comparisons need caveats. |
| CellTypist labels suspect (ILC, HSC) | Automated misclassification | Cross-check markers. Relabel ILC→NK or HSC→Unassigned. |
| Multimodal data detected | RNA-only workflow on CITE-seq | Note in reports: "RNA modality only; ADT not analyzed." |
**Detailed troubleshooting:** [references/troubleshooting_guide.md](references/troubleshooting_guide.md)
## Suggested Next Steps
1. **Functional Enrichment** — functional-enrichment-from-degs for pathway analysis of DE results
2. **Trajectory Analysis** — PAGA, Palantir, or scVelo for developmental datasets
3. **Cell-Cell Communication** — CellPhoneDB, LIANA, or NicheNet for ligand-receptor interactions
## Related Skills
**Alternative:** scrnaseq-seurat-core-analysis (R-based) | **Downstream:** functional-enrichment-from-degs, de-results-to-plots, de-results-to-gene-lists | **Complementary:** bulk-omics-clustering, experimental-design-statistics
## References
1. **Scanpy:** Wolf FA, et al. (2018) *Genome Biol*. 19:15.
2. **Best Practices:** Luecken MD, Theis FJ. (2019) *Mol Syst Biol*. 15:e8746.
3. **Pseudobulk DE:** Squair JW, et al. (2021) *Nat Commun*. 12:5692.
4. **scVI:** Lopez R, et al. (2018) *Nat Methods*. 15:1053-1058.
**Detailed guides:** [workflow-details.md](references/workflow-details.md) | [common-patterns.md](references/common-patterns.md) | [scanpy_best_practices.md](references/scanpy_best_practices.md) | [qc_guidelines.md](references/qc_guidelines.md) | [integration_methods.md](references/integration_methods.md) | [pseudobulk_de_guide.md](references/pseudobulk_de_guide.md) | [marker_gene_database.md](references/marker_gene_database.md) | [troubleshooting_guide.md](references/troubleshooting_guide.md)
**Scripts:** [scripts/](knowledge_base/scripts/scrnaseq-scanpy-core-analysis/) | **Evaluation:** [assets/eval/complete_example_analysis.py](assets/eval/complete_example_analysis.py)
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!