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) |