读取数据:三个核心函数
好,到了真正动手的环节了。
数据读取,是所有分析的起点。这一步如果错了,后面全是白费功夫。我个人习惯在项目一开始就花几分钟确认数据格式,而不是跑了几十个步骤才发现读进来的数据是空的——这种坑我踩过不止一次。
今天咱们就聊透 Scanpy 的三个主力读取函数:read_10x_mtx、read_h5ad 和 read_csv。每个函数长什么样,参数怎么配,什么时候用哪个,我给你掰扯清楚。
4.1 家族图谱:数据从哪里来
在读代码之前,先让你对这三个函数的关系有个整体感觉。下面这张图总结了它们各自服务的场景:
4.2 read_10x_mtx:标准 10X 数据读取
做单细胞分析,10X Genomics 的数据格式基本是标配。你从 Cell Ranger 拿到的输出目录里,通常会有一个 filtered_feature_bc_matrix 文件夹。里面有三个文件:matrix.mtx、barcodes.tsv 和 features.tsv。
read_10x_mtx 就是专门吃这种结构的。你用不着手动拼文件路径,它自己会去目录里找。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| path | str | 必填 | 目录路径,指向包含三个文件的文件夹 |
| var_names | str | "gene_symbols" | 选 gene_ids 还是 gene_symbols 作为变量名 |
| make_unique | bool | True | 是否自动去重重复的基因名 |
| cache | bool | False | 是否缓存原始数据,节省下次读取时间 |
| gzipped | bool | None | 自动检测文件是否 gzip 压缩 |
import scanpy as sc
adata = sc.read_10x_mtx(
path="data/filtered_feature_bc_matrix/",
var_names="gene_symbols",
make_unique=True
)
print(adata)
# Output: AnnData object with n_obs × n_vars = 5000 × 33694
# 错误示范:自己拼接文件路径
adata = sc.read_10x_mtx("data/matrix.mtx") # 不要这样做!
# 正确做法:给目录路径
adata = sc.read_10x_mtx("data/filtered_feature_bc_matrix/")
函数会自动去找 matrix.mtx。你想想看,如果每次都手动拼文件名,换一个批次就得改代码,多容易出错。
还有一个细节:var_names 选 "gene_symbols" 还是 "gene_ids"?我推荐前者,因为后续画图、查找 marker 时,人类可读的基因名比 ENSEMBL ID 直观太多。
4.3 read_h5ad:加载已处理数据
如果你手头有一份 .h5ad 文件,那真是最省心的格式了。h5ad 是 AnnData 的原生持久化格式,里面把矩阵、观测信息、变量信息、非结构化元数据全都打包了。说白了,读进来就直接是一个完整的 AnnData 对象,什么都不用再配。
adata = sc.read_h5ad("path/to/processed_data.h5ad")
print(adata)
# Output: AnnData object with n_obs × n_vars = 15000 × 2000
# obs: 'cell_type', 'sample_id', 'n_genes'
# var: 'highly_variable'
# uns: 'pca', 'umap', 'neighbors'
你可能会问:那什么时候用 read_10x_mtx,什么时候用 read_h5ad?我一般这样区分:
| 场景 | 推荐函数 | 理由 |
|---|---|---|
| 从 Cell Ranger 原始输出开始 | read_10x_mtx | 直接读目录,保留完整变量信息 |
| 加载中间处理结果 | read_h5ad | 已经过质控、标准化,层次化组织 |
| 与其他工具、合作者交换数据 | read_h5ad | 单一文件,可自包含所有信息 |
| 只想快速预览数据 | read_h5ad | 一行代码,零配置 |
4.4 read_csv:灵活导入外部表格
现实工作中,数据格式并不总是那么标准。有时候你会拿到第三方平台导出的 CSV、TSV,或者某个实验室自己整理的表达矩阵。read_csv 就是为你提供后门的。
import pandas as pd
import scanpy as sc
# 先读成 DataFrame
df = pd.read_csv("my_data.csv", index_col=0)
# 再转成 AnnData
adata = sc.AnnData(X=df.values,
obs=pd.DataFrame(index=df.index),
var=pd.DataFrame(index=df.columns))
print(adata.shape)
# Output: (8000, 30000)
嗯,这里要注意,read_csv 本身只能读外部 CSV 后构造 AnnData,不能一步到位。不过我个人更喜欢先用 pandas 看看数据质量,再决定要不要转。
read_csv 的参数其实不多,因为它本质上是给 pd.read_csv 搭了个桥:
| 参数 | 说明 |
|---|---|
| filename | 文件路径 |
| delimiter | 分隔符,默认制表符(TSV),CSV 时用逗号 |
| first_column_names | 第一列是否当作列名 |
| backed | 是否以 backed 模式读取(不全部载入内存) |
4.5 实战对比:我该怎么选?
为了让你更直观地记住区别,我把三个函数的核心特征拉了个表:
| 维度 | read_10x_mtx | read_h5ad | read_csv + 构造 |
|---|---|---|---|
| 输入格式 | 目录(含三个标准文件) | 单个 .h5ad 文件 | 任意 CSV/TSV 表格 |
| 自动解析 | 自动读取矩阵、barcode、feature | 完整恢复 AnnData | 需要手动指定索引/列名 |
| 数据完整性 | 只含原始计数 | 可包含 obs/var/uns 等 | 取决于导出方 |
| 使用频次 | 项目初期 | 项目中期及二次加载 | 特殊对接场景 |
| 加载速度 | 较快 | 极快 | 较慢(依赖 pandas) |
| 适合人群 | 所有单细胞操作者 | 任何有 .h5ad 的场景 | 有第三方数据的人 |
- 如果你的数据量很大(比如几十万细胞),考虑用
backed='r'模式,这样读取时不会一下子吃掉所有内存。 - 每次读完数据,我都习惯先打印
adata,瞄一眼n_obs和n_vars。如果数字是 0,嗯,八成是路径错了。 - 从 10X 读取后,可以顺手
adata.var_names_make_unique(),防止后续报重名错误。