关于 geo-database
geo-database 是用于查找、下载和解析 NCBI GEO 公共功能基因组学记录的技能。研究人员可通过 GEOparse 和 NCBI E-utilities 获取 GEO 系列、表达数据及样本元数据。
主要功能
- 01通过 E-utilities 搜索 GEO 记录
- 02下载并解析 GSE、GPL 和 GSM 记录
- 03提取表达矩阵与样本元数据
- 04将平台探针关联到基因注释
- 05通过 FTP 下载大型系列矩阵文件
适用场景
- 01按物种、组织或条件查找研究
- 02为差异分析准备 GEO 表达数据
- 03收集用于荟萃分析的样本注释
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jaechang-hits/ sciagent-skills --skill geo-database安装后在 Claude Code 中输入/geo-database确认已加载。
详细介绍
GEO 基因表达综合数据库
概述
GEO(Gene Expression Omnibus)是 NCBI 的公共高通量功能基因组学数据存储库,包含来自微阵列、RNA-seq、ChIP-seq、甲基化和蛋白质组学实验的 200,000 多个数据集(系列)。GEOparse 提供了用于下载和解析 GEO 记录(GSE 系列、GPL 平台、GSM 样本)的 Python 接口,而 NCBI E-utilities 则支持以编程方式搜索 GEO 元数据。
适用场景
- 按生物体、组织、疾病或实验条件搜索公开的基因表达数据集
- 下载和解析特定的 GEO 系列(GSE)及其表达矩阵和样本元数据
- 提取样本注释表(例如治疗组、临床协变量)用于荟萃分析
- 将微阵列表达数据(带 GPL 平台注释的探针)加载到整洁的 DataFrame 中
- 检索与感兴趣的基因或通路相关的所有 GEO 实验
- 构建自动化工作流,下载并处理 GEO 数据集以供下游分析
- 对于大规模单细胞 RNA-seq 数据,请使用
cellxgene-census;对于已比对的读段,请改为从 ENA/SRA 下载 FASTQ
前置条件
- Python 包:
GEOparse、requests、pandas - 数据要求:GSE/GPL/GSM 登录号或搜索词
- 环境:需要互联网连接;下载时需要对本地目录具有写入权限
- 速率限制:E-utilities:未认证时为 3 req/s,使用 API key 时为 10 req/s;GEO FTP 不限速
pip install GEOparse requests pandas
快速入门
import GEOparse
# Download a GEO series (caches in current directory)
gse = GEOparse.get_GEO("GSE2553", destdir="./geo_data/")
print(f"Title: {gse.metadata['title'][0]}")
print(f"Samples: {len(gse.gsms)}")
print(f"Platform: {list(gse.gpls.keys())}")
# Sample metadata
meta = gse.phenotype_data
print(meta.head())
核心 API
查询 1:通过 E-utilities 搜索 GEO 数据集
按关键词、生物体或数据集类型查找 GEO 系列(GSE)。
import requests
EMAIL = "[email protected]"
BASE = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils"
def geo_search(query, retmax=20):
r = requests.get(f"{BASE}/esearch.fcgi",
params={"db": "gds", "term": query,
"retmax": retmax, "retmode": "json", "email": EMAIL})
r.raise_for_status()
return r.json()["esearchresult"]
# Search for human breast cancer RNA-seq datasets
result = geo_search(
"breast cancer[title] AND Homo sapiens[organism] AND gse[entry type]",
retmax=10
)
print(f"Found {result['count']} matching GEO datasets")
print(f"First accessions (UIDs): {result['idlist']}")
# Search for specific platform (e.g., Illumina HumanHT-12)
result = geo_search(
"Illumina HumanHT-12[platform] AND Homo sapiens[organism] AND gse[entry type]",
retmax=5
)
print(f"Illumina HumanHT-12 human datasets: {result['count']}")
查询 2:获取数据集摘要元数据
检索搜索结果的标题、登录号和生物体信息。
import requests
EMAIL = "[email protected]"
BASE = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils"
def geo_summary(uids):
r = requests.post(f"{BASE}/esummary.fcgi",
data={"db": "gds", "id": ",".join(uids),
"retmode": "json", "email": EMAIL})
r.raise_for_status()
return r.json()["result"]
# Get metadata for search results
result = geo_search_func = lambda q: requests.get(
f"{BASE}/esearch.fcgi",
params={"db": "gds", "term": q, "retmax": 3, "retmode": "json", "email": EMAIL}
).json()["esearchresult"]["idlist"]
uids = requests.get(
f"{BASE}/esearch.fcgi",
params={"db": "gds", "term": "lung cancer[title] AND gse[entry type]",
"retmax": 3, "retmode": "json", "email": EMAIL}
).json()["esearchresult"]["idlist"]
summaries = geo_summary(uids)
for uid in summaries.get("uids", []):
s = summaries[uid]
print(f"\nAccession: {s.get('accession')} | {s.get('title')}")
print(f" Organism: {s.get('taxon')}")
print(f" Samples: {s.get('n_samples')}")
print(f" Type: {s.get('gdstype')}")
查询 3:下载并解析 GEO 系列
使用 GEOparse 下载完整的 GSE 记录,包括表达矩阵和样本元数据。
import GEOparse
# Download GSE (auto-caches; skip download if already present)
gse = GEOparse.get_GEO("GSE2553", destdir="./geo_data/", silent=True)
# Series metadata
print(f"Title : {gse.metadata['title'][0]}")
print(f"Summary : {gse.metadata['summary'][0][:200]}...")
print(f"Samples : {len(gse.gsms)} GSMs")
print(f"Platforms: {list(gse.gpls.keys())}")
# Sample metadata table (phenotype data)
meta = gse.phenotype_data
print(f"Metadata columns: {list(meta.columns)}")
print(meta.head())
查询 4:提取表达矩阵
解析探针级表达数据,并可选择与平台基因注释合并。
import GEOparse, pandas as pd
gse = GEOparse.get_GEO("GSE2553", destdir="./geo_data/", silent=True)
# Pivot to gene expression matrix (probes × samples)
gpl_id = list(gse.gpls.keys())[0]
pivot = gse.pivot_samples("VALUE", gpl_id)
print(f"Expression matrix shape: {pivot.shape}") # (probes, samples)
print(pivot.iloc[:5, :3])
# Annotate probes with gene symbols from the GPL platform
gpl = gse.gpls[gpl_id]
annot = gpl.table[["ID", "Gene Symbol", "Gene Title"]].copy()
annot.columns = ["ID", "gene_symbol", "gene_title"]
annot = annot.dropna(subset=["gene_symbol"])
annot = annot[annot["gene_symbol"] != ""]
expr_annotated = pivot.join(annot.set_index("ID"), how="inner")
print(f"Annotated expression matrix: {expr_annotated.shape}")
print(expr_annotated[["gene_symbol", "gene_title"]].head())
查询 5:下载单个样本(GSM)
获取单个样本的表达值和元数据。
import GEOparse
gsm = GEOparse.get_GEO("GSM45553", destdir="./geo_data/", silent=True)
print(f"Title : {gsm.metadata['title'][0]}")
print(f"Source : {gsm.metadata.get('source_name_ch1', ['n/a'])[0]}")
print(f"Organism: {gsm.metadata.get('organism_ch1', ['n/a'])[0]}")
print(f"Data rows: {len(gsm.table)}")
print(gsm.table.head())
查询 6:通过 FTP 直接下载大型系列
对于大型数据集,可直接从 GEO FTP 下载系列矩阵文件。
import urllib.request, gzip, io, pandas as pd
# GEO series matrix file URL pattern
accession = "GSE2553"
series_num = accession[3:] # strip "GSE"
folder = f"GSE{series_num[:-3]}nnn" if len(series_num) > 3 else f"GSE{series_num[:-2]}nn"
url = f"https://ftp.ncbi.nlm.nih.gov/geo/series/{folder}/{accession}/matrix/{accession}_series_matrix.txt.gz"
with urllib.request.urlopen(url) as resp:
with gzip.open(resp, "rt", encoding="utf-8") as f:
lines = f.readlines()
# Find metadata lines (start with !) and data table
meta_lines = [l for l in lines if l.startswith("!")]
data_start = next(i for i, l in enumerate(lines) if l.startswith('"ID_REF"'))
df = pd.read_csv(
io.StringIO("".join(lines[data_start:])),
sep="\t", index_col=0
)
print(f"Matrix shape: {df.shape}")
print(df.iloc[:3, :3])
关键概念
GEO 记录类型
- GSE(系列):包含所有样本和元数据的完整实验
- GPL(平台):微阵列或测序平台定义(探针/基因映射)
- GSM(样本):单次杂交或测序运行
- GDS(数据集):从系列中整理和标准化的子集(数量少于 GSE 记录)
超级系列和子系列
多重检测或多批次提交(例如 RNA-seq + ATAC-seq)会组织为一个引用一个或多个 子系列 GSE 的 超级系列 GSE。每个子系列都有自己的样本、平台和矩阵;超级系列本身不包含样本。两者都通过 gse.metadata 标记:
- 超级系列:
gse.metadata["relation"]包含类似"SuperSeries of: GSExxxx"的条目 - 子系列:
gse.metadata["relation"]包含"SubSeries of: GSEyyyy"
提取表达矩阵前,务必先解析子系列——单独下载超级系列只能获得元数据,不包含数据。
import GEOparse
gse = GEOparse.get_GEO("GSE47966", destdir="./geo_data/", silent=True) # a SuperSeries
relations = gse.metadata.get("relation", [])
subseries = [r.split(": ")[1] for r in relations if r.startswith("SuperSeries of")]
print(f"SubSeries to download: {subseries}")
for acc in subseries:
sub = GEOparse.get_GEO(acc, destdir="./geo_data/", silent=True)
print(f" {acc}: {len(sub.gsms)} samples, platforms={list(sub.gpls.keys())}")
SOFT 与 MiniML 格式
GEOparse 下载 SOFT 格式文件(纯文本)。如需基于 XML 的访问,请通过 E-utilities 使用 MiniML 格式。系列矩阵文件(制表符分隔)是表达数据最紧凑的格式。
常见工作流
工作流 1:下载并准备差异表达分析所需的表达数据
目标:下载 GEO 数据集,提取表达矩阵和分组标签,并保存以供下游差异表达分析使用。
import GEOparse, pandas as pd
# Download series
gse = GEOparse.get_GEO("GSE2553", destdir="./geo_data/", silent=True)
# 1. Extract expression matrix
gpl_id = list(gse.gpls.keys())[0]
expr = gse.pivot_samples("VALUE", gpl_id)
# 2. Extract sample groups from characteristics
meta = gse.phenotype_data
print("Available metadata columns:", list(meta.columns))
# 3. Annotate probes with gene symbols
gpl = gse.gpls[gpl_id]
gene_col = "Gene Symbol" if "Gene Symbol" in gpl.table.columns else gpl.table.columns[1]
annot = gpl.table[["ID", gene_col]].dropna()
annot.columns = ["probe_id", "gene_symbol"]
annot = annot[annot["gene_symbol"].str.strip() != ""]
expr_genes = expr.join(annot.set_index("probe_id")[["gene_symbol"]], how="inner")
expr_genes = expr_genes.groupby("gene_symbol").mean() # average duplicate probes
print(f"Genes × Samples: {expr_genes.shape}")
expr_genes.to_csv("expression_matrix.csv")
meta.to_csv("sample_metadata.csv")
print("Saved: expression_matrix.csv, sample_metadata.csv")
工作流 2:搜索并构建数据集清单
目标:在 GEO 中搜索符合主题的数据集,并构建经过整理的数据集清单 CSV。
import requests, time, pandas as pd
EMAIL = "[email protected]"
BASE = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils"
topic = "Alzheimer disease"
r = requests.get(f"{BASE}/esearch.fcgi",
params={"db": "gds", "email": EMAIL, "retmode": "json", "retmax": 50,
"term": f"{topic}[title] AND Homo sapiens[organism] AND gse[entry type]"})
uids = r.json()["esearchresult"]["idlist"]
print(f"Found {len(uids)} GSE datasets for '{topic}'")
rows = []
for i in range(0, len(uids), 20):
batch = uids[i:i+20]
r2 = requests.post(f"{BASE}/esummary.fcgi",
data={"db": "gds", "id": ",".join(batch),
"retmode": "json", "email": EMAIL})
result = r2.json()["result"]
for uid in result.get("uids", []):
s = result[uid]
rows.append({
"accession": s.get("accession"),
"title": s.get("title"),
"n_samples": s.get("n_samples"),
"organism": s.get("taxon"),
"gds_type": s.get("gdstype"),
"pub_date": s.get("pdat"),
})
time.sleep(0.4)
df = pd.DataFrame(rows).sort_values("n_samples", ascending=False)
df.to_csv(f"{topic.replace(' ', '_')}_geo_datasets.csv", index=False)
print(df[["accession", "title", "n_samples"]].head(10).to_string(index=False))
关键参数
| 参数 | 模块 | 默认值 | 范围 / 选项 | 作用 |
|---|---|---|---|---|
destdir | GEOparse.get_GEO | "./" | 任意目录路径 | 下载文件的保存位置 |
silent | GEOparse.get_GEO | False | True/False | 隐藏下载进度输出 |
retmax | ESearch | 20 | 1–10000 | 返回的数据集记录数上限 |
entry type 查询 | ESearch | — | "gse"、"gds"、"gpl"、"gsm" | 按 GEO 记录类型筛选 |
VALUE 列 | pivot_samples | — | GSM 表中的列名 | 要透视的表达值列 |
email | E-utilities | 必填 | 有效的电子邮件地址 | 用于 NCBI 速率限制归属 |
最佳实践
-
在 GEOparse 中使用
silent=True:隐藏冗长的下载进度;添加自己的 print 语句以确认下载完成。 -
缓存下载内容:如果
.soft.gz文件已存在于destdir中,GEOparse 会跳过重新下载。跨会话设置共用的destdir,避免重复下载。 -
大型数据集优先使用 Series Matrix:对于样本数超过 100 个的系列,直接从 FTP 下载
_series_matrix.txt.gz,而不要解析单个 GSM soft 文件——速度会快几个数量级。 -
谨慎处理探针到基因的映射:许多探针会映射到多个基因或没有对应基因。分析前,先决定如何处理映射到多个基因的探针(删除、拆分或保留)。对于 Affymetrix 芯片,使用
gene_symbol.str.split(" /// ")。 -
检查平台列名:GPL 注释表的列名因平台而异(例如,
"Gene Symbol"、"GENE_SYMBOL"或"gene_id")。假定字段名称前,务必检查gpl.table.columns。 -
分析前始终解析 SubSeries:加载任何 GSE 后,检查
gse.metadata.get("relation", [])中是否有"SuperSeries of: ..."条目。如果存在,则遍历所有引用的 SubSeries accession 并逐一下载——SuperSeries 记录本身不包含样本或表达矩阵。跳过此步骤会导致实际数据被悄然遗漏。
常用示例
示例:快速查看 GSE 元数据
适用场景:获取任意 GSE accession 的系列标题、样本数量和平台。
import GEOparse
gse = GEOparse.get_GEO("GSE2553", destdir="./geo_data/", silent=True)
print(f"Title : {gse.metadata['title'][0]}")
print(f"Samples: {len(gse.gsms)}")
print(f"Platform: {list(gse.gpls.keys())}")
print(f"Summary: {gse.metadata['summary'][0][:300]}")
示例:提取样本特征
适用场景:将 GEO 样本特征解析为整洁的 DataFrame,以便分组。
import GEOparse, pandas as pd, re
gse = GEOparse.get_GEO("GSE2553", destdir="./geo_data/", silent=True)
meta = gse.phenotype_data
# Parse "characteristics_ch1" columns
ch_cols = [c for c in meta.columns if "characteristics" in c.lower()]
print(f"Characteristic columns: {ch_cols}")
print(meta[ch_cols].head())
示例:列出系列中的所有 GSM
适用场景:枚举样本 accession,以便下载或收集元数据。
import GEOparse
gse = GEOparse.get_GEO("GSE2553", destdir="./geo_data/", silent=True)
gsm_ids = list(gse.gsms.keys())
print(f"Total samples: {len(gsm_ids)}")
print("First 5:", gsm_ids[:5])
故障排除
| 问题 | 原因 | 解决方案 |
|---|---|---|
下载期间出现 FileNotFoundError | destdir 不正确 | 先创建目录:os.makedirs("geo_data/", exist_ok=True) |
pivot_samples 返回空 DataFrame | GPL 注释表缺少 ID | 检查 gpl.table.columns;使用正确的探针 ID 列名 |
"Gene Symbol" 出现 KeyError | 平台使用了不同的列名 | 检查 gpl.table.columns 并使用正确的注释列 |
| 大型系列下载卡住 | SOFT 文件较大(GB 级) | 大型系列改用 FTP Series Matrix 下载,而不要使用 GEOparse |
| ESearch 返回 0 个结果 | entry type 或字段标签错误 | 将 gse[entry type] 改为 gds[entry type];检查查询语法 |
数值样本列包含 null | 表达值缺失或不存在 | 使用 df.fillna(0) 填充,或删除缺失值较多的列 |
GSE 没有样本 / gse.gsms 为空 | accession 是 SuperSeries | 解析 gse.metadata["relation"] 中的 SuperSeries of: 条目,并逐一下载每个 SubSeries |
相关技能
cellxgene-census— 大规模单细胞 RNA-seq 数据(超过 6,100 万个细胞),可作为 GEO 的 scRNA-seq 替代来源gene-database— NCBI Gene 记录,包含 GEO 研究中基因的精选注释pubmed-database— 通过 NCBI ELink 检索与 GEO 数据集关联的出版物pydeseq2-differential-expression— 加载 GEO 计数数据后的下游差异表达分析
参考资料
- GEO 数据库主页 — 浏览和搜索 GEO 数据集
- GEOparse GitHub — Python 库文档和示例
- GEO FTP 服务器 — 直接访问系列、样本和平台文件
- GEO 的 NCBI E-utilities —
gds数据库的 ESearch/ESummary API 参考