Quiet
  • 主页
  • 归档
  • 分类
  • 标签
  • 链接
  • 关于我

bajiu

  • 主页
  • 归档
  • 分类
  • 标签
  • 链接
  • 关于我
Quiet主题

AnnData数据结构

bajiu
生物信息学

2025-08-18 11:12:00

什么是 AnnData

AnnData(Annotated Data) 是一个为“矩阵 + 丰富注释”而设计的 Python 容器,是 Scanpy/Squidpy、许多单细胞/空间转录组工具的核心数据结构。

它用一个形状为 (n_obs, n_vars) 的矩阵(通常是 细胞 × 基因)作为主体,并把与行、列、矩阵相关的一切元信息整齐地放在命名的“抽屉”里,保证对齐与可追溯。

数据模型总览

  • .X:主体矩阵(通常存放当前分析用的表达矩阵)。可为 numpy.ndarray 或 scipy.sparse。
  • .obs:行(细胞)的表格注释,pandas.DataFrame,索引是细胞 ID。
  • .var:列(基因)的表格注释,pandas.DataFrame,索引是基因 ID。
  • .obsm:行方向的多维嵌入/坐标(如 UMAP、t-SNE、空间坐标),每个键是一块 n_obs × k 的数组,例如 obsm['X_umap']、obsm['spatial']。
  • .varm:列方向的多维特征(如 PCA 的基因载荷),每个键是 n_vars × k。
  • .obsp:行方向的方阵(n_obs × n_obs),如邻接图 connectivities、距离矩阵 distances。
  • .varp:列方向的方阵(n_vars × n_vars),如基因-基因相关性。
  • .layers:与 .X 同形状的多版本矩阵层(如 counts、log1p、normalized)。
  • .uns:无结构字典,存放分析元数据、绘图参数、结果摘要(如 uns['pca']、uns['neighbors']、uns['spatial'])。
  • .raw:一个只读快照(通常保存“原始计数 + 当时的 var”),用于差异分析等需回溯原始表达的场景。

记忆法:obs/var 是表格注释,obsm/varm 是多维数组,obsp/varp 是方阵图,layers 是多版本矩阵,uns 是杂项/配置。

最小工作样例

import numpy as np
import pandas as pd
import scipy.sparse as sp
import anndata as ad

# 假造 3 个细胞 × 5 个基因的 count 矩阵(稀疏)
X_counts = sp.csr_matrix(np.array([
    [0, 3, 0, 1, 0],
    [2, 0, 0, 0, 5],
    [0, 1, 0, 0, 0],
], dtype=np.float32))

adata = ad.AnnData(
    X=X_counts,                          # 主矩阵(当前版本)
    obs=pd.DataFrame(index=['cell1','cell2','cell3']),
    var=pd.DataFrame(index=['GeneA','GeneB','GeneC','GeneD','GeneE'])
)

# 加注释
adata.obs['sample'] = ['S1','S1','S2']
adata.var['mt']     = [False, False, True, False, False]  # 例:线粒体基因标记

# 存层:保存原始计数
adata.layers['counts'] = adata.X.copy()

# 做一个“归一化后的表达”版本进 .X(示例,真实请用 Scanpy API)
X_norm = adata.layers['counts'].astype(np.float32)
libsize = np.asarray(X_norm.sum(1)).flatten()
X_norm = X_norm.multiply(1e4 / np.maximum(libsize, 1))     # CPM-like
adata.X = X_norm

# 嵌入 / 坐标
adata.obsm['X_umap'] = np.random.randn(adata.n_obs, 2).astype(np.float32)

# 写盘
adata.write_h5ad('demo.h5ad', compression='gzip')

读取与存储(h5ad / zarr / 10x 等)

  • h5ad(最常用,单文件):
import scanpy as sc
adata = sc.read_h5ad('data.h5ad')              # 读
adata.write_h5ad('data_out.h5ad', compression='gzip')  # 写
  • Zarr(分块、云端友好):
adata.write_zarr('data.zarr')
adata = ad.read_zarr('data.zarr')
  • 10x:
adata = sc.read_10x_mtx('filtered_feature_bc_matrix/')  # 目录
# 或 sc.read_10x_h5('filtered_feature_bc_matrix.h5')
  • 大数据 “背后读取”(backed):适合巨型 h5ad 的只读/有限写场景
adata = sc.read_h5ad('big.h5ad', backed='r')   # 'r' 只读,'r+' 可就地写
# 注意:backed 模式下许多操作受限,需小心/分步处理

切片、对齐与视图(非常重要)

  • 基本切片
ad_small = adata[adata.obs['sample']=='S1', ['GeneA','GeneB']].copy()
  • 视图 VS 副本:adata[…] 默认产生视图(view);在其上赋值会触发 setting on view 的警告。
    规范做法:切片后立刻 .copy() 再改。

  • 任何 .obs/.var/.obsm/... 与 .X 都按索引严格对齐。

例如你重排了 adata = adata[sorted_idx, :],.obsm['X_umap'] 会同步重排。

常见抽屉的典型内容与约定

  1. .X 与 .layers
  • 建议把原始计数放入 layers['counts'],把当前分析用矩阵放 .X。
  • 规范管线常见层命名:
    • layers['counts']:原始 UMI(int/float,稀疏 CSR/CSC)
    • layers['normalized']:size-factor/CPM 归一化后
    • layers['log1p']:log1p(normalized) 结果
  • 好处:你可以在不同分析步骤间切换基质而不丢信息:
adata.X = adata.layers['log1p']
  1. .obs / .var
  • .obs 举例:cluster、sample、batch、percent_mt、n_genes_by_counts 等。
  • .var 举例:gene_symbol、mt(是否线粒体基因)、highly_variable 等。
  • 类别型列请转为 category,节省内存并利于分组:
adata.obs['cluster'] = adata.obs['cluster'].astype('category')
  1. .obsm / .varm
  • .obsm['X_pca']、.obsm['X_umap']、.obsm['spatial'](Visium 二维坐标)。
  • .varm['PCs']:PCA 的基因载荷(n_vars × n_pcs)。
  • 约定俗成:低维嵌入用键名以 X_ 开头(如 X_umap/X_tsne)。
  1. .obsp / .varp
  • .obsp['connectivities'] & .obsp['distances']:邻居图(n_obs×n_obs)。
  • 基因网络可放 .varp['correlation'] 等。
  1. .uns
  • 保存步骤配置、颜色表、统计摘要、空间影像信息等:
    • uns[‘neighbors’]:邻居图的元参数
    • uns[‘pca’]:PCA 的方差解释等
    • uns[‘spatial’]:Visium/Squidpy 使用的 切片图像 与 缩放参数
    • uns[‘rank_genes_groups’]:差异分析结果及其元数据
  1. .raw
    常用于 把原始(或标准化但未 log)表达 + var 快照 冻结起来:
adata.raw = adata           # 这会把当前 .X 和 .var 存为只读快照

有些新工作流更提倡用 layers['counts'] 等分层管理,raw 主要用于兼容传统函数或需要只读对照的场景。

与 Scanpy 的常见配合

import scanpy as sc

# 1) 质控统计
sc.pp.calculate_qc_metrics(adata, qc_vars=['mt'], percent_top=None, log1p=False, inplace=True)

# 2) 归一化 + log1p(保留 counts)
adata.layers['counts'] = adata.X.copy()
sc.pp.normalize_total(adata, target_sum=1e4)  # 改写 .X
sc.pp.log1p(adata)                            # 改写 .X -> log1p
adata.layers['log1p'] = adata.X.copy()

# 3) HVG
sc.pp.highly_variable_genes(adata, flavor='seurat_v3', n_top_genes=3000)
adata = adata[:, adata.var['highly_variable']].copy()

# 4) 标准化、PCA、邻居、UMAP
sc.pp.scale(adata, zero_center=True, max_value=10)
sc.tl.pca(adata, svd_solver='arpack')
sc.pp.neighbors(adata, n_neighbors=15, n_pcs=30)
sc.tl.umap(adata)

空间转录组特别提示(Visium/Squidpy)

  • 空间坐标:adata.obsm['spatial'] = (n_obs × 2) 的像素/坐标。
  • 切片影像与缩放:adata.uns['spatial'][library_id]['images'][...] 及 ['scalefactors']。
  • 分辨率/坐标系由 Squidpy/Scanpy 读入函数写入,绘图/邻域分析都会读这些键。

拼接与合并(多样本/批次)

  • 推荐用 anndata.concat(或 scanpy.concat),而非旧的 AnnData.concatenate:
import anndata as ad
adata_all = ad.concat(
    [adata1, adata2, adata3],
    join='outer',               # 控制 var 对齐策略:'outer'/'inner'
    label='batch',              # 在 .obs 新增列记录来源
    keys=['S1','S2','S3'],
    index_unique='-'
)
  • 变量(基因)对齐策略至关重要:
    • join='inner':取交集,适合不同平台但想要严格可比的基因集
    • join='outer':取并集,不存在处补零(稀疏矩阵友好)

常用 API 速查

# 形状与索引
adata.shape; adata.n_obs; adata.n_vars
adata.obs_names[:5]; adata.var_names[:5]

# 新增/修改注释
adata.obs['cluster'] = cluster_labels.astype('category')
adata.var['mt'] = adata.var_names.str.upper().str.startswith('MT-')

# 选择细胞/基因
adata_sub = adata[adata.obs['cluster']=='T', :].copy()
adata_genes = adata[:, ['CD3D','CD3E','MS4A1']].copy()

# 层的切换
adata.layers['counts'] = adata.X.copy()
adata.X = adata.layers['log1p']

# 嵌入与邻接
adata.obsm['X_umap'].shape
adata.obsp['connectivities'].shape

# 读写
adata.write_h5ad('out.h5ad', compression='gzip')
adata2 = sc.read_h5ad('out.h5ad')

常见坑与排雷清单

  • “setting on view” 警告:切片后马上 .copy() 再改。
  • 基因/细胞名不唯一:请先 var_names_make_unique() / obs_names_make_unique()。
  • 混用密集/稀疏类型:下游函数可能假设稀疏;统一为 csr_matrix 更稳。
  • 把 Log 值当作计数:请将不同版本放入 layers,并在分析函数中明确指定来源。
  • raw 与 layers 混乱:要么规范使用 raw,要么全用 layers 管理,避免两套并行而互相覆盖。
  • 连接(concat)忘了 join 策略:默认不一定是你要的;跨平台数据尤其要确认。
  • 空间数据键名不匹配:确保 obsm['spatial'] 与 uns['spatial'] 结构完整,否则绘图/邻域计算会失败。

小型工作流模板

import scanpy as sc

# 读取
adata = sc.read_10x_mtx('filtered_feature_bc_matrix/')  # or read_h5ad

# 预处理
adata.var_names_make_unique()
sc.pp.calculate_qc_metrics(adata, qc_vars=['mt'], inplace=True)

# 保存 counts
adata.layers['counts'] = adata.X.copy()

# 归一化 + log1p (更新 .X)
sc.pp.normalize_total(adata, target_sum=1e4)
sc.pp.log1p(adata)
adata.layers['log1p'] = adata.X.copy()

# HVG
sc.pp.highly_variable_genes(adata, n_top_genes=3000, flavor='seurat_v3')
adata = adata[:, adata.var['highly_variable']].copy()

# 标准化与降维
sc.pp.scale(adata, max_value=10)
sc.tl.pca(adata, n_comps=50)
sc.pp.neighbors(adata, n_neighbors=15, n_pcs=30)
sc.tl.umap(adata)

# 写盘
adata.write_h5ad('analysis.h5ad', compression='gzip')
上一篇

pnpm搭建Monorpeo项目

下一篇

空间转录组简介

©2025 By bajiu.