Scanpy AnnData 对象深度解析:高效操作数据结构的10个技巧

AnnData 是 Scanpy 的核心数据结构,相当于单细胞分析里的"万能容器"。很多初学者只会基础操作,但掌握这些进阶技巧能让你的分析效率提升一个数量级。


AnnData 结构回顾

csharp 复制代码
AnnData (n_obs × n_vars)
├── .X          → 主数据矩阵(细胞 × 基因)
├── .obs         → 细胞元数据(DataFrame)
├── .var         → 基因元数据(DataFrame)
├── .obsm        → 细胞多维嵌入(PCA, UMAP...)
├── .obsp        → 细胞间距离/图(kNN graph...)
├── .varm        → 基因多维嵌入
├── .uns         → 非结构化数据(颜色、参数等)
└── .layers      → 多个数据矩阵(raw, normalized...)

技巧1:用 layers 保存多个数据状态

ini 复制代码
import scanpy as sc
import numpy as np
​
adata = sc.read_10x_h5("sample.h5")
​
# 保存原始 counts
adata.layers["counts"] = adata.X.copy()
​
# 标准化
sc.pp.normalize_total(adata, target_sum=1e4)
adata.layers["normalized"] = adata.X.copy()
​
# 对数变换
sc.pp.log1p(adata)
adata.layers["log1p"] = adata.X.copy()
​
# 随时切换
adata.X = adata.layers["counts"]   # 切回原始数据
adata.X = adata.layers["log1p"]    # 切回对数变换数据

技巧2:批量操作 obs 元数据

csharp 复制代码
# 添加多个元数据列
import pandas as pd
​
# 方法一:直接赋值
adata.obs["sample_id"] = "sample_01"
adata.obs["condition"] = "control"
​
# 方法二:从外部文件合并
meta_df = pd.read_csv("sample_metadata.csv", index_col=0)
adata.obs = adata.obs.join(meta_df, how="left")
​
# 方法三:基于现有列计算新列
adata.obs["high_mt"] = adata.obs["pct_counts_mt"] > 20
adata.obs["cell_quality"] = np.where(
    adata.obs["high_mt"], "low", "high"
)

技巧3:高效切片和子集

ini 复制代码
# 布尔索引
adata_filtered = adata[adata.obs["n_genes_by_counts"] > 500].copy()
​
# 按类别筛选
adata_cd4 = adata[adata.obs["cell_type"] == "CD4+ T cell"].copy()
​
# 多条件组合
mask = (
    (adata.obs["total_counts"] > 1000) &
    (adata.obs["pct_counts_mt"] < 20) &
    (adata.obs["n_genes_by_counts"] > 500)
)
adata_clean = adata[mask].copy()
​
# 按基因筛选
hvg_genes = adata.var_names[adata.var["highly_variable"]]
adata_hvg = adata[:, hvg_genes].copy()

技巧4:合并多个 AnnData 对象

python 复制代码
import anndata as ad
​
# 拼接多个样本
adatas = []
for sample_id in ["s1", "s2", "s3"]:
    tmp = sc.read_10x_h5(f"{sample_id}/filtered_feature_bc_matrix.h5")
    tmp.obs["sample_id"] = sample_id
    # 确保 cell barcode 唯一
    tmp.obs_names = [f"{sample_id}_{bc}" for bc in tmp.obs_names]
    adatas.append(tmp)
​
adata_merged = ad.concat(adatas, join="outer", fill_value=0)
print(f"合并后: {adata_merged.shape}")

技巧5:读写优化

ini 复制代码
# 保存为 h5ad(推荐格式,读写最快)
adata.write_h5ad("analysis.h5ad", compression="gzip")
​
# 只读取部分数据(大文件神器)
adata_partial = sc.read_h5ad("analysis.h5ad", backed="r")
# backed模式下,.X 是内存映射,不会全部加载进内存
​
# 读取特定列的 obs
import h5py
with h5py.File("analysis.h5ad", "r") as f:
    cell_types = f["obs/cell_type"][:]

技巧6:obsm 存储降维结果

scss 复制代码
# 标准嵌入键名
print(adata.obsm.keys())
# odict_keys(['X_pca', 'X_umap', 'X_tsne', 'X_harmony'])
​
# 手动添加自定义嵌入
from sklearn.decomposition import NMF
nmf = NMF(n_components=20)
adata.obsm["X_nmf"] = nmf.fit_transform(adata.X.toarray())
​
# 使用自定义嵌入做 UMAP
sc.pp.neighbors(adata, use_rep="X_nmf")
sc.tl.umap(adata)

技巧7:uns 存储分析参数

bash 复制代码
# uns 用来记录分析过程中的重要参数
adata.uns["analysis_params"] = {
    "qc_min_genes": 500,
    "qc_max_mt_pct": 20,
    "n_hvg": 3000,
    "n_pca": 50,
    "n_neighbors": 15,
    "leiden_resolution": 0.5
}
​
# 查看颜色配置
print(adata.uns.get("leiden_colors", "未设置"))

技巧8:快速查看数据摘要

python 复制代码
# 打印 AnnData 摘要
print(adata)
​
# 详细统计
def adata_summary(adata):
    print(f"细胞数: {adata.n_obs:,}")
    print(f"基因数: {adata.n_vars:,}")
    print(f"obs 列: {list(adata.obs.columns)}")
    print(f"obsm 键: {list(adata.obsm.keys())}")
    print(f"layers: {list(adata.layers.keys())}")
    if adata.X is not None:
        import scipy.sparse as sp
        if sp.issparse(adata.X):
            print(f"稀疏度: {1 - adata.X.nnz / (adata.n_obs * adata.n_vars):.2%}")
​
adata_summary(adata)

技巧9:复制 vs 视图

ini 复制代码
# 切片默认返回视图,修改可能影响原数据
view = adata[adata.obs["condition"] == "control"]
print(view.is_view)  # True
​
# 用 .copy() 获取独立副本
copy = adata[adata.obs["condition"] == "control"].copy()
print(copy.is_view)  # False
​
# 规则:需要修改数据时,先 .copy()

技巧10:与 pandas/numpy 无缝互操作

ini 复制代码
# obs 就是 pandas DataFrame
df_obs = adata.obs  # 直接就是 DataFrame
grouped = df_obs.groupby("cell_type")["total_counts"].mean()
​
# var 也是 DataFrame
high_expr_genes = adata.var[adata.var["mean_counts"] > 1].index.tolist()
​
# .X 转 numpy(小数据集可以这样)
matrix_np = adata.X.toarray()  # 稀疏→密集
​
# 反向操作
import scipy.sparse as sp
adata.X = sp.csr_matrix(matrix_np)  # 密集→稀疏

掌握这 10 个技巧,AnnData 操作效率能提升至少 3 倍。

相关推荐
颜酱3 小时前
LangChain调用向量模型,存入向量数据库
python·langchain
2501_928945523 小时前
七本性全面签名体系:从互递归类型到∞-范畴生成语法
python
2601_961194024 小时前
考研资料电子版|去哪找|网盘
java·c语言·c++·python·考研·php
veminhe4 小时前
关于下载pip install faiss-cpu失败的问题
python·pip·faiss
战族狼魂4 小时前
从零构建企业级Hermes-Agent:复杂任务拆解、工具协同与安全落地实践
开发语言·人工智能·python
belong_my_offer4 小时前
可视化各种库的用法并区分其作用
python
weixin_439857544 小时前
短剧MP4合并器
python·mp4合并·短剧合并
李可以量化4 小时前
量化之MiniQMT 实战:一键读取通达信自选股并实时监控涨跌幅(附完整可运行代码)
开发语言·python·量化·qmt·ptrade
CTA量化套保5 小时前
一个账户跑多个期货策略:仓位与报单隔离思路
python·区块链