Skills › Scientific Agent Skills › scanpy › api_reference.md
api_reference.md
279 lines · skills/scanpy/references/api_reference.md at 5f2bc03
1# Scanpy API Quick Reference
3Reviewed for Scanpy 1.12.4 / AnnData 0.13.4 on 2026-10-01. These snippets are
4illustrative and require the named genes, groups and prior results to exist.
5Use the tested CLI workflow for a runnable starting point. Counts, normalized
6expression and `.raw` have distinct roles; see [upstream-review.md](upstream-review.md).
9Quick reference for commonly used scanpy functions organized by module.
Scanpy API Quick Reference
Reviewed for Scanpy 1.12.4 / AnnData 0.13.4 on 2026-10-01. These snippets are illustrative and require the named genes, groups and prior results to exist. Use the tested CLI workflow for a runnable starting point. Counts, normalized expression and .raw have distinct roles; see upstream-review.md.
Quick reference for commonly used scanpy functions organized by module.
Raw lines 1 to 10Reading and Writing Data (sc.read_*)
Raw lines 17 to 1819### Reading Functions
21```python
22sc.read_10x_h5(filename) # Read 10X HDF5 file
23sc.read_10x_mtx(path) # Read 10X mtx directory
24sc.read_h5ad(filename) # Read h5ad (AnnData) file
25sc.read_csv(filename) # Read CSV file
26sc.read_excel(filename, sheet=0) # Requires a sheet and Excel reader dependency
27sc.read_loom(filename) # Read loom file
28sc.read_text(filename) # Read text file
29sc.read_visium(path) # Deprecated; use spatialdata-io for new workflows
30```
Reading Functions
sc.read_10x_h5(filename) # Read 10X HDF5 file
sc.read_10x_mtx(path) # Read 10X mtx directory
sc.read_h5ad(filename) # Read h5ad (AnnData) file
sc.read_csv(filename) # Read CSV file
sc.read_excel(filename, sheet=0) # Requires a sheet and Excel reader dependency
sc.read_loom(filename) # Read loom file
sc.read_text(filename) # Read text file
sc.read_visium(path) # Deprecated; use spatialdata-io for new workflowsRaw lines 19 to 3132### Writing Functions
34```python
35adata.write_h5ad(filename) # Write to h5ad format
36adata.write_csvs(dirname) # Write to CSV files
37adata.write_loom(filename) # Write to loom format
38adata.write_zarr(filename) # Write to zarr format
39```
Writing Functions
adata.write_h5ad(filename) # Write to h5ad format
adata.write_csvs(dirname) # Write to CSV files
adata.write_loom(filename) # Write to loom format
adata.write_zarr(filename) # Write to zarr formatRaw lines 32 to 40Preprocessing (sc.pp.*)
Raw lines 41 to 4243### Quality Control
45```python
46sc.pp.calculate_qc_metrics(adata, qc_vars=['mt'], percent_top=None, inplace=True)
47sc.pp.filter_cells(adata, min_genes=200)
48sc.pp.filter_genes(adata, min_cells=3)
49sc.pp.scrublet(adata) # Doublet detection (core since 1.10)
50sc.pp.scrublet_simulate_doublets(adata) # Simulate doublets for benchmarking
51```
Quality Control
sc.pp.calculate_qc_metrics(adata, qc_vars=['mt'], percent_top=None, inplace=True)
sc.pp.filter_cells(adata, min_genes=200)
sc.pp.filter_genes(adata, min_cells=3)
sc.pp.scrublet(adata) # Doublet detection (core since 1.10)
sc.pp.scrublet_simulate_doublets(adata) # Simulate doublets for benchmarkingRaw lines 43 to 5253### Normalization and Transformation
55```python
56sc.pp.normalize_total(adata, target_sum=1e4) # Normalize to target sum
57sc.pp.log1p(adata) # Log(x + 1) transformation
58sc.pp.sqrt(adata) # Square root transformation
59```
Normalization and Transformation
sc.pp.normalize_total(adata, target_sum=1e4) # Normalize to target sum
sc.pp.log1p(adata) # Log(x + 1) transformation
sc.pp.sqrt(adata) # Square root transformationRaw lines 53 to 6061### Feature Selection
63```python
64sc.pp.highly_variable_genes(adata, min_mean=0.0125, max_mean=3, min_disp=0.5)
65sc.pp.highly_variable_genes(adata, flavor='seurat_v3', n_top_genes=2000)
66# Dask support is experimental; v3 flavors gained support in 1.12.
67```
Feature Selection
sc.pp.highly_variable_genes(adata, min_mean=0.0125, max_mean=3, min_disp=0.5)
sc.pp.highly_variable_genes(adata, flavor='seurat_v3', n_top_genes=2000)
# Dask support is experimental; v3 flavors gained support in 1.12.Raw lines 61 to 6869### Scaling and Regression
71```python
72sc.pp.scale(adata, max_value=10) # Scale to unit variance
73sc.pp.regress_out(adata, ['total_counts', 'pct_counts_mt']) # Regress out unwanted variation
74```
Scaling and Regression
sc.pp.scale(adata, max_value=10) # Scale to unit variance
sc.pp.regress_out(adata, ['total_counts', 'pct_counts_mt']) # Regress out unwanted variationRaw lines 69 to 7576### Dimensionality Reduction (Preprocessing)
78```python
79sc.pp.pca(adata, n_comps=50) # Principal component analysis
80sc.pp.neighbors(adata, n_neighbors=10, n_pcs=40) # Compute neighborhood graph
81sc.pp.neighbors(adata, method='jaccard') # Jaccard connectivities (scanpy 1.12)
82```
Dimensionality Reduction (Preprocessing)
sc.pp.pca(adata, n_comps=50) # Principal component analysis
sc.pp.neighbors(adata, n_neighbors=10, n_pcs=40) # Compute neighborhood graph
sc.pp.neighbors(adata, method='jaccard') # Jaccard connectivities (scanpy 1.12)Raw lines 76 to 83Tools (sc.tl.*)
Raw lines 90 to 9192### Dimensionality Reduction
94```python
95sc.tl.pca(adata, svd_solver='arpack') # PCA
96sc.tl.umap(adata) # UMAP embedding
97sc.tl.tsne(adata) # t-SNE embedding
98sc.tl.diffmap(adata) # Diffusion map
99sc.tl.draw_graph(adata, layout='fa') # Force-directed graph
100```
Dimensionality Reduction
sc.tl.pca(adata, svd_solver='arpack') # PCA
sc.tl.umap(adata) # UMAP embedding
sc.tl.tsne(adata) # t-SNE embedding
sc.tl.diffmap(adata) # Diffusion map
sc.tl.draw_graph(adata, layout='fa') # Force-directed graphRaw lines 92 to 101102### Clustering
104```python
105sc.tl.leiden(adata, resolution=0.5) # Leiden clustering (recommended)
106# sc.tl.louvain(adata, resolution=0.5) # Deprecated in scanpy 1.12 — use leiden
107# K-means is not a scanpy.tl function; use sklearn.cluster.KMeans explicitly.
108```
Clustering
sc.tl.leiden(adata, resolution=0.5) # Leiden clustering (recommended)
# sc.tl.louvain(adata, resolution=0.5) # Deprecated in scanpy 1.12 — use leiden
# K-means is not a scanpy.tl function; use sklearn.cluster.KMeans explicitly.Raw lines 102 to 109110### Marker Genes and Differential Expression
112```python
113sc.tl.rank_genes_groups(adata, groupby='leiden', method='wilcoxon')
114sc.tl.rank_genes_groups(adata, groupby='leiden', method='t-test')
115sc.tl.rank_genes_groups(adata, groupby='leiden', method='logreg')
117# Get results as dataframe
118sc.get.rank_genes_groups_df(adata, group='0')
119# Exploratory only — per-cell tests inflate significance (often making p-values too small); pseudobulk for rigorous DE
120```
Marker Genes and Differential Expression
sc.tl.rank_genes_groups(adata, groupby='leiden', method='wilcoxon')
sc.tl.rank_genes_groups(adata, groupby='leiden', method='t-test')
sc.tl.rank_genes_groups(adata, groupby='leiden', method='logreg')
# Get results as dataframe
sc.get.rank_genes_groups_df(adata, group='0')
# Exploratory only — per-cell tests inflate significance (often making p-values too small); pseudobulk for rigorous DERaw lines 110 to 121122### Aggregation (Pseudobulk)
124```python
125sc.get.aggregate(adata, by='cell_type', func='sum', layer='counts')
126pb = sc.get.aggregate(adata, by=['sample', 'cell_type'], func='sum', layer='counts')
127counts = pb.layers['sum'] # cells/groups x genes; pb.X is None
128# Keep biological replicate metadata; PyDESeq2 expects sample rows and gene columns.
129```
Aggregation (Pseudobulk)
sc.get.aggregate(adata, by='cell_type', func='sum', layer='counts')
pb = sc.get.aggregate(adata, by=['sample', 'cell_type'], func='sum', layer='counts')
counts = pb.layers['sum'] # cells/groups x genes; pb.X is None
# Keep biological replicate metadata; PyDESeq2 expects sample rows and gene columns.Raw lines 122 to 130131### Trajectory Inference
133```python
134sc.tl.paga(adata, groups='leiden') # PAGA trajectory
135sc.tl.diffmap(adata, n_comps=10) # choose fewer for small datasets
136sc.tl.dpt(adata, n_dcs=10) # Diffusion pseudotime
137```
Trajectory Inference
sc.tl.paga(adata, groups='leiden') # PAGA trajectory
sc.tl.diffmap(adata, n_comps=10) # choose fewer for small datasets
sc.tl.dpt(adata, n_dcs=10) # Diffusion pseudotimeRaw lines 131 to 138139### Gene Scoring
141```python
142sc.tl.score_genes(adata, gene_list, score_name='score')
143sc.tl.score_genes_cell_cycle(adata, s_genes=s_genes, g2m_genes=g2m_genes)
144```
Gene Scoring
sc.tl.score_genes(adata, gene_list, score_name='score')
sc.tl.score_genes_cell_cycle(adata, s_genes=s_genes, g2m_genes=g2m_genes)Raw lines 139 to 145146### Embeddings and Projections
148```python
149sc.tl.ingest(adata, adata_ref) # Map to reference
150sc.tl.embedding_density(adata, basis='umap', groupby='leiden')
151```
Embeddings and Projections
sc.tl.ingest(adata, adata_ref) # Map to reference
sc.tl.embedding_density(adata, basis='umap', groupby='leiden')Raw lines 146 to 152Plotting (sc.pl.*)
Raw lines 153 to 154155### Basic Embeddings
157```python
158sc.pl.umap(adata, color='leiden') # UMAP plot
159sc.pl.tsne(adata, color='gene_name') # t-SNE plot
160sc.pl.pca(adata, color='leiden') # PCA plot
161sc.pl.diffmap(adata, color='leiden') # Diffusion map plot
162```
Basic Embeddings
sc.pl.umap(adata, color='leiden') # UMAP plot
sc.pl.tsne(adata, color='gene_name') # t-SNE plot
sc.pl.pca(adata, color='leiden') # PCA plot
sc.pl.diffmap(adata, color='leiden') # Diffusion map plotRaw lines 155 to 163164### Heatmaps and Dot Plots
166```python
167sc.pl.heatmap(adata, var_names=genes, groupby='leiden')
168sc.pl.dotplot(adata, var_names=genes, groupby='leiden')
169sc.pl.matrixplot(adata, var_names=genes, groupby='leiden')
170sc.pl.stacked_violin(adata, var_names=genes, groupby='leiden')
171```
Heatmaps and Dot Plots
sc.pl.heatmap(adata, var_names=genes, groupby='leiden')
sc.pl.dotplot(adata, var_names=genes, groupby='leiden')
sc.pl.matrixplot(adata, var_names=genes, groupby='leiden')
sc.pl.stacked_violin(adata, var_names=genes, groupby='leiden')Raw lines 164 to 172173### Violin and Scatter Plots
175```python
176sc.pl.violin(adata, keys=['gene1', 'gene2'], groupby='leiden')
177sc.pl.scatter(adata, x='gene1', y='gene2', color='leiden')
178```
Violin and Scatter Plots
sc.pl.violin(adata, keys=['gene1', 'gene2'], groupby='leiden')
sc.pl.scatter(adata, x='gene1', y='gene2', color='leiden')Raw lines 173 to 179180### Marker Gene Visualization
182```python
183sc.pl.rank_genes_groups(adata, n_genes=25, sharey=False)
184sc.pl.rank_genes_groups_violin(adata, groups='0')
185sc.pl.rank_genes_groups_heatmap(adata, n_genes=10)
186sc.pl.rank_genes_groups_dotplot(adata, n_genes=5)
187```
Marker Gene Visualization
sc.pl.rank_genes_groups(adata, n_genes=25, sharey=False)
sc.pl.rank_genes_groups_violin(adata, groups='0')
sc.pl.rank_genes_groups_heatmap(adata, n_genes=10)
sc.pl.rank_genes_groups_dotplot(adata, n_genes=5)Raw lines 180 to 188189### Trajectory Visualization
191```python
192sc.pl.paga(adata, color='leiden') # PAGA graph
193# Legacy branch-order plot requires dpt_order_indices/dpt_changepoints,
194# which default n_branchings=0 does not create. Prefer the pseudotime UMAP.
195# sc.pl.dpt_timeseries(adata) # DPT timeseries
196```
Trajectory Visualization
sc.pl.paga(adata, color='leiden') # PAGA graph
# Legacy branch-order plot requires dpt_order_indices/dpt_changepoints,
# which default n_branchings=0 does not create. Prefer the pseudotime UMAP.
# sc.pl.dpt_timeseries(adata) # DPT timeseriesRaw lines 189 to 197198### QC Plots
200```python
201sc.pl.highest_expr_genes(adata, n_top=20)
202sc.pl.violin(adata, ['n_genes_by_counts', 'total_counts', 'pct_counts_mt'])
203sc.pl.scatter(adata, x='total_counts', y='n_genes_by_counts')
204```
QC Plots
sc.pl.highest_expr_genes(adata, n_top=20)
sc.pl.violin(adata, ['n_genes_by_counts', 'total_counts', 'pct_counts_mt'])
sc.pl.scatter(adata, x='total_counts', y='n_genes_by_counts')Raw lines 198 to 205206### Advanced Plots
208```python
209sc.pl.dendrogram(adata, groupby='leiden')
210sc.pl.correlation_matrix(adata, groupby='leiden')
211sc.pl.tracksplot(adata, var_names=genes, groupby='leiden')
212```
Advanced Plots
sc.pl.dendrogram(adata, groupby='leiden')
sc.pl.correlation_matrix(adata, groupby='leiden')
sc.pl.tracksplot(adata, var_names=genes, groupby='leiden')Raw lines 206 to 213Common Parameters
Raw lines 214 to 215216### Color Parameters
217- `color`: Variable(s) to color by (gene name, obs column)
218- `use_raw`: Use `.raw` attribute of adata
219- `palette`: Color palette to use
220- `vmin`, `vmax`: Color scale limits
Color Parameters
color: Variable(s) to color by (gene name, obs column)use_raw: Use.rawattribute of adatapalette: Color palette to usevmin,vmax: Color scale limits
222### Layout Parameters
223- `basis`: Embedding basis ('umap', 'tsne', 'pca', etc.)
224- `legend_loc`: Legend location ('on data', 'right margin', etc.)
225- `size`: Point size
226- `alpha`: Point transparency
Layout Parameters
basis: Embedding basis ('umap', 'tsne', 'pca', etc.)legend_loc: Legend location ('on data', 'right margin', etc.)size: Point sizealpha: Point transparency
228### Saving Parameters
229- `show`: Whether to show plot
230- Prefer `sc.settings.autosave` + `sc.settings.figdir` over deprecated `save=`
Saving Parameters
show: Whether to show plot- Prefer
sc.settings.autosave+sc.settings.figdirover deprecatedsave=
232## AnnData Structure
234```python
235adata.X # Expression matrix (cells × genes)
236adata.obs # Cell annotations (DataFrame)
237adata.var # Gene annotations (DataFrame)
238adata.uns # Unstructured annotations (dict)
239adata.obsm # Multi-dimensional cell annotations (e.g., PCA, UMAP)
240adata.varm # Multi-dimensional gene annotations
241adata.layers # Additional data layers
242adata.raw # Snapshot of X/var; not necessarily counts
244# Access
245adata.obs_names # Cell barcodes
246adata.var_names # Gene names
247adata.shape # (n_cells, n_genes)
249# Slicing
250adata[cell_indices, gene_indices]
251adata[:, adata.var_names.isin(gene_list)]
252adata[adata.obs['leiden'] == '0', :]
253```
AnnData Structure
adata.X # Expression matrix (cells × genes)
adata.obs # Cell annotations (DataFrame)
adata.var # Gene annotations (DataFrame)
adata.uns # Unstructured annotations (dict)
adata.obsm # Multi-dimensional cell annotations (e.g., PCA, UMAP)
adata.varm # Multi-dimensional gene annotations
adata.layers # Additional data layers
adata.raw # Snapshot of X/var; not necessarily counts
# Access
adata.obs_names # Cell barcodes
adata.var_names # Gene names
adata.shape # (n_cells, n_genes)
# Slicing
adata[cell_indices, gene_indices]
adata[:, adata.var_names.isin(gene_list)]
adata[adata.obs['leiden'] == '0', :]Raw lines 232 to 254255## Settings
257```python
258sc.settings.verbosity = 3 # 0=error, 1=warning, 2=info, 3=hint
259sc.set_figure_params(dpi=80, facecolor='white')
260sc.settings.autoshow = False # Don't show plots automatically
261sc.settings.autosave = True # Save figures to figdir (preferred over save=)
262sc.settings.figdir = './figures/' # Figure directory
263sc.settings.file_format_figs = 'pdf' # Output format when autosave is True
264sc.settings.cachedir = './cache/' # Cache directory
265sc.settings.n_jobs = 8 # Number of parallel jobs
266```
268Note: the `save=` parameter on individual `sc.pl.*` functions is deprecated in scanpy 1.12. Use `sc.settings.autosave` and `sc.settings.figdir` instead.
Settings
sc.settings.verbosity = 3 # 0=error, 1=warning, 2=info, 3=hint
sc.set_figure_params(dpi=80, facecolor='white')
sc.settings.autoshow = False # Don't show plots automatically
sc.settings.autosave = True # Save figures to figdir (preferred over save=)
sc.settings.figdir = './figures/' # Figure directory
sc.settings.file_format_figs = 'pdf' # Output format when autosave is True
sc.settings.cachedir = './cache/' # Cache directory
sc.settings.n_jobs = 8 # Number of parallel jobsNote: the save= parameter on individual sc.pl.* functions is deprecated in scanpy 1.12. Use sc.settings.autosave and sc.settings.figdir instead.
270## Useful Utilities
272```python
273sc.logging.print_header() # Print version information
274sc.logging.print_memory_usage() # Print memory usage
275adata.copy() # Create a copy of AnnData object
276import anndata as ad
277ad.concat({'sample1': adata1, 'sample2': adata2}, label='sample', join='inner')
278# Align gene IDs first; outer joins fill absent sparse features with zero.
279```
Useful Utilities
sc.logging.print_header() # Print version information
sc.logging.print_memory_usage() # Print memory usage
adata.copy() # Create a copy of AnnData object
import anndata as ad
ad.concat({'sample1': adata1, 'sample2': adata2}, label='sample', join='inner')
# Align gene IDs first; outer joins fill absent sparse features with zero.Raw lines 270 to 279