| name | alterlab-anndata |
| description | Build, slice, concatenate, read, and write AnnData annotated data matrices (obs, var, X, layers, obsm, uns) — the scverse data STRUCTURE, not an analysis pipeline. Use when creating or wrangling .h5ad/zarr files, managing cell and gene annotations, concatenating batches, or handling layers/obsm/backed-mode; for the QC, normalization, clustering, UMAP, and differential-expression analysis pipeline prefer alterlab-scanpy instead, and for RNA velocity from spliced/unspliced layers prefer alterlab-scvelo instead. Part of the AlterLab Academic Skills suite. |
| license | MIT |
| allowed-tools | Read Write Edit Bash(python:*) Bash(uv:*) |
| compatibility | Runs under `uv run python` with `anndata` (>=0.11) installed in the project env; no API key or account required. |
| metadata | {"skill-author":"AlterLab","version":"1.0.0"} |
AnnData
Overview
AnnData is a Python package for handling annotated data matrices, storing experimental measurements (X) alongside observation metadata (obs), variable metadata (var), and multi-dimensional annotations (obsm, varm, obsp, varp, uns). Originally designed for single-cell genomics through Scanpy, it now serves as a general-purpose framework for any annotated data requiring efficient storage, manipulation, and analysis.
When to Use This Skill
Use this skill when:
- Creating, reading, or writing AnnData objects
- Working with h5ad, zarr, or other genomics data formats
- Performing single-cell RNA-seq analysis
- Managing large datasets with sparse matrices or backed mode
- Concatenating multiple datasets or experimental batches
- Subsetting, filtering, or transforming annotated data
- Integrating with scanpy, scvi-tools, or other scverse ecosystem tools
Installation
uv pip install anndata
uv pip install 'anndata[dask]'
Quick Start
Creating an AnnData object
import anndata as ad
import numpy as np
import pandas as pd
X = np.random.rand(100, 2000)
adata = ad.AnnData(X)
obs = pd.DataFrame({
'cell_type': ['T cell', 'B cell'] * 50,
'sample': ['A', 'B'] * 50
}, index=[f'cell_{i}' for i in range(100)])
var = pd.DataFrame({
'gene_name': [f'Gene_{i}' for i in range(2000)]
}, index=[f'ENSG{i:05d}' for i in range(2000)])
adata = ad.AnnData(X=X, obs=obs, var=var)
Reading data
import scanpy as sc
adata = ad.read_h5ad('data.h5ad')
adata = ad.read_h5ad('large_data.h5ad', backed='r')
adata = ad.io.read_csv('data.csv')
adata = ad.io.read_loom('data.loom')
adata = sc.read_10x_h5('filtered_feature_bc_matrix.h5')
API namespaces (anndata >= 0.11): all format readers/writers moved to the
anndata.io module (ad.io.read_csv, ad.io.read_mtx, ad.io.read_loom,
ad.io.read_elem, ...). The top-level ad.read_csv-style aliases still work
but emit a DeprecationWarning. Exceptions: ad.read_h5ad, ad.read_zarr,
adata.write_h5ad, and adata.write_zarr stay top-level with no warning.
10x readers (read_10x_h5, read_10x_mtx) live in scanpy
(sc.read_10x_h5), not anndata — this skill defers analysis-specific I/O to scanpy.
Writing data
adata.write_h5ad('output.h5ad')
adata.write_h5ad('output.h5ad', compression='gzip')
adata.write_zarr('output.zarr')
adata.write_csvs('output_dir/')
Basic operations
t_cells = adata[adata.obs['cell_type'] == 'T cell']
subset = adata[0:50, 0:100]
adata.obs['quality_score'] = np.random.rand(adata.n_obs)
adata.var['highly_variable'] = np.random.rand(adata.n_vars) > 0.8
print(f"{adata.n_obs} observations × {adata.n_vars} variables")
Core Capabilities
1. Data Structure
Understand the AnnData object structure including X, obs, var, layers, obsm, varm, obsp, varp, uns, and raw components.
See: references/data_structure.md for comprehensive information on:
- Core components (X, obs, var, layers, obsm, varm, obsp, varp, uns, raw)
- Creating AnnData objects from various sources
- Accessing and manipulating data components
- Memory-efficient practices
2. Input/Output Operations
Read and write data in various formats with support for compression, backed mode, and cloud storage.
See: references/io_operations.md for details on:
- Native formats (h5ad, zarr)
- Alternative formats (CSV, MTX, Loom, 10X, Excel)
- Backed mode for large datasets
- Remote data access
- Format conversion
- Performance optimization
Common commands:
adata = ad.read_h5ad('data.h5ad', backed='r')
adata.write_h5ad('output.h5ad', compression='gzip')
import scanpy as sc
adata = sc.read_10x_h5('filtered_feature_bc_matrix.h5')
adata = ad.io.read_mtx('matrix.mtx').T
3. Concatenation
Combine multiple AnnData objects along observations or variables with flexible join strategies.
See: references/concatenation.md for comprehensive coverage of:
- Basic concatenation (axis=0 for observations, axis=1 for variables)
- Join types (inner, outer)
- Merge strategies (same, unique, first, only)
- Tracking data sources with labels
- Lazy concatenation (AnnCollection)
- On-disk concatenation for large datasets
Common commands:
adata = ad.concat(
[adata1, adata2, adata3],
axis=0,
join='inner',
label='batch',
keys=['batch1', 'batch2', 'batch3']
)
adata = ad.concat([adata_rna, adata_protein], axis=1)
from anndata.experimental import AnnCollection
collection = AnnCollection(
['data1.h5ad', 'data2.h5ad'],
join_obs='outer',
label='dataset'
)
4. Data Manipulation
Transform, subset, filter, and reorganize data efficiently.
See: references/manipulation.md for detailed guidance on:
- Subsetting (by indices, names, boolean masks, metadata conditions)
- Transposition
- Copying (full copies vs views)
- Renaming (observations, variables, categories)
- Type conversions (strings to categoricals, sparse/dense)
- Adding/removing data components
- Reordering
- Quality control filtering
Common commands:
filtered = adata[adata.obs['quality_score'] > 0.8]
hv_genes = adata[:, adata.var['highly_variable']]
adata_T = adata.T
view = adata[0:100, :]
copy = adata[0:100, :].copy()
adata.strings_to_categoricals()
5. Best Practices
Follow recommended patterns for memory efficiency, performance, and reproducibility.
See: references/best_practices.md for guidelines on:
- Memory management (sparse matrices, categoricals, backed mode)
- Views vs copies
- Data storage optimization
- Performance optimization
- Working with raw data
- Metadata management
- Reproducibility
- Error handling
- Integration with other tools
- Common pitfalls and solutions
Key recommendations:
from scipy.sparse import csr_matrix
adata.X = csr_matrix(adata.X)
adata.strings_to_categoricals()
adata = ad.read_h5ad('large.h5ad', backed='r')
adata.raw = adata.copy()
adata = adata[:, adata.var['highly_variable']]
Integration with Scverse Ecosystem
AnnData serves as the foundational data structure for the scverse ecosystem:
Scanpy (Single-cell analysis)
AnnData is scanpy's native object — once built/loaded, pass it straight in.
Preprocessing, dimensionality reduction, clustering, and plotting
(sc.pp.normalize_total, sc.pp.highly_variable_genes, sc.pp.pca,
sc.pp.neighbors, sc.tl.umap, sc.tl.leiden, sc.pl.*) are scanpy's
job, not anndata's — defer the analysis workflow there.
import scanpy as sc
sc.pp.filter_cells(adata, min_genes=200)
Muon (Multimodal data)
import muon as mu
mdata = mu.MuData({'rna': adata_rna, 'protein': adata_protein})
PyTorch integration
from anndata.experimental import AnnLoader
dataloader = AnnLoader(adata, batch_size=128, shuffle=True)
for batch in dataloader:
X = batch["X"]
labels = batch["obs"]["cell_type"]
Common Workflows
Single-cell data lifecycle (the anndata-owned parts)
Load, compute simple QC metrics on obs/var, snapshot raw, subset, and write.
The normalize/log1p/HVG/cluster steps belong to scanpy — hand off there.
import anndata as ad
import scanpy as sc
adata = sc.read_10x_h5('filtered_feature_bc_matrix.h5')
adata.obs['n_genes'] = (adata.X > 0).sum(axis=1)
adata.obs['n_counts'] = adata.X.sum(axis=1)
adata = adata[(adata.obs['n_genes'] > 200) & (adata.obs['n_counts'] < 50000)].copy()
adata.raw = adata.copy()
adata = adata[:, adata.var['highly_variable']].copy()
adata.write_h5ad('processed.h5ad', compression='gzip')
Batch integration (concatenate, then defer correction)
adatas = [ad.read_h5ad(p) for p in ['batch1.h5ad', 'batch2.h5ad', 'batch3.h5ad']]
adata = ad.concat(
adatas,
label='batch',
keys=['batch1', 'batch2', 'batch3'],
join='inner',
)
Working with large datasets
adata = ad.read_h5ad('100GB_dataset.h5ad', backed='r')
high_quality = adata[adata.obs['quality_score'] > 0.8]
adata_subset = high_quality.to_memory()
process(adata_subset)
chunk_size = 1000
for i in range(0, adata.n_obs, chunk_size):
chunk = adata[i:i+chunk_size, :].to_memory()
process(chunk)
Troubleshooting
Out of memory errors
Use backed mode or convert to sparse matrices:
adata = ad.read_h5ad('file.h5ad', backed='r')
from scipy.sparse import csr_matrix
adata.X = csr_matrix(adata.X)
Slow file reading
Use compression and appropriate formats:
adata.strings_to_categoricals()
adata.write_h5ad('file.h5ad', compression='gzip')
adata.write_zarr('file.zarr', chunks=(1000, 1000))
Index alignment issues
Always align external data on index:
adata.obs['new_col'] = external_data['values']
adata.obs['new_col'] = external_data.set_index('cell_id').loc[adata.obs_names, 'values']
Additional Resources