同事传来一个已经分群的单细胞对象。你想查某个细胞的一个基因究竟测到了几个分子,打开主矩阵,看到的是带小数的 7.60。翻到另一个层又变成 2。再画一张基因表达图,颜色却好像来自第三套数值。单细胞对象可以同时保存多个处理阶段的表达数据,要回答“检测到几个分子”,得先查这个数是从哪一层取的,以及保存这一层之前做过什么。
这里从一个已经通过基础质控、尚未归一化的计数矩阵开始。这个前提很重要。如果收到的文件只有标准化后的表达值,把那一层改名为“计数”不会带回原始分子数。尤其在公开数据库项目中,应先阅读作者的处理说明,确认文件是原始计数、文库归一化表达还是已按基因中心化的值。单靠文件后缀识别不了这些差别。
先把三个位置的含义弄明白
adata.X 是当前主矩阵。很多预处理函数默认修改它,因此它的含义会随操作改变。adata.layers 是可以命名的平行矩阵集合,例如把一份原始分子计数保存在名为 counts 的层中。一个普通层在细胞与基因两个维度上应与当前主矩阵匹配。名字由创建对象的人决定,叫 counts 并不自动证明其中是整数分子计数。AnnData 的 layers 定义
adata.raw 容易引起另一种误会。它保存的是你设置这个快照时的表达矩阵和特征信息。若在归一化和取对数之后才保存,读出来的就是那个阶段的值。还有一个很实用的差异,对原对象按细胞切片时,快照中的细胞也会随之切片。对当前矩阵按基因切片时,先前快照里保留的其他基因仍可能可以读取。因此,从主矩阵里找不到某个标记,先检查基因是否在快照以及最初的参考注释中,再判断是基因命名不匹配还是确实被过滤。AnnData 对 raw 快照与切片的说明
用两个细胞把 7.60 算出来
假设甲细胞总共计到十个分子,目标基因占两个。乙细胞总共计到二十个分子,目标基因也占两个。把每个细胞的总量归一到一万,甲的目标基因变为两千,乙的变为一千。接着取自然对数形式的 log1p,得到约 7.60 和 6.91。同一个原始计数之所以显示出不同颜色,原因就藏在两个细胞不同的分母里。反过来,较高的对数表达值也不能单独说明它测到了更多目标分子。
| 教学细胞 | 原始计数 / 全细胞总量 | 归一化至一万 | 再取 log1p |
|---|---|---|---|
| 甲 | 2 / 10 | 2,000 | 7.60 |
| 乙 | 2 / 20 | 1,000 | 6.91 |

算完这两行再看真实对象,记住这里明确指定了目标总量,并假定用所有特征计算归一化因子。若在归一化前先删除大量基因,或启用排除高表达基因的选项,用作分母的总量可能改变。同一份数据在两个项目里出现不同数值,先确认这些细节比先调颜色上限有效。某个细胞如果本来就是空列,也没有值得比较的表达组成,应该回到质控环节说明其去留。Scanpy 对总量归一化和可选参数的说明
实跑经验|写入磁盘再读回,看看目标基因究竟还在哪一层
在 Python 3.12.14 中安装 Scanpy 1.12.6 和 AnnData 0.13.4。用两枚细胞、每枚两个基因的整数稀疏矩阵实际运行总量归一化和取对数,目标基因输出为 7.601402 与 6.908755,保存的计数层仍分别为 2 和 2。随后把归一化后的完整对象存入 raw,主对象只留下背景基因,写成 h5ad,再从磁盘重新读取。此时主矩阵的形状为两行一列,raw 的形状仍是两行两列,从快照里按名字取回目标基因,结果与切片前完全相同。
这个小实验能帮忙诊断两种真实问题。若按主矩阵找不到基因,先看是不是在高变基因筛选时被移走,再看快照建立在筛选之前还是之后。若快照里确实找到了该基因,还要读取一枚细胞的具体数值,确认它是计数还是对数表达。一次写盘、读回、按名字取数,比仅仅打印对象中是否存在 raw 字段更能说明交付文件里保留了什么。
用可复查的顺序保存自己的对象
下面几行只适用于已确认主矩阵存放原始计数的对象。先复制一份原始计数,再处理主矩阵,最后把处理后的完整基因表达设为快照。复制矩阵是为了让后续改动不影响已经保存的数据。如果你需要仅以高变基因计算降维,可以在这一步之后再对子对象筛选基因,并在文件说明里记下这一选择。
import scanpy as sc
adata.layers["counts"] = adata.X.copy()
sc.pp.normalize_total(adata, target_sum=1e4)
sc.pp.log1p(adata)
adata.raw = adata.copy()
print(list(adata.layers.keys()), adata.shape, adata.raw.shape)
拿到别人做好的对象,不妨选一个细胞、两个基因,沿着各层追踪坐标。查看 obs_names 和 var_names 后按名字取数,避免直接假设两个文件中的行号相同。先找一个有明显非零表达的标记,再找一个低表达标记。前者帮助核对变换的数量级,后者能发现显示时被隐去的弱信号。若主矩阵出现负数,一个可能原因是后来进行了中心化或其他变换,应追查分析记录。仅凭“全是整数”或“存在小数”都不足以恢复对象历史。
还可以做一项很便宜的反查。在当初的全基因计数矩阵里,用同一个细胞计算行和,再用目标基因的整数计数除以这个行和,按处理说明重算对数值。如果多数基因都对得上而少数坐标对不上,先核对特征编号和重复基因名。如果同一细胞的许多值同时偏移,则更像是使用了不同的归一化分母。这个顺序能帮你决定先找注释文件还是先翻预处理脚本。
画标记图和做检验时,主动指定来源
如果画图的任务是显示归一化后的标记表达,应在方法说明中写清它来自快照、主矩阵还是某个指定层。遇到两个图不一致,可以先强制使用同一个数据来源,再比较分组、色阶以及筛掉的细胞。一个典型陷阱是先在主矩阵上做了基因缩放,却让绘图函数自动选择仍然存在的旧快照。视觉差异有时是因为比较对象不同,不能只靠调色解决。
做标记基因排序也有具体的输入约定。Scanpy 的 rank_genes_groups 文档要求使用对数化数据,并说明 use_raw 和 layer 如何选择。将原始整数计数层直接传给一个预期对数表达的流程,输出表格可能照样生成,但其排序和效应量解释会偏离预期。若实际要比较独立供体的处理效应,应单独设计按生物学样本汇总的比较,保留原始计数和供体标签。不要把分群后的细胞当成独立个体来增加实验重复数。Scanpy 标记基因排序的输入说明
交付对象时,给每个表达层写一句来历,再附上一个细胞与一个基因在各层的实际读数。接手人能用这条记录验证文件是否读对,也能知道哪些值适合求和、哪些值适合画图。若团队要让同一份图谱和处理脚本长期由多人复查,可以随对象保存依赖版本。需要公共分析环境时,可了解云生信的生物信息分析服务器。
