$npx -y skills add LeonChaoX/qinyan-academic-skills --skill scanpyStandard single-cell RNA-seq analysis pipeline. Use for QC, normalization, dimensionality reduction (PCA/UMAP/t-SNE), clustering, differential expression, and visualization. Best for exploratory scRNA-seq analysis with established workflows. For deep learning models use scvi-tool
| 1 | # Scanpy: Single-Cell Analysis |
| 2 | |
| 3 | ## Overview |
| 4 | |
| 5 | Scanpy is a scalable Python toolkit for analyzing single-cell RNA-seq data, built on AnnData. Apply this skill for complete single-cell workflows including quality control, normalization, dimensionality reduction, clustering, marker gene identification, visualization, and trajectory analysis. |
| 6 | |
| 7 | ## When to Use This Skill |
| 8 | |
| 9 | This skill should be used when: |
| 10 | - Analyzing single-cell RNA-seq data (.h5ad, 10X, CSV formats) |
| 11 | - Performing quality control on scRNA-seq datasets |
| 12 | - Creating UMAP, t-SNE, or PCA visualizations |
| 13 | - Identifying cell clusters and finding marker genes |
| 14 | - Annotating cell types based on gene expression |
| 15 | - Conducting trajectory inference or pseudotime analysis |
| 16 | - Generating publication-quality single-cell plots |
| 17 | |
| 18 | ## Quick Start |
| 19 | |
| 20 | ### Basic Import and Setup |
| 21 | |
| 22 | ```python |
| 23 | import scanpy as sc |
| 24 | import pandas as pd |
| 25 | import numpy as np |
| 26 | |
| 27 | # Configure settings |
| 28 | sc.settings.verbosity = 3 |
| 29 | sc.settings.set_figure_params(dpi=80, facecolor='white') |
| 30 | sc.settings.figdir = './figures/' |
| 31 | ``` |
| 32 | |
| 33 | ### Loading Data |
| 34 | |
| 35 | ```python |
| 36 | # From 10X Genomics |
| 37 | adata = sc.read_10x_mtx('path/to/data/') |
| 38 | adata = sc.read_10x_h5('path/to/data.h5') |
| 39 | |
| 40 | # From h5ad (AnnData format) |
| 41 | adata = sc.read_h5ad('path/to/data.h5ad') |
| 42 | |
| 43 | # From CSV |
| 44 | adata = sc.read_csv('path/to/data.csv') |
| 45 | ``` |
| 46 | |
| 47 | ### Understanding AnnData Structure |
| 48 | |
| 49 | The AnnData object is the core data structure in scanpy: |
| 50 | |
| 51 | ```python |
| 52 | adata.X # Expression matrix (cells × genes) |
| 53 | adata.obs # Cell metadata (DataFrame) |
| 54 | adata.var # Gene metadata (DataFrame) |
| 55 | adata.uns # Unstructured annotations (dict) |
| 56 | adata.obsm # Multi-dimensional cell data (PCA, UMAP) |
| 57 | adata.raw # Raw data backup |
| 58 | |
| 59 | # Access cell and gene names |
| 60 | adata.obs_names # Cell barcodes |
| 61 | adata.var_names # Gene names |
| 62 | ``` |
| 63 | |
| 64 | ## Standard Analysis Workflow |
| 65 | |
| 66 | ### 1. Quality Control |
| 67 | |
| 68 | Identify and filter low-quality cells and genes: |
| 69 | |
| 70 | ```python |
| 71 | # Identify mitochondrial genes |
| 72 | adata.var['mt'] = adata.var_names.str.startswith('MT-') |
| 73 | |
| 74 | # Calculate QC metrics |
| 75 | sc.pp.calculate_qc_metrics(adata, qc_vars=['mt'], inplace=True) |
| 76 | |
| 77 | # Visualize QC metrics |
| 78 | sc.pl.violin(adata, ['n_genes_by_counts', 'total_counts', 'pct_counts_mt'], |
| 79 | jitter=0.4, multi_panel=True) |
| 80 | |
| 81 | # Filter cells and genes |
| 82 | sc.pp.filter_cells(adata, min_genes=200) |
| 83 | sc.pp.filter_genes(adata, min_cells=3) |
| 84 | adata = adata[adata.obs.pct_counts_mt < 5, :] # Remove high MT% cells |
| 85 | ``` |
| 86 | |
| 87 | **Use the QC script for automated analysis:** |
| 88 | ```bash |
| 89 | python scripts/qc_analysis.py input_file.h5ad --output filtered.h5ad |
| 90 | ``` |
| 91 | |
| 92 | ### 2. Normalization and Preprocessing |
| 93 | |
| 94 | ```python |
| 95 | # Normalize to 10,000 counts per cell |
| 96 | sc.pp.normalize_total(adata, target_sum=1e4) |
| 97 | |
| 98 | # Log-transform |
| 99 | sc.pp.log1p(adata) |
| 100 | |
| 101 | # Save raw counts for later |
| 102 | adata.raw = adata |
| 103 | |
| 104 | # Identify highly variable genes |
| 105 | sc.pp.highly_variable_genes(adata, n_top_genes=2000) |
| 106 | sc.pl.highly_variable_genes(adata) |
| 107 | |
| 108 | # Subset to highly variable genes |
| 109 | adata = adata[:, adata.var.highly_variable] |
| 110 | |
| 111 | # Regress out unwanted variation |
| 112 | sc.pp.regress_out(adata, ['total_counts', 'pct_counts_mt']) |
| 113 | |
| 114 | # Scale data |
| 115 | sc.pp.scale(adata, max_value=10) |
| 116 | ``` |
| 117 | |
| 118 | ### 3. Dimensionality Reduction |
| 119 | |
| 120 | ```python |
| 121 | # PCA |
| 122 | sc.tl.pca(adata, svd_solver='arpack') |
| 123 | sc.pl.pca_variance_ratio(adata, log=True) # Check elbow plot |
| 124 | |
| 125 | # Compute neighborhood graph |
| 126 | sc.pp.neighbors(adata, n_neighbors=10, n_pcs=40) |
| 127 | |
| 128 | # UMAP for visualization |
| 129 | sc.tl.umap(adata) |
| 130 | sc.pl.umap(adata, color='leiden') |
| 131 | |
| 132 | # Alternative: t-SNE |
| 133 | sc.tl.tsne(adata) |
| 134 | ``` |
| 135 | |
| 136 | ### 4. Clustering |
| 137 | |
| 138 | ```python |
| 139 | # Leiden clustering (recommended) |
| 140 | sc.tl.leiden(adata, resolution=0.5) |
| 141 | sc.pl.umap(adata, color='leiden', legend_loc='on data') |
| 142 | |
| 143 | # Try multiple resolutions to find optimal granularity |
| 144 | for res in [0.3, 0.5, 0.8, 1.0]: |
| 145 | sc.tl.leiden(adata, resolution=res, key_added=f'leiden_{res}') |
| 146 | ``` |
| 147 | |
| 148 | ### 5. Marker Gene Identification |
| 149 | |
| 150 | ```python |
| 151 | # Find marker genes for each cluster |
| 152 | sc.tl.rank_genes_groups(adata, 'leiden', method='wilcoxon') |
| 153 | |
| 154 | # Visualize results |
| 155 | sc.pl.rank_genes_groups(adata, n_genes=25, sharey=False) |
| 156 | sc.pl.rank_genes_groups_heatmap(adata, n_genes=10) |
| 157 | sc.pl.rank_genes_groups_dotplot(adata, n_genes=5) |
| 158 | |
| 159 | # Get results as DataFrame |
| 160 | markers = sc.get.rank_genes_groups_df(adata, group='0') |
| 161 | ``` |
| 162 | |
| 163 | ### 6. Cell Type Annotation |
| 164 | |
| 165 | ```python |
| 166 | # Define marker genes for known cell types |
| 167 | marker_genes = ['CD3D', 'CD14', 'MS4A1', 'NKG7', 'FCGR3A'] |
| 168 | |
| 169 | # Visualize markers |
| 170 | sc.pl.umap(adata, color=marker_genes, use_raw=True) |
| 171 | sc.pl.dotplot(adata, var_names=marker_genes, groupby='leiden') |
| 172 | |
| 173 | # Manual annotation |
| 174 | cluster_to_celltype = { |
| 175 | '0': 'CD4 T cells', |
| 176 | '1': 'CD14+ Monocytes', |
| 177 | '2': 'B cells', |
| 178 | '3': 'CD8 T ce |