跳转至

AnnData — 单细胞组学数据的标准容器格式


一句话说明

AnnData(Annotated Data)是 Scanpy 生态的核心数据格式,用一个对象同时存细胞×基因矩阵、细胞注释、基因注释和降维结果,是单细胞分析的"瑞士军刀数据盒"。


安装与配置

# pip 安装
pip install anndata                # 当前版本 0.12+

# 单细胞分析全套(含 scanpy)
pip install scanpy anndata

# conda 安装
conda install -c conda-forge anndata

# 验证
python -c "import anndata; print(anndata.__version__)"

核心用法

AnnData 结构说明

AnnData 对象结构:
   ┌─────────────────────────────┐
   │  X: 表达矩阵 (n_obs × n_vars)│  ← 主矩阵:细胞×基因 计数
   │  obs: 细胞注释 DataFrame     │  ← 行注释:细胞类型/样本等
   │  var: 基因注释 DataFrame     │  ← 列注释:基因名/染色体等
   │  obsm: 降维结果字典          │  ← umap/pca 坐标存这里
   │  obsp: 邻居图稀疏矩阵        │  ← 细胞-细胞关系图
   │  uns: 非结构化字典           │  ← 颜色/参数等杂项
   └─────────────────────────────┘

创建 AnnData

import anndata as ad
import numpy as np
import pandas as pd

# 创建计数矩阵(100个细胞,2000个基因)
n_cells, n_genes = 100, 2000
X = np.random.poisson(1, size=(n_cells, n_genes)).astype("float32")

# 细胞注释(行注释)
obs = pd.DataFrame({
    "cell_type": np.random.choice(["T细胞", "B细胞", "NK细胞"], n_cells),  # 细胞类型
    "n_genes":   (X > 0).sum(axis=1),     # 检测到的基因数
}, index=[f"Cell_{i}" for i in range(n_cells)])

# 基因注释(列注释)
var = pd.DataFrame({
    "gene_name": [f"Gene_{i}" for i in range(n_genes)],   # 基因名
    "n_cells":   (X > 0).sum(axis=0),                      # 表达该基因的细胞数
}, index=[f"ENSG{i:08d}" for i in range(n_genes)])

# 组装 AnnData
adata = ad.AnnData(X=X, obs=obs, var=var)
print(adata)  # AnnData object with n_obs × n_vars = 100 × 2000

基本操作

# 访问数据
print(adata.X.shape)           # (100, 2000):主矩阵
print(adata.obs.head())        # 细胞注释表格前5行
print(adata.var_names[:5])     # 前5个基因名

# 子集选择(保留结构)
t_cells = adata[adata.obs["cell_type"] == "T细胞"]  # 选 T 细胞
print(t_cells.shape)           # (子集细胞数, 2000)

# 添加降维结果
adata.obsm["X_pca"]  = np.random.rand(n_cells, 50)  # PCA 结果放 obsm
adata.obsm["X_umap"] = np.random.rand(n_cells, 2)   # UMAP 结果放 obsm

# 添加全局信息
adata.uns["project"] = "肠道菌群宏基因组"    # 非结构化信息

实战案例

读写 h5ad 文件

# 保存为 h5ad(HDF5-based)
adata.write_h5ad("single_cell.h5ad")

# 读取
adata = ad.read_h5ad("single_cell.h5ad")

# 只读部分数据(大文件)
adata_lazy = ad.read_h5ad("large.h5ad", backed="r")  # 延迟加载
subset = adata_lazy[:1000, :500]                      # 只读前1000细胞

与 Scanpy 集成

import scanpy as sc

# 标准单细胞流程(每步结果自动存入 adata)
sc.pp.filter_cells(adata, min_genes=200)   # 过滤低质量细胞
sc.pp.normalize_total(adata, target_sum=1e4)  # 归一化到1万计数
sc.pp.log1p(adata)                         # log1p 变换
sc.pp.highly_variable_genes(adata)         # 找高变基因
sc.tl.pca(adata)                           # PCA 降维(存入 obsm["X_pca"])
sc.tl.umap(adata)                          # UMAP(存入 obsm["X_umap"])
sc.pl.umap(adata, color="cell_type")       # 可视化

常见报错与解决

报错原因解决
ImplicitModificationWarning修改切片而非原始对象.copy() 后再修改
KeyError 访问 obsm键名不存在print(adata.obsm.keys()) 查看
写 h5ad 很慢矩阵未压缩adata.X = scipy.sparse.csr_matrix(adata.X)
TypeError: 稠密矩阵X 不是稀疏矩阵转稀疏:from scipy.sparse import csr_matrix

速查表

操作代码
主矩阵adata.X
细胞注释adata.obs
基因注释adata.var
降维结果adata.obsm["X_umap"]
按细胞过滤adata[adata.obs["type"]=="T"]
保存adata.write_h5ad("f.h5ad")
读取ad.read_h5ad("f.h5ad")
转稀疏矩阵scipy.sparse.csr_matrix(adata.X)