☰
单细胞转录组数据查找指南:从GEO到HCA的检索策略与元数据核对
2026/10/20 17:38:21 网站建设 项目流程

简介:这份资源是面向生物信息学初学者与单细胞研究入门者的数据查找指南配套项目代码,聚焦单细胞转录组公共数据的检索与获取路径。内容围绕GEO、Single Cell Portal、Human Cell Atlas等常用数据库展开,讲解如何用布尔逻辑运算符构建精确查询、借助GEO分类功能细化结果,并涉及数据格式识别、质量检查、下载链接处理与许可协议引用等实操要点,帮助读者建立从找数据到用数据的完整认知。资源包共3个文件,以html页面为主体,辅以inscode项目配置与gitignore忽略规则,整体约9KB,结构轻量,便于直接浏览与二次整理。目前已有131人学习关注。对于需要快速定位单细胞表达数据集、理解公共数据库检索逻辑的研究者而言,这份代码可作为入门参考与检索思路的落地示例。

1. 单细胞转录组数据查找指南:为什么你总在“找数据”这一步卡三天

做过单细胞的人大概都有过这种体验:分析流程的代码早就写好了,Seurat 或 Scanpy 的管道跑得滚瓜烂熟,结果卡在第一步——数据从哪来。明明公共数据库里躺着几十万份单细胞样本,真到自己要找一个“人外周血 PBMC 静息态 + 刺激态对照”的数据集时,翻了半天不是物种不对,就是组织不对,要么就是只有处理后的矩阵没有原始 count,再不然就是元数据里连分组信息都缺。这种“数据查找”的痛,本质不是搜索能力问题,而是你不知道数据都藏在哪几个地方、每个地方的数据长什么样、元数据字段叫什么。

这篇指南要解决的就是这件事:把单细胞转录组数据查找从“碰运气”变成“有章法”。我会按数据源类型、检索策略、元数据核对、下载与格式转换、踩坑排查的顺序讲一遍,适合刚接触单细胞、需要快速定位公共数据的新手,也适合已经跑过几套数据、但想建立一套可复用查找流程的熟手。核心结论先放这里:找数据的时间应该花在“定义清楚你要什么”和“核对元数据”上,而不是在下载按钮上反复横跳。

2. 单细胞数据都藏在哪:四类数据源的定位与取舍

2.1 综合型公共仓库:GEO、ArrayExpress、SRA 的分工

绝大多数人第一次找单细胞数据,都是从 GEO 开始的。GEO 全称 Gene Expression Omnibus,是 NCBI 维护的基因表达数据库,单细胞数据主要以 GSE 编号组织。一个 GSE 可以包含多个样本,每个样本下挂 GSM 记录,原始测序数据可能放在 SRA,处理后的表达矩阵可能作为 supplementary file 提供。这里有个关键区分:GEO 本身不强制要求上传 count 矩阵,很多老数据集只给了 FASTQ 的 SRA 链接,你得自己跑 Cell Ranger 或 STARsolo 才能得到表达矩阵。

ArrayExpress 是 EBI 维护的类似仓库,单细胞数据相对少一些,但部分欧洲实验室的数据会优先放这里。SRA(Sequence Read Archive)则是原始测序数据的归档库,GEO 里的原始数据最终都指向 SRA。如果你需要从头处理,就得从 SRA 下载 FASTQ,这一步的坑后面会专门讲。

常见做法是:先搜 GEO,看有没有现成的 count 矩阵;没有再去 SRA 下原始数据自己跑。ArrayExpress 作为补充检索,不要漏。

2.2 单细胞专用门户:Human Cell Atlas、Single Cell Portal 等

综合仓库的问题是元数据不规范,同一个 GSE 里不同样本的字段命名可能都不一样。单细胞专用门户在这方面做得好很多。Human Cell Atlas(HCA)按组织、发育阶段、疾病状态组织数据,元数据遵循统一 schema。Broad Institute 的 Single Cell Portal 则提供了在线可视化和标准化的元数据下载。

这类门户的优势是元数据字段统一、可按条件筛选,缺点是覆盖范围有限,不是所有已发表数据都会同步过来。我一般会先用专用门户快速定位候选数据集,拿到 GSE 或 accession 编号后,再回 GEO 确认原始数据是否完整。

2.3 文献补充材料与预印本平台:被忽略的“暗数据”

有一类数据不在任何数据库里,而是作为论文的 supplementary file 存在。尤其是一些较早的单细胞研究,作者可能只上传了处理后的聚类结果,完整的 count 矩阵放在补充材料里。预印本平台上的论文,有时数据链接指向的是实验室自己的服务器或云盘。

这类数据的查找方法是:在 PubMed 或预印本平台搜到相关论文后,直接翻到 Data Availability 部分,看作者把数据放在了哪。如果只给了“available upon request”,那基本可以放弃,发邮件要数据的周期不可控。

2.4 数据源选择决策表

数据源类型典型入口数据形态元数据规范度适用场景
综合仓库GEO / ArrayExpress原始 FASTQ 或 count 矩阵参差不齐大多数公共数据检索
专用门户HCA / Single Cell Portal标准化 count 矩阵高按组织/疾病快速筛选
文献补充论文 supplementary处理后的矩阵或结果低数据库未收录的老数据
预印本附件预印本平台不定低最新未发表数据

选数据源的原则很简单:能用专用门户就用专用门户,找不到再回综合仓库,最后才考虑文献补充材料。因为元数据规范度直接决定你后续整合数据的成本。

3. 检索策略:怎么用关键词和过滤器精准命中目标数据集

3.1 关键词组合:从“组织+物种+状态”三要素出发

在 GEO 里直接搜“single cell”会返回几万条结果,毫无意义。有效的检索是把你的生物学问题拆成三个要素:组织类型、物种、实验状态。比如你要找“人肺组织 COVID-19 感染 vs 对照的单细胞数据”,关键词组合应该是:

("lung" OR "pulmonary") AND ("Homo sapiens") AND ("COVID-19" OR "SARS-CoV-2") AND ("single cell" OR "scRNA-seq")

在 GEO 的高级检索里,可以限定字段。比如[Title]限定标题,[Description]限定描述,[Organism]限定物种。这样能大幅减少噪音。

3.2 GEO 高级检索语法与字段限定

GEO 的检索语法支持布尔运算和字段限定。几个常用的字段:

  • [Organism]:物种,如Homo sapiens[Organism]
  • [Title]:数据集标题
  • [Description]:数据集描述
  • [Sample Count]:样本数量范围
  • [Publication Date]:发表日期

一个实用的检索式示例:

(single cell[Title] OR scRNA[Title]) AND (lung[Title] OR pulmonary[Title]) AND Homo sapiens[Organism] AND 2020:2024[Publication Date]

这个检索式会返回标题里含单细胞关键词、组织为肺、物种为人、2020 到 2024 年发表的数据集。比直接搜“single cell lung”精准得多。

3.3 用代码批量检索 GEO:一个可复现的 Python 脚本

手动在网页上翻页效率太低,我一般用 Python 脚本批量拉取 GEO 的检索结果。下面这个脚本用requests和BeautifulSoup抓取 GEO 检索页面的数据集列表,输出 GSE 编号、标题和样本数。

import requests from bs4 import BeautifulSoup import time def search_geo(keyword, max_results=50): """ 检索 GEO 数据集,返回 GSE 编号、标题、样本数列表 keyword: 检索关键词,支持 GEO 字段限定语法 max_results: 最大返回条数 """ base_url = "https://www.ncbi.nlm.nih.gov/gds/" params = { "term": keyword, "sort": "pubdate", "size": 200 # 每页显示条数 } headers = { "User-Agent": "Mozilla/5.0 (compatible; ResearchBot/1.0)" } results = [] page = 0 while len(results) < max_results: params["page"] = page resp = requests.get(base_url, params=params, headers=headers, timeout=30) if resp.status_code != 200: print(f"请求失败,状态码:{resp.status_code}") break soup = BeautifulSoup(resp.text, "html.parser") items = soup.select("div.rprt") # GEO 结果条目容器 if not items: break for item in items: title_tag = item.select_one("a.rprt") if not title_tag: continue title = title_tag.get_text(strip=True) gse_id = title_tag.get("href", "").split("/")[-1] # 样本数通常在描述文本里 desc = item.get_text(" ", strip=True) results.append({ "gse": gse_id, "title": title, "desc_snippet": desc[:200] }) if len(results) >= max_results: break page += 1 time.sleep(1) # 控制请求频率,避免被封 return results # 使用示例:检索人肺组织单细胞数据 hits = search_geo( '(single cell[Title] OR scRNA[Title]) AND (lung[Title] OR pulmonary[Title]) AND Homo sapiens[Organism]', max_results=30 ) for h in hits: print(h["gse"], "|", h["title"][:80])

这段代码的逻辑是:构造带字段限定的检索词,请求 GEO 检索页面,解析返回的 HTML 提取 GSE 编号和标题。参数说明:keyword支持 GEO 的完整检索语法,max_results控制抓取上限,time.sleep(1)是必要的礼貌间隔,避免高频请求被限流。注意 GEO 页面结构可能调整,如果div.rprt选择器失效,需要根据实际页面更新解析逻辑。

3.4 检索结果太多怎么办:用样本数和数据类型做二次过滤

检索式跑完可能还是几十上百条结果。这时候按两个维度二次过滤:样本数和数据类型。样本数太少的(比如少于 6 个样本)统计效力有限,除非是稀有组织。数据类型看 GSE 页面里的 supplementary file,如果只有 FASTQ 的 SRA 链接,处理成本高;如果有count matrix或expression matrix的压缩包,优先选。

我一般会在脚本输出结果后,再写一个小函数去逐个访问 GSE 页面,检查 supplementary file 列表里有没有.mtx、.h5、.csv这类矩阵文件。这一步能省掉大量后续下载和处理的麻烦。

4. 元数据核对:下载前必须确认的五个字段

4.1 样本分组信息:control 和 treatment 到底怎么标的

这是最容易翻车的地方。GSE 页面上的样本标题可能写的是“Patient1_control”“Patient1_treated”,但下载下来的矩阵列名可能是“Sample_1”“Sample_2”,元数据文件里又是另一套命名。下载前一定要确认分组信息在哪个文件里、字段名是什么。

常见做法是:在 GSE 页面点开每个 GSM 样本,看characteristics字段里有没有disease state、treatment、group这类字段。如果没有,去 supplementary file 里找metadata.csv或cell_metadata.txt。如果都没有,这个数据集的分组信息可能只存在于论文里,需要手动对照。

4.2 细胞类型注释:作者标了还是没标

单细胞数据的价值很大程度上取决于细胞类型注释。有些数据集在 supplementary file 里提供了带cell_type列的元数据文件,有些只给了聚类编号。如果作者没标,你得自己跑注释流程,这是额外的工作量。

在 GSE 页面看 supplementary file 列表,如果有*_cell_metadata*、*_annotation*、*_cluster*这类文件名,大概率有注释信息。下载后先看列名,确认cell_type或celltype字段是否存在。

4.3 物种与基因组版本:GRCh38 还是 GRCh37

物种不用多说,但基因组版本容易被忽略。同样是人的数据,比对到 GRCh38 和 GRCh37 得到的基因名和坐标不同。如果你要把多个数据集整合,基因组版本必须统一。GEO 页面通常在data processing部分会写比对用的参考基因组版本。如果没有写,看作者提供的矩阵里的基因名格式,Ensembl ID 带版本号的话能推断出来。

4.4 矩阵格式:count、TPM 还是 log-normalized

单细胞数据常见的矩阵格式有三种:原始 count、TPM/CPM 归一化后的、log-normalized 后的。做差异表达分析通常需要原始 count,做可视化可以用 log-normalized。下载前确认矩阵是哪种,否则后续分析步骤会出错。

判断方法:看 supplementary file 的文件名和描述。raw_counts、count_matrix一般是原始 count;normalized、lognorm是归一化后的。如果文件名看不出来,下载后看数值范围:原始 count 是整数,log-normalized 是小数且有很多负值或零附近的值。

4.5 元数据核对清单表

核对项在哪看缺失时的后果
样本分组GSM characteristics 或 metadata 文件无法做组间比较
细胞类型注释supplementary file 的 metadata需自行注释,增加工作量
基因组版本data processing 部分多数据集整合时基因不匹配
矩阵格式supplementary file 文件名/数值范围分析步骤选错,结果不可靠
样本数GSE 页面 summary统计效力不足

这五项核对完,再决定要不要下载。我见过太多人下载了十几个 G 的数据,解压后发现分组信息缺失,只能重新找。

5. 下载与格式转换:从 GSE 到可分析矩阵的完整链路

5.1 用 GEOparse 批量下载 GSE 补充文件

手动点下载按钮效率低,用 Python 的GEOparse库可以批量拉取。下面这个脚本下载指定 GSE 的所有 supplementary file。

import GEOparse import os def download_gse_supplementary(gse_id, output_dir="./data"): """ 下载指定 GSE 的所有 supplementary 文件 gse_id: GSE 编号,如 "GSE123456" output_dir: 本地保存目录 """ os.makedirs(output_dir, exist_ok=True) # 获取 GSE 对象,destdir 指定缓存目录 gse = GEOparse.get_GEO(geo=gse_id, destdir=output_dir, silent=False) # 打印基本信息 print(f"GSE: {gse_id}") print(f"标题: {gse.metadata.get('title', ['N/A'])[0]}") print(f"样本数: {len(gse.gsms)}") # 下载 supplementary 文件 for gsm_name, gsm in gse.gsms.items(): for supp_file in gsm.metadata.get("supplementary_file", []): if supp_file and supp_file != "NONE": print(f"下载: {supp_file}") # GEOparse 会自动处理 FTP 链接下载 gsm.download_supplementary_files(directory=output_dir) return gse # 使用示例 gse = download_gse_supplementary("GSE123456", output_dir="./geo_data")

逻辑说明:GEOparse.get_GEO会拉取 GSE 的元数据并缓存到本地,gsm.download_supplementary_files负责下载每个样本的补充文件。参数destdir控制缓存位置,silent=False会打印进度。注意有些 GSE 的 supplementary file 是打包在一个大压缩包里的,GEOparse 可能只下载压缩包本身,需要手动解压。

5.2 从 SRA 到 FASTQ:fasterq-dump 的参数与坑

如果 GSE 只提供了 SRA 链接,你需要自己下载 FASTQ。常用工具是 SRA Toolkit 里的fasterq-dump。基本命令:

# 下载单个 SRA 文件并转换为 FASTQ fasterq-dump SRR12345678 --outdir ./fastq --threads 8 --split-files # 批量下载:从 SRA accession 列表文件读取 while read srr; do fasterq-dump "$srr" --outdir ./fastq --threads 8 --split-files done < sra_list.txt

参数说明:--outdir指定输出目录,--threads控制线程数,--split-files会把双端测序的 read1 和 read2 分开输出。坑点:fasterq-dump默认会在当前目录生成临时文件,如果磁盘空间不够会中途失败。建议用--temp指定临时目录到大容量磁盘。另外,SRA 下载速度受网络影响大,批量下载时建议加--progress看进度。

5.3 格式转换:h5ad、mtx、h5 之间的互转

单细胞矩阵常见格式有:10x Genomics 的mtx三件套(matrix.mtx、barcodes.tsv、features.tsv)、HDF5 格式的.h5、AnnData 的.h5ad。不同工具支持的格式不同,Scanpy 用.h5ad,Seurat 用.h5或mtx。

用 Scanpy 读取和转换:

import scanpy as sc # 读取 10x mtx 格式 adata = sc.read_10x_mtx( "./filtered_feature_bc_matrix/", var_names="gene_symbols", cache=True ) # 读取 h5 格式 adata = sc.read_10x_h5("./filtered_feature_bc_matrix.h5") # 保存为 h5ad adata.write("./data.h5ad") # 从 h5ad 导出为 mtx(供 Seurat 使用) adata.write_10x_mtx("./export_mtx/")

逻辑说明:read_10x_mtx读取 10x 的标准输出目录,var_names="gene_symbols"指定用基因名而非 Ensembl ID 作为变量名。write_10x_mtx导出为 10x 格式,Seurat 可以用Read10X读取。注意导出时如果 adata 已经做了归一化,导出的就不是原始 count 了,需要提前保留原始 count 层。

5.4 下载后的完整性校验

下载完成后,至少做三项校验:文件大小是否与页面标注一致、压缩包能否正常解压、矩阵维度是否与样本数匹配。我一般会写一个简单的校验脚本:

import os import gzip def check_download(filepath, expected_size_mb=None): """检查下载文件完整性""" if not os.path.exists(filepath): print(f"文件不存在: {filepath}") return False actual_size = os.path.getsize(filepath) / (1024 * 1024) print(f"文件大小: {actual_size:.2f} MB") if expected_size_mb and abs(actual_size - expected_size_mb) > expected_size_mb * 0.1: print(f"警告: 文件大小与预期差异超过 10%") return False # 如果是 gzip 文件,尝试读取头部 if filepath.endswith(".gz"): try: with gzip.open(filepath, "rb") as f: f.read(1024) print("gzip 文件头正常") except Exception as e: print(f"gzip 文件损坏: {e}") return False return True

这一步看起来简单,但能避免很多“下载到一半断了但没发现”的问题。

6. 避坑与排查:单细胞数据查找中最容易翻车的五件事

6.1 坑一:GSE 页面显示有矩阵,下载后只有 FASTQ

现象:GSE 页面的 supplementary file 列表里看起来有文件,下载后发现全是 SRA 的 FASTQ 压缩包,没有 count 矩阵。

原因:GEO 的 supplementary file 分两类,一类是作者上传的处理后矩阵,一类是 NCBI 自动从 SRA 生成的 FASTQ 文件。页面展示时可能混在一起,不仔细看容易误判。

解决:在 supplementary file 列表里找文件名含matrix、count、h5、mtx的条目。如果只有fastq、sra字样,说明需要自己跑比对流程。这时候评估一下:如果样本数少(比如少于 10 个),自己跑 Cell Ranger 可行;如果样本数多,考虑换一个数据集。

6.2 坑二:元数据里的分组字段名和论文里写的不一致

现象:论文里写的是“responder vs non-responder”,下载的元数据里字段叫group,值是R和NR,没有完整说明。

原因:作者上传数据时用了简写,论文里的详细说明没有同步到元数据文件。

解决:对照论文的 Methods 部分和 GSE 页面的样本描述,手动建立映射关系。如果实在对不上,发邮件问作者是最直接的方式,但周期长。另一个办法是看矩阵的聚类结果,如果分组在聚类上分得很开,可以推断分组含义。

6.3 坑三:不同数据集的基因名格式不统一

现象:整合两个数据集时,一个用基因 symbol(如TP53),一个用 Ensembl ID(如ENSG00000141510),直接合并后基因对不上。

原因:不同作者使用的比对工具和参考基因组注释版本不同。

解决:统一转换为基因 symbol 或 Ensembl ID。用 Scanpy 的var_names_make_unique和adata.var操作,或者用 MyGene.info 这类注释服务做 ID 转换。转换时注意版本号,Ensembl ID 带版本号(如ENSG00000141510.17)的话要去掉版本号再匹配。

6.4 坑四:下载的 h5ad 文件版本与当前 Scanpy 不兼容

现象:用sc.read_h5ad读取下载的 h5ad 文件时报错,提示 AnnData 版本不兼容。

原因:h5ad 文件是用旧版 AnnData 写的,新版 Scanpy 读取时 schema 不匹配。

解决:先看报错信息里的版本号,尝试安装对应版本的 AnnData 读取,或者用sc.read_h5ad的backed模式先读元数据,再手动转换。更稳妥的做法是让作者提供 mtx 格式,或者从原始 count 重新构建 AnnData 对象。

6.5 坑五:样本量足够但细胞数太少

现象:GSE 页面显示有 20 个样本,下载后发现每个样本只有几百个细胞,做聚类分析时细胞数不够,聚类结果不稳定。

原因:早期单细胞数据或某些组织类型的细胞捕获率低,作者可能没有做细胞过滤。

解决:在 GSE 页面的data processing部分看作者是否提到了细胞过滤标准。如果细胞数确实少,考虑合并多个数据集,或者换用细胞数更多的数据集。一般来说,每个样本至少要有 1000 个以上细胞才能做可靠的聚类。

7. 进阶技巧:用元数据反向筛选高价值数据集

前面讲的都是“找到数据、下载数据、核对数据”,这一章讲一个更高效的思路:不主动搜数据集,而是从高质量论文反向定位数据。具体做法是,先锁定你研究领域内近两年发表的高影响力论文,直接看它们的 Data Availability 部分,拿到 GSE 编号后再去 GEO 确认数据完整性。这样找到的数据集,元数据规范度和注释质量通常更高,因为论文的审稿过程会倒逼作者把数据整理清楚。

另一个技巧是建立自己的数据集索引表。我一般会维护一个 CSV 文件,每找到一个新的候选数据集就记一行,字段包括:GSE 编号、物种、组织、样本数、细胞数、矩阵格式、是否有注释、基因组版本、下载状态、备注。这个表看起来简单,但当你需要整合多个数据集时,能省掉大量重复核对的时间。

import pandas as pd # 维护数据集索引表的示例 index_data = { "gse_id": ["GSE111111", "GSE222222"], "species": ["Homo sapiens", "Mus musculus"], "tissue": ["lung", "brain"], "n_samples": [12, 8], "n_cells": [45000, 22000], "matrix_format": ["h5", "mtx"], "has_annotation": [True, False], "genome_version": ["GRCh38", "mm10"], "download_status": ["done", "pending"], "notes": ["COVID-19 cohort", "need cell type annotation"] } df = pd.DataFrame(index_data) df.to_csv("./dataset_index.csv", index=False) print(df)

这个索引表的价值在于:当你需要找“人肺组织、有细胞类型注释、GRCh38 版本”的数据时,直接筛选 CSV 就行,不用重新去 GEO 翻。我自己的索引表积累了两年多,现在找数据基本十分钟内能定位到候选。

最后一个习惯:下载数据前先看数据量,别急着全下。有些 GSE 的 supplementary file 有几十个 G,全下完发现元数据不对,时间全浪费了。我一般先下载一个样本的矩阵和元数据文件,确认格式和字段符合预期后,再批量下载其余样本。这个“先试一个”的习惯,帮我省过至少几十个小时的无效下载。

希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询