4. 数据导入:从10X格式、h5ad或CSV读取数据
数据导入这一步,看着简单,其实坑不少。
我当年刚用Scanpy时,遇到过读进来的矩阵维度对不上,找了好久才发现是文件路径写错了。
今天咱们就把三种主流格式——10X格式、h5ad、CSV——的读取方法捋清楚,顺便聊聊我踩过的坑。
四种常见格式,你该用哪个?
Scanpy支持的数据格式远不止这三种,但实战中90%的场景都在它们之间切换。
我个人习惯按这个优先级选:
- h5ad:首选,原生Anndata格式,读写快,保留所有信息。
- 10X格式:最常用,Cell Ranger输出就是这个,需要三个文件。
- CSV/TSV:老格式,兼容性强,但性能差、易丢元数据。
- 其他(如HDF5、MTX):极少用,暂不展开。
那具体怎么读?下面逐个拆解。
读取10X格式:三件套别缺了
10X Genomics的Cell Ranger管道会输出三个文件:
matrix.mtx(或matrix.mtx.gz):稀疏矩阵,包含非零计数。barcodes.tsv(或barcodes.tsv.gz):细胞条形码列表。features.tsv(或genes.tsv):基因列表,第三列是特征类型(如Gene Expression)。
Scanpy用sc.read_10x_mtx一步搞定:
import scanpy as sc
adata = sc.read_10x_mtx(
"path/to/filtered_feature_bc_matrix/", # 包含三个文件的目录
var_names='gene_symbols', # 用基因符号作为变量名
cache=True # 缓存解析结果,下次秒开
)
print(adata)
matrix.mtx等文件。如果文件名不同(比如后缀.gz),它也认得。
我记得有一次同事把filtered_feature_bc_matrix文件夹直接放到了path/to下,结果读进来全是空矩阵。后来发现目录名少写了一层……嗯,路径层级一定要和Cell Ranger输出一致,否则会抛FileNotFoundError。
如果你拿到的是Gene Expression + Antibody Capture的联合产物,features.tsv第三列会标注“Antibody Capture”。Scanpy默认只读“Gene Expression”部分,如果想保留所有特征,需要加参数:
adata = sc.read_10x_mtx(
"path/to/raw_feature_bc_matrix/",
gex_only=False # 保留非基因表达特征
)
读取h5ad:最省心的选择
h5ad是Anndata的原生格式,一句话就够:
adata = sc.read_h5ad("data.h5ad")
为什么我推荐优先用h5ad?因为它读写快、体积小、保留完整元数据。你在预处理阶段加的.obs、.var、.uns都能完整保存下来。而CSV读进去只有表达矩阵,所有别的信息都得重算。
不过有一点要注意:h5ad的版本兼容性。Scanpy 1.8.x和1.9.x生成的h5ad,低版本Scanpy可能读不了。我遇到过跨环境迁移时读不了,最后用sc.read的backed模式先检查文件头才找到原因。
backed='r'模式只读加载,避免内存爆炸:
adata = sc.read_h5ad("large_file.h5ad", backed='r')
读取CSV/TSV:万不得已的选择
有时候上游工具只输出CSV,比如一些老的scRNA-seq流程或第三方分析结果。Scanpy提供sc.read_csv和sc.read_tsv:
# CSV:行是细胞,列是基因
adata_csv = sc.read_csv("counts.csv", first_column_names=True)
# TSV:用sc.read_text更灵活
adata_tsv = sc.read_text("counts.tsv", delimiter='\t', first_column_names=True)
这里有个关键:行列方向。Scanpy默认n_obs × n_vars,即行是细胞、列是基因。如果你的CSV是基因×细胞,需要转置后再传入:
import pandas as pd
df = pd.read_csv("data.csv", index_col=0)
adata = sc.AnnData(df.T) # 转置
为什么不推荐CSV?说白了,CSV没有元数据,连.var的gene_id和gene_symbol都分不清。而且大矩阵读写慢成龟速。我建议:只要不是被迫,就别用CSV。
通用读取函数:sc.read
Scanpy还有个万能函数sc.read,能自动识别格式:
# 自动根据后缀选择读取方式
adata = sc.read("data.h5ad") # → read_h5ad
adata = sc.read("data.csv") # → read_csv
adata = sc.read("data.mtx") # → 只读矩阵,需额外指定元数据
但注意:对于10X格式,不能直接用sc.read读matrix.mtx(它会当作普通MTX读,丢掉基因和条形码)。10X格式必须走sc.read_10x_mtx或sc.read_10x_h5。
数据导入决策流程
常用参数对照表
不同读取函数的参数差异很大,我整理了一个速查表,方便你直接粘贴:
| 函数 | 关键参数 | 注意事项 |
|---|---|---|
sc.read_10x_mtx |
var_names('gene_symbols'或'gene_ids')、gex_only、cache |
目录路径,三文件必须存在且命名规范 |
sc.read_h5ad |
backed('r'或'r+')、filename |
大文件用backed='r'节省内存 |
sc.read_csv |
first_column_names、delimiter |
只能读基因×细胞的转置矩阵时手动处理 |
sc.read_10x_h5 |
gex_only、genome |
专门读取Cell Ranger的h5输出(单文件格式) |
实战避坑指南
这些年我帮同事调试读数据的问题,总结出三个高频翻车点:
- 路径写成了文件而非目录:
read_10x_mtx会直接报错,别问我怎么知道的。 - CSV行列方向反了:读进来一看
n_obs只有几百,明显是基因数量。记得检查adata.shape,如果n_obs远小于n_vars,大概率需要转置。 - h5ad跨版本不兼容:我在用Scanpy 1.9.8写的h5ad,放到1.7.0的环境里死活读不了。解决方案:拿新版本再写一遍,或者用
anndata.read_h5ad的backed模式先浏览内容。
好了,数据导入就这些。
接下来你可以在实际数据上试试:找个10X数据集跑一遍sc.read_10x_mtx,再用sc.read_h5ad读我的测试文件(我会在代码仓库里放一个示例h5ad)。
读进来以后,记得先跑adata.obs_names[:5]和adata.var_names[:5]看一眼,确认数据没跑偏。