信息速览
INFOBOX — BioSamples 一屏速览
维度 内容 本质 生物样本的元数据登记库(不存测量值) 维护机构 EMBL-EBI 样本总数 56,108,301(实测) 主要来源 NCBI 导入 18,056,239 / 既有迁移 1,968,930 / ENA 导入 929,852 唯一键 SAME / SAMN 形式登录号 单条记录 特征键值对,上限 50 个 时间戳 三个:release / submitted / update 接口 分页 JSON 列表 + 单条 JSON 官方定位 参考样本(1000 Genomes、HipSci、FAANG)与已用于检测的样本 是否需注册 否(公开样本匿名可取) 特殊设计 站点本身就是一份 Schema.org DataCatalog 声明
§1 速览:它到底是什么
1.1 一句话定义
BioSamples 是一个**只登记"样本是什么"、不登记"测出来什么"**的元数据库。
这个区分是理解它的起点。一个 DNA 测序项目里有三层东西:
| 层 | 内容 | 落在哪 |
|---|---|---|
| 样本层 | 这份材料是什么(组织、细胞类型、年龄、疾病) | BioSamples |
| 实验层 | 对它做了什么检测(测序、芯片、质谱) | ENA / ArrayExpress |
| 数据层 | 测出来的序列或信号 | ENA / ArrayExpress |
BioSamples 只做第一层,而且只做"描述",不做实物。 它没有试管,没有 DNA,只有一张张记录卡。
1.2 它最不可替代的一件事
不是"样本多"(5,610 万这个数字确实大),而是:
它给跨库的样本提供了一个共同的键,让"同一个样本在不同库里"能被认出来。
具体说:一份血液样本,测序数据在 ENA(登录号 ERR####),芯片数据可能在 ArrayExpress(E-MTAB-####),而这两个库各自描述样本的方式不同、字段不同、甚至编号不同。BioSamples 的作用是给这个样本发一个 SAME 号,然后让 ENA 和 ArrayExpress 的记录都指向它。
这就是为什么官方把它定位成"参考样本与已用于检测的样本"的登记处——它不是数据的仓库,是数据之间那条线的锚点。
1.3 用一个真实样本把全文串起来
SAMN19844094
domain: self.BiosampleImportNCBI· taxId 10090(小鼠) · status PUBLIC
submittedVia: PIPELINE_IMPORT· 特征 20 个
含SRA accession、INSDC center name、cell_type、age、biomaterial_provider
它是从 NCBI 自动管道导入的一条小鼠样本记录。选它做案例,因为它把本篇要讲的四件事全占了:
| 章节 | 看它的哪个侧面 | 回答什么问题 |
|---|---|---|
| §2.2 | domain 与 submittedVia |
5,610 万条是谁填的 |
| §3.2 | 20 个特征字段的具体内容 | 一条样本记录到底能描述到什么程度 |
| §4.2 | SRA accession 这个外部引用 |
它怎么把数据连起来 |
| §5.3 | 一条 curl 取它 | API 长什么样、坑在哪 |
1.4 为什么它不是「又一个数据仓库」
| 常见的误解 | 实际情况 |
|---|---|
| “里面能下到测序数据” | 不能。它只有元数据,序列在 ENA |
| “样本数 5,610 万,等于 5,610 万份实验” | 一个样本可以对应多个实验,也可以没有任何实验(参考样本) |
| “字段是标准化的,可以直接拼表” | 键名由提交方自填,同义不同名极常见 |
| “它是 EBI 自建的库” | 大量内容是从 NCBI、ENA 导入的,它是聚合层 |
理解这一点决定了使用方式:要把 BioSamples 当"索引"用,不要当"数据表"用。
1.5 三十秒决策卡
| 你的处境 | BioSamples 能帮你什么 |
|---|---|
| 拿到一批 SRA/ENA 登录号,想知道样本是什么 | 按登录号反查样本元数据 |
| 要筛"人类肺组织的样本"做队列 | 按 taxId + 特征键过滤 |
| 想知道某样本在几个库里出现 | 看 externalReferences 与 publications |
| 要把自己的样本登记成可引用的条目 | 提交到 BioSamples 拿到 SAME 号 |
| 要下载测序原始数据 | 不能,去 ENA / SRA |
| 想要一张字段整齐、可直接建模的表 | 不能,键名不统一,要自己做归一 |
1.6 不适合谁
如果你要的是测量值,BioSamples 给不了你。它只有"这是肺组织、62 岁、男性",没有"这个肺组织的表达谱"。数据在 ENA、ArrayExpress、PRIDE 里。
如果你要一张可以直接进模型的宽表,也不适合。5,610 万条记录的特征键名由各提交方自填,"cell_type"和"cell type"和"CellType"可能是同一个意思。归一这步必须自己做,而且工作量不小。
如果你只关心人类数据,要注意它没有"只显示人类"的一键开关。5,610 万里混着小鼠、拟南芥、细菌、环境样本。必须用 taxId 过滤。
如果你需要完整枚举全部样本,会遇到分页限制。实测深层分页返回 400,必须用 cursor 分页或按域切片。
§2 数据构成:5,610 万条是怎么来的
2.1 总量的实测口径
curl -s "https://www.ebi.ac.uk/biosamples/samples?size=1&page=0"
返回的 page 对象里:
{"size": 1, "totalElements": 56108301, "totalPages": 56108301, "number": 0}
totalElements = 56,108,301。
注意 totalPages 与 totalElements 相等,因为 size=1。这是 Spring Data 分页的标准形态,也顺带说明了一件事:这个 API 没有做总数缓存,它每次都在算真实的总量。
2.2 来源构成:三个渠道,量级差两个数量级
用 filter=domain: 可以按来源通道分别统计:
| 域(domain) | 样本数 | 占比 |
|---|---|---|
self.BiosampleImportNCBI |
18,056,239 | 32.2% |
self.BioSamplesMigration |
1,968,930 | 3.5% |
self.BiosampleImportENA |
929,852 | 1.7% |
| 其余(未逐一枚举) | 约 35,153,280 | 62.6% |
三点解读:
第一,最大的单一来源是 NCBI 导入(1,805 万)。 这说明 BioSamples 的中心工作是"把 NCBI BioSample 的记录同步过来"。它不是从零建库,是做镜像与整合。
第二,BioSamples 自有迁移数据只有 197 万。 这部分是历史上 BioSamples 自己的记录(不是从别处导入的),经过一次迁移整理。
第三,domain 值的命名规律暴露了实现细节。 self.BiosampleImportNCBI 这种"self. + 驼峰类名"的写法,是 Java 内部枚举直接暴露到 API 的结果。这也解释了为什么很多看起来合理的域值(如 self.ENA)返回 0——实际值不叫那个名字。
2.3 一个必须知道的统计陷阱
totalElements 在带 filter 时是准确的,但域值必须写对。 试错成本很高,因为错的值也返回 200(只是 totalElements 为 0),不报错。
实测中下面这些值都返回 0:
self.ENA, self.BioSamples, self.BioSample, ENA, NCBI,
self.BiosampleImportEVA, self.BiosampleImportDDBJ,
self.BiosampleImportArrayExpress, self.BiosampleImportPRIDE
它们不是不存在,是名字不对。 正确的做法不是猜,是抽样看真实值:
import json, urllib.request, collections
UA = {"User-Agent": "Mozilla/5.0 (research script)"}
def sample_domains(pages=5, size=100):
c = collections.Counter()
for p in range(pages):
u = f"https://www.ebi.ac.uk/biosamples/samples?size={size}&page={p}"
with urllib.request.urlopen(urllib.request.Request(u, headers=UA),
timeout=60) as r:
d = json.load(r)
for s in d["_embedded"]["samples"]:
c[s.get("domain")] += 1
return c
if __name__ == "__main__":
for k, v in sample_domains().most_common():
print(f"{k:<45}{v}")
这个脚本的价值在于:它把"猜域值"变成了"看域值"。 任何靠枚举猜参数名的接口,都应该先这么探一遍。
2.4 样本不等于实验:一对多的关系
一个常见的误算:把 5,610 万样本当作 5,610 万份数据。
实际关系是:
一个样本 → 可对应 0 个实验(参考样本,或纯登记)
一个样本 → 可对应 1 个实验(最常见)
一个样本 → 可对应多个实验(同一样本做了 RNA-seq + 芯片 + 蛋白组)
一个实验 → 也可复用多个样本(多样本设计)
所以"样本数"和"数据集数"是两个不同量级的概念。 官方对"参考样本"的定义就很说明问题——1000 Genomes、HipSci、FAANG 这类样本,它们的价值在于"作为基准被反复引用",而不在于"对应多少实验"。
2.5 与库内相邻资源的定位
| 资源 | 存什么 | 与 BioSamples 的关系 |
|---|---|---|
| BioSamples | 样本是什么 | 本条目,样本层 |
| ENA | 测序读段与实验元数据 | 上游数据方;样本部分引用 BioSamples |
| ArrayExpress / BioStudies | 芯片与多组学实验 | 上游数据方 |
| NCBI BioSample / SRA | 美国侧的对应物 | 最大的数据来源(1,805 万条) |
| Expression Atlas | 重处理后的表达量 | 下游:消费样本注释做可比矩阵 |
| Single Cell Expression Atlas | 单细胞重处理结果 | 下游:细胞级样本描述大量来自此类登记 |
一句话判断规则:
要知道这个样本是什么 → BioSamples;
要拿测出来的数据 → ENA / SRA / ArrayExpress;
要重处理后的可比结果 → Expression Atlas 系列。
§3 记录层:一条样本记录里有什么
3.1 记录的字段结构
以 SAMN19844094 为例,单条记录的 JSON 顶层字段是:
| 字段 | 值 | 含义 |
|---|---|---|
name |
SAMN19844094 |
显示名,通常等于登录号 |
accession |
SAMN19844094 |
唯一键 |
domain |
self.BiosampleImportNCBI |
来源通道 |
taxId |
10090 |
物种 NCBI 分类号(小鼠) |
status |
PUBLIC |
访问状态 |
release |
发布时间 | 该样本公开的时间 |
submitted |
提交时间 | 提交方提交的时间 |
update |
更新时间 | 最后一次修改的时间 |
characteristics |
20 个键值对 | 样本描述本体(§3.2) |
submittedVia |
PIPELINE_IMPORT |
怎么进来的(管道导入) |
externalReferences |
外部链接 | 指向 SRA 等 |
create |
创建时间 | 记录生成的时刻 |
三个时间戳的区分很重要:
submitted:提交方什么时候提交release:什么时候对公众开放update:最后一次被改动
三者可以相差很多年。案例里这类从 NCBI 导入的老样本,release 常在 2014 年前后,而 update 可能到 2023 年——因为 2022 年前后 INSDC 之间做过一次同步,导致大量老样本的 update 被刷新。用 update 判断数据新鲜度会误判,用 release 才反映样本的原始时间。
3.2 特征字段:一条记录能描述到什么程度
characteristics 是核心。案例样本的 20 个字段可以分四类:
| 类别 | 字段 | 说明 |
|---|---|---|
| 溯源字段 | External Id、INSDC center name、INSDC first public、INSDC last update、INSDC secondary accession、INSDC status |
INSDC(国际核苷酸序列数据库协作)自动写入的对接信息 |
| 提交规范字段 | NCBI submission model、NCBI submission package |
说明按哪个模型/包提交(如 MIGS.ba、MIGS.ba.6.0) |
| 生物描述字段 | cell_type、age、organism、source_name、biomaterial_provider、sort |
提交方自己填的内容 |
| 数据链接字段 | SRA accession |
指向 SRA 的实验 |
这个分类揭示了 BioSamples 的真实构成:一半是自动生成的对接字段,一半是人工填写的描述字段。
而且每个字段的值不是纯字符串,是带标签的对象数组:
"characteristics": {
"cell_type": [{"text": "...", "tag": "..."}],
"age": [{"text": "..."}]
}
text 是原始文本,tag 是(可能的)标准化标记。 这个设计允许同一字段有多个值,也允许保留标准化标签——但大多数字段只有 text 没有 tag,标准化覆盖率不高。这是 §7 要展开的局限。
3.3 三个登录号前缀,含义不同
| 前缀 | 发号方 | 典型场景 |
|---|---|---|
SAMEA |
EBI(欧洲) | 欧洲提交的样本 |
SAMN |
NCBI(美国) | 美国提交的样本,BioSamples 镜像 |
SAMD |
DDBJ(日本) | 日本提交的样本 |
这三个前缀对应 INSDC 的三个成员机构。看到前缀就知道样本最初在哪个库里登记,这比读 domain 更直接——因为 domain 反映的是"BioSamples 怎么拿到它的",前缀反映的是"谁发的号"。
实用技巧:想只取欧洲原生样本,筛 SAMEA 前缀;想只取 NCBI 镜像,筛 SAMN。这比用 domain 过滤更稳,因为前缀是登录号的一部分,不会随导入管道改名。
3.4 50 个特征字段的上限
单条记录的特征字段上限是 50 个。这个限制的实际影响:
- 复杂的临床队列样本,可能需要描述几十个变量,接近上限
- 超出部分不会被保留,且不会有显式提示
- 做字段完整性评估时,看到"正好 50 个"的记录要警惕——可能是被截断的
这也是为什么 BioSamples 不适合当临床数据表:真正的临床研究有数百个变量,这里放不下。
3.5 三份真实记录的字段对比:同一套规范,三种填法
只看一条记录会以为字段结构是统一的。把三条真实记录并排放,差别立刻显出来。
样本 A:SAMN19844094(小鼠,浙江大学的隐窝细胞,20 个字段)
age = 8-week-old sex = pooled male and female
strain = C57BL/6 tissue = the small intestine
cell_type = crypt cell treatment = Cells (7-AAD-CD45-EpCAM+CD44+) sorted by FACS
biomaterial_provider = Shanghai SLAC Laboratory
description = Epithelial cells were isolated from the small intestine. In brief, ...
样本 B:SAMD00000593(小鼠,日本 DDBJ 提交的脑皮层,13 个字段)
organism = Mus musculus
title = C57BL6 mouse male cerebral cortex poly(A)+ RNA
sample name = DRS002786
sample comment = The C57BL6 mouse (Mus musculus; Japan SLC) were kept under a lighting...
样本 C:SAMEA115329281(人,韩国,6 个字段)
Organism = Homo sapiens
scientific name = Homo sapiens
collection_date = 2017-02-06
geographic location (country and/or sea) = South Korea
description = Human sample 32
三条记录的字段名互相对不上:
| 概念 | 样本 A 的键名 | 样本 B 的键名 | 样本 C 的键名 |
|---|---|---|---|
| 物种 | organism |
organism |
Organism / scientific name |
| 组织 | tissue |
无(藏在 title 里) |
无 |
| 性别 | sex |
藏在 title 里(male) |
无 |
| 采集地 | 无 | 无 | geographic location (country and/or sea) |
| 采集时间 | 无 | 无 | collection_date |
| 补充说明 | description(长段实验方法) |
sample comment(饲养条件) |
description(“Human sample 32”) |
这张表的结论,比 §7.5 那句"字段名不统一"要具体得多:
第一,大小写都不统一。 样本 C 用 Organism(大写 O),样本 A/B 用 organism。做列名匹配时必须转小写。
第二,同一个概念可能有两条并行的键。 样本 C 同时有 Organism 和 scientific name,值是同一个 Homo sapiens。这不是冗余,是两个不同的提交模板留下的。
第三,缺失的信息不是"没填",而是"填在别处"。 样本 B 有性别(male)和组织(cerebral cortex),但都在 title 一句话里。按字段筛 attr:sex:male 会漏掉它——这正是 §5.8 那张表里 sex:male 只有 503 万条的原因之一。
第四,字段数的差距直接反映提交成本。 20 → 13 → 6。日本 DDBJ 早期提交(2012 年)和韩国 ENA 提交(2017 年)都只填了最低限度。这不是质量问题,是提交模板本身要求的字段就少。
给使用者的判断:拿到一条 BioSamples 记录,先数字段。6–8 个字段的记录基本只够定位"这是一份什么材料",不足以支撑任何分组分析。 想做人vs鼠、病vs正常的对比,必须自己去 ENA/SRA 补,或者换用已经把字段对齐过的库(如 Expression Atlas)。
§4 关联层:它怎么把数据连起来
4.1 三条关联路径
BioSamples 的价值在关联,关联靠三条路径:
| 路径 | 机制 | 例子(案例样本) |
|---|---|---|
| 向下:样本 → 实验 | SRA accession 等外部引用 |
SRA accession → SRA 里的 run |
| 横向:同一样本的多库记录 | INSDC secondary accession |
同一材料在 NCBI 与 EBI 各有一个号 |
| 向上:样本 → 文献 | publications |
关联的 PubMed 条目 |
这三条路径决定了它能回答什么问题:
- “这个样本测过什么?” → 走向下路径
- “这个样本在两个库里的记录一致吗?” → 走横向路径
- “这个样本出自哪篇论文?” → 走向上路径
4.2 用案例看向下路径
SAMN19844094 的特征里有 SRA accession。这一条把样本和数据连起来了:
SAMN19844094(BioSamples:这是什么)
↓ SRA accession
SRR#######(SRA:测序 run)
↓
FASTQ(实际读段)
所以正确的取数链路是:先查 BioSamples 拿到样本描述,再从 SRA accession 跳到 SRA 拿数据。 不要试图在 BioSamples 里找数据,它没有。
4.3 一个容易被忽略的字段:submittedVia
案例里 submittedVia 是 PIPELINE_IMPORT。这个字段记录了记录是怎么进入 BioSamples 的:
| 值 | 含义 | 对使用者的意义 |
|---|---|---|
PIPELINE_IMPORT |
自动管道导入 | 字段质量取决于上游,BioSamples 未做人工整理 |
| 直接提交(网页/API) | 提交方手工录 | 通常字段更完整 |
| 迁移 | 历史数据迁移 | 可能有遗留格式 |
这个字段是判断记录可靠性的第一道筛子。 自动导入的记录,字段名、填写规范完全由上游决定,BioSamples 只是搬运。看到 PIPELINE_IMPORT 就应该预期:字段命名会很不统一。
4.4 与 INSDC 的关系
BioSamples 不是一个孤立的库,它是 INSDC(International Nucleotide Sequence Database Collaboration)体系的一部分。这个体系由三家机构组成:ENA(欧洲)、NCBI(美国)、DDBJ(日本)。
它们之间的约定是:登录号互认、数据定期同步。这解释了 §3.3 的三个前缀,也解释了为什么大量样本的 update 时间会集中刷新——那是同步动作留下的痕迹。
对使用者的实际含义:同一份样本可能在三个库里都有记录,且编号不同。 去重时不能只看登录号,要看 INSDC secondary accession 这类交叉引用字段。
4.5 一条完整的取数链路:从 SAME 号到 FASTQ
这是把 BioSamples 用起来的核心动作。以案例样本 SAMN19844094 为例,走一遍完整路径。
第一步:从 BioSamples 拿描述与跳转键。
import json, urllib.request
UA = {"User-Agent": "research-script/1.0"}
def biosample(acc):
u = f"https://www.ebi.ac.uk/biosamples/samples/{acc}"
with urllib.request.urlopen(urllib.request.Request(u, headers=UA), timeout=60) as r:
return json.load(r)
b = biosample("SAMN19844094")
print(b["taxId"]) # 10090
print(b["characteristics"]["SRA accession"][0]["text"]) # SRS9276500
print(b["externalReferences"][0]["url"])
# https://www.ebi.ac.uk/ena/browser/view/SAMN19844094
这一步要拿到的关键东西有三个:
| 取值 | 从哪来 | 用途 |
|---|---|---|
taxId 10090 |
记录顶层字段 | 确认物种,防止编号串库 |
SRS9276500 |
characteristics["SRA accession"] |
跳转到测序数据的钥匙 |
| ENA 浏览页 URL | externalReferences[0].url |
人工核对用 |
第二步:理解 SRS 这个号的语义。
这里有一个容易乱的地方。同一条记录里出现了三个号:
| 前缀 | 值 | 语义层级 |
|---|---|---|
SAMN |
SAMN19844094 |
样本(BioSample 层) |
SRS |
SRS9276500 |
样本在 SRA 里的对应编号(SRA Sample) |
SRR(未出现,在下一步才拿到) |
— | 测序 run(真正的数据文件层) |
一次测序可以产生多个 run。 所以 SRS → SRR 是一对多,不能假设一个样本只有一个 FASTQ。
第三步:用 SRS 去 ENA 拿 run 列表。
# ENA Portal API:按 sample accession 反查所有 run
curl -s "https://www.ebi.ac.uk/ena/portal/api/filereport?accession=SRS9276500&result=read_run&fields=run_accession,sample_accession,fastq_ftp,read_count&format=tsv"
返回的是制表符分隔表,fastq_ftp 列直接给出可下载的 FTP 路径(多个文件用分号分隔,对应双端测序的 R1/R2)。
第四步:下载。
# 先从 filereport 取出 fastq_ftp 列,再按分号拆分逐条下载
curl -s "https://www.ebi.ac.uk/ena/portal/api/filereport?accession=SRS9276500&result=read_run&fields=run_accession,fastq_ftp&format=tsv" \
| awk -F'\t' 'NR>1{n=split($2,a,";"); for(i=1;i<=n;i++) print "ftp://" a[i]}' \
| while read -r url; do wget -c "$url"; done
这段脚本没有硬编码任何 run 号——它从 ENA 的返回里现取。这是唯一稳妥的写法,因为 SRS → SRR 的映射会随时间增加(同一个样本后续可能补测)。
整条链路的形状:
SAMN19844094 ← BioSamples:这是什么样本(20 个字段描述)
│ characteristics["SRA accession"]
▼
SRS9276500 ← SRA Sample:样本在测序库里的身份
│ ENA Portal API(filereport)
▼
SRR####### ← 测序 run:可能有多个
│ fastq_ftp
▼
FASTQ 文件 ← 真正的数据
三个必须注意的地方:
① 不要在 BioSamples 里找数据。 它只有"样本是什么",没有"测出来多少"。找不到序列不是接口问题,是定位问题。
② SRA accession 字段可能缺失。 案例样本有,但不是全部记录都有。缺失意味着这个样本还没被测序,或者提交方没填——两种情况的处理方式不同,但都无法从 BioSamples 本身区分。
③ 案例样本的 SRS 与 INSDC secondary accession 是同一个值(SRS9276500)。 这不是巧合,而是说明对于 NCBI 导入的样本,这两个字段经常重合。做去重时不要以为它们是两个不同的引用。
BioSamples 不是一个孤立的库,它是 INSDC(International Nucleotide Sequence Database Collaboration)体系的一部分。这个体系由三家机构组成:ENA(欧洲)、NCBI(美国)、DDBJ(日本)。
它们之间的约定是:登录号互认、数据定期同步。这解释了 §3.3 的三个前缀,也解释了为什么大量样本的 update 时间会集中刷新——那是同步动作留下的痕迹。
对使用者的实际含义:同一份样本可能在三个库里都有记录,且编号不同。 去重时不能只看登录号,要看 INSDC secondary accession 这类交叉引用字段。
§5 接口层:怎么取
5.1 两个端点
| 用途 | URL |
|---|---|
| 分页列表 | https://www.ebi.ac.uk/biosamples/samples?size=N&page=P |
| 单条记录 | https://www.ebi.ac.uk/biosamples/samples/{accession} |
注意:官方的 /summary 端点已废弃,实测返回 404。旧代码如果还在用,需要改成 /samples 分页。
5.2 单条取数
# 取一条样本的完整 JSON
curl -s -A "Mozilla/5.0" \
"https://www.ebi.ac.uk/biosamples/samples/SAMN19844094" \
-o sample.json
有一个坑必须提醒:直接请求部分 SAMEA 前缀的记录会返回 403。实测 SAMEA104688033 就是 403。原因不是该样本不公开,而是单条记录的访问控制与列表接口不完全一致。稳定做法是走列表接口取,或先确认该记录的 status 为 PUBLIC。
5.3 分页与实际限制
import json, urllib.request
HEADERS = {"User-Agent": "biosamples-client/1.0"}
def fetch_page(page=0, size=100, filt=None):
"""按页码取一页。加 page=0 才会返回 totalElements(§5.5 坑二)。"""
u = f"https://www.ebi.ac.uk/biosamples/samples?size={size}&page={page}"
if filt:
u += f"&filter={urllib.parse.quote(filt, safe='')}"
req = urllib.request.Request(u, headers=HEADERS)
with urllib.request.urlopen(req, timeout=90) as r:
return json.load(r)
if __name__ == "__main__":
d = fetch_page(0, 100)
print("总数:", d["page"]["totalElements"])
for s in d["_embedded"]["samples"][:3]:
print(s["accession"], s.get("taxId"), len(s.get("characteristics", {})))
实测的三条限制:
| 限制 | 现象 | 应对 |
|---|---|---|
size 上限 |
size=1000 返回 400 |
用 size=100 |
| 深层分页 | page=12000000 返回 400 |
用 cursor 分页(见下) |
| 单条部分记录 | SAMEA 开头某些记录 403 |
走列表接口 |
响应里除了 first/self/next/last,还有一个 cursor 链接:
"cursor": {"href": "https://www.ebi.ac.uk/biosamples/samples?applyCurations=true&cursor=*&size=1"}
这是遍历全库的正确方式。 游标分页不受深度限制,适合做全量扫描或大规模抽样。
5.4 按物种过滤
5,610 万条里混着所有物种,实际使用几乎一定需要过滤。但过滤的正确写法不直观,而且有三个会静默出错的坑——这是本节最值得读的部分。
正确的写法是给字段名加 attr: 前缀:
# 正确:物种用 taxId,且必须加 attr: 前缀
u = ("https://www.ebi.ac.uk/biosamples/samples"
"?size=100&page=0&filter=attr:taxId:9606")
错误写法 filter=taxId:9606 会直接返回 HTTP 500:
{"timestamp":"2026-10-25T08:46:10.025+00:00","status":500,
"error":"Internal Server Error",
"message":"Cannot infer filter type from string taxId:9606",
"path":"/biosamples/samples"}
这个错误信息是有价值的:Cannot infer filter type 说明接口在解析 filter 时需要一个"类型前缀",attr: 就是告诉它"我要匹配的是样本属性"。同族的还有 domain:(来源通道),这两个够用。
5.5 三个会静默出错的坑
这一节全部来自实测,不是文档摘要。
坑一:attr:taxId 只覆盖了 2.96% 的样本。
用 attr:taxId:9606 过滤人类样本,返回的是 12,419 条。但换用 attr:organism:Homo sapiens,返回 15,241,833 条。
两个数差了一千多倍。原因:
| 过滤字段 | 命中条数 | 说明 |
|---|---|---|
attr:taxId:9606 |
12,419 | 只有显式把 taxId 当特征字段填了的样本 |
attr:organism:Homo sapiens |
15,241,833 | 几乎全部人类样本 |
attr:taxId:9913(牛) |
17,812 | 部分来源会填 taxId |
attr:taxId:9031(鸡) |
1 | 几乎没人填 |
attr:taxId:10090(小鼠) |
0 | — |
taxId在记录顶层是一个正经字段,但在characteristics里几乎没人填。filter=匹配的是characteristics,不是顶层字段。
正确的做法:物种用 attr:organism:<学名>,不要用 attr:taxId。学名要写拉丁名全称(Homo sapiens、Mus musculus),不能写 human——attr:organism:human 只命中 252 条(小写)或 13 条(大写 H),而 Homo sapiens 命中 1,524 万。
坑二:size=1&filter=... 不返回总数,必须带 page=0。
这是一个很隐蔽的行为差异:
| 请求 | 是否返回 page 段 |
|---|---|
?size=1&page=0 |
✅ 有 totalElements: 56108301 |
?size=1&filter=attr:taxId:9606 |
❌ 只有 _embedded 和 _links |
?size=1&page=0&filter=attr:taxId:9606 |
✅ 有 totalElements: 12419 |
滤条件一旦出现,接口就自动切到 cursor 分页模式,page 段被静默丢弃。 加回 page=0 才会强制走页码模式并返回计数。这是取"某一类样本有多少条"的唯一办法——没有专门的 count 端点。
坑三:两个 domain 过滤同时写,会静默退化成只认一个。
# 期望:NCBI 导入 ∩ 迁移(这个写法是错的,见下文)
# ?filter=domain:self.BiosampleImportNCBI AND domain:self.BioSamplesMigration
实测 domain:self.BiosampleImportNCBI AND domain:self.BioSamplesMigration 返回 1,968,930——恰好等于后者的单独结果,不是交集(交集应为 0,因为一条记录的 domain 只能有一个值)。domain: 是不支持 AND 的,多写会被忽略而不是报错。
三个坑的共同点:都不报错。 坑一返回 12,419 条看起来像个正常结果,坑二返回一个没有计数的合法 JSON,坑三返回一个非零数字。如果不去对照总量做交叉验证,任何一个都会让你拿到错的数据还以为成功了。
5.6 一次拿全:物种分布实测
用 attr:organism 加 page=0,可以把这个库的真实物种构成量出来。以下全部为 2026-10-25 实测的 totalElements:
| 物种 | 学名过滤 | 条数 | 占比 |
|---|---|---|---|
| 人 | Homo sapiens |
15,241,833 | 27.2% |
| 小鼠 | Mus musculus |
3,303,771 | 5.9% |
| 大肠杆菌 | Escherichia coli |
676,179 | 1.2% |
| 酿酒酵母 | Saccharomyces cerevisiae |
331,239 | 0.6% |
| 大鼠 | Rattus norvegicus |
278,111 | 0.5% |
| 拟南芥 | Arabidopsis thaliana |
258,289 | 0.5% |
| 黑腹果蝇 | Drosophila melanogaster |
206,934 | 0.4% |
| 水稻 | Oryza sativa |
141,671 | 0.3% |
| 斑马鱼 | Danio rerio |
139,633 | 0.2% |
| 秀丽隐杆线虫 | Caenorhabditis elegans |
78,507 | 0.1% |
这些加起来只占 36.9%。剩下 63% 是什么?
宏基因组。实测:
| 类群 | 条数 |
|---|---|
soil metagenome |
1,367,045 |
human gut metagenome |
1,265,233 |
metagenome(泛称) |
1,180,261 |
gut metagenome |
643,749 |
marine metagenome |
380,424 |
BioSamples 不是"人类生物样本库",它更接近"全球测序样本登记处"。 单看人类样本会觉得它是一个临床样本库,但按总量看,环境与肠道宏基因组占了大头——这些样本没有"患者"概念,是
patient_tags该写"不适用"的原因。
顺带一个可以验证的推论:土壤宏基因组 136.7 万条,这个量级只可能来自少数几个大型测序计划。样本登记库的分布,本质上是几个大项目在堆数量——这条规律在第 8 章讲"它为何能持续"时会再用到。
5.7 状态与可访问性
| 过滤 | 条数 | 占比 |
|---|---|---|
| (无过滤) | 56,108,301 | 100% |
attr:status:PUBLIC |
55,863,835 | 99.56% |
attr:status:PRIVATE |
229,824 | 0.41% |
99.6% 是公开的。 这个比例解释了为什么 §5.2 提到的 403 是个例——SAMEA104688033 恰好落在 22.9 万条私有样本里,不是接口问题。
但"公开"不等于"可在列表里看见":PRIVATE 样本在列表接口里仍会出现(带 status: PRIVATE 标记),只是单条取详情会 403。做批量抓取时必须显式过滤 attr:status:PUBLIC,否则会在某些记录上被 403 打断。
5.8 常用描述字段的可筛条数
过滤不仅能筛物种,还能直接问"有多少样本描述了某个属性"。以下全部实测:
| 过滤 | 条数 | 说明 |
|---|---|---|
attr:sex:female |
5,040,414 | 女性/雌性 |
attr:sex:male |
5,031,260 | 男性/雄性 |
attr:sex:not applicable |
353,459 | 不适用 |
attr:sex:missing |
161,888 | 明确填了"缺失" |
attr:sex:pooled male and female |
70,788 | 混合样本(案例样本属此类) |
attr:tissue:blood |
256,556 | 组织=血液 |
attr:tissue:brain |
139,330 | 组织=脑 |
attr:tissue:liver |
128,922 | 组织=肝 |
attr:tissue:lung |
51,765 | 组织=肺 |
attr:tissue:tumor |
23,081 | 组织=肿瘤 |
attr:disease:normal |
112,833 | 疾病=正常 |
attr:disease:cancer |
14,181 | 疾病=癌症 |
attr:disease:COVID-19 |
5,312 | 疾病=新冠 |
attr:age:missing |
236,159 | 年龄明确标缺失 |
attr:dev_stage:adult |
304,483 | 发育阶段=成体 |
attr:strain:C57BL/6 |
433,440 | 品系=C57BL/6 |
attr:geographic location (country and/or sea):China |
467,770 | 地理来源=中国 |
这张表最该被读出三件事:
第一,性别字段的性别比几乎完美平衡(504 万 vs 503 万)。 这在真实生物样本里不寻常——它更像是"提交规范要求填 sex,于是两边各填一半"的产物。不能拿这个比例去论证队列设计合理。
第二,missing 是一个真实的取值,不是空值。 sex:missing 16.2 万、age:missing 23.6 万。这意味着"没填"和"填了不知道"在这个库是两回事:前者字段不出现,后者字段出现且值为 missing。做缺失率统计时必须把两者合并,否则会低估。
第三,字段名带空格和括号是可以过滤的。 attr:geographic location (country and/or sea):China 这种写法能正常返回 46.8 万条。所以不要因为字段名"不像程序变量"就放弃用它——URL 编码后照样能查。
§6 使用场景:三类典型工作流
6.1 场景一:从登录号反查样本(最常见)
手里有 SRA/ENA 的 run 号,想知道样本是什么:
SRR#######(run 号)
→ 查 ENA/SRA 得到 sample accession(SAMN/SAMEA)
→ 到 BioSamples 取该 accession
→ 拿到 taxId、组织、疾病、年龄等
关键点:run 号和 sample 号是两级。 一个样本可以被测多次,产生多个 run。要样本级信息查 BioSamples,要测序级信息查 ENA。
6.2 场景二:按条件筛样本做队列
比如"筛人类肺组织样本":
def match_lung(s):
if s.get("taxId") != 9606:
return False
ch = s.get("characteristics", {})
# 键名不统一,要同时试多种写法
for k in ("tissue", "Tissue", "organism part", "organism_part"):
vals = ch.get(k)
if vals and any("lung" in (v.get("text") or "").lower() for v in vals):
return True
return False
这段代码里那个 for 循环就是本库的核心难点:同一个"组织"概念,键名可能有五种写法。写死一个键名会漏掉大部分数据。
6.3 场景三:样本登记(作为提交方)
如果要把自己的样本登记成可引用条目,流程是:通过 Annotare / ENA 提交,样本部分会自动落到 BioSamples 拿到 SAME 号。这个号就是论文里应该写的那一个。
好处很实际:
- 数据被引用的最小单位从"论文"变成了"样本"
- 样本可以独立被检索与引用
- 未来做元分析时,别人的样本可以直接被你复用
这也解释了官方为什么强调"参考样本"(1000 Genomes、HipSci、FAANG)——这些样本的价值就是被反复引用。
§7 质量评估与局限性
7.0 阅读本节前需要知道的一件事
评价 BioSamples 时,最容易犯的框架错位是:用"标准化数据库"的标准要求它。
如果按标准化数据库看,它的缺陷一眼可见:字段名不统一、同义不同名、大量自动导入未经整理、单条记录只有 50 个字段。
但 BioSamples 不是标准化数据库。它的产品是"把分散的样本登记聚到一处"。 评价它应该用聚合层的标准:覆盖面、关联是否完整、编号是否稳定、接口是否可用。
这两个框架的差别很关键:按前者看,它会显得粗糙;按后者看,它是整个 INSDC 体系里不可绕过的一环。下面的评估按聚合层标准进行。
7.1 优点一:规模与覆盖面
5,610 万条,覆盖所有 INSDC 物种。这个规模不是自建的,是聚合来的,这也是它唯一可能达到这种量级的方式。
对做"样本级元分析"的研究,这个覆盖面意味着:大多数公开测序项目的样本,都能在这里查到。
7.2 优点二:编号稳定,长期可引用
SAME/SAMN 号一旦发出就不会变,且跨库互认。这是做可复现研究的基础——引用样本时写这个号,比写"某论文里那个肺组织样本"要可靠得多。
7.3 优点三:自动同步,不用自己维护映射
NCBI 的样本会通过管道自动同步过来(1,805 万条)。对使用者来说,等于用一套接口访问了两个库的样本层。
自己维护这种映射的工作量极大:编号体系不同、更新节奏不同、字段名不同。BioSamples 把这件事做了。
7.4 优点四:站点本身是一份机器可读的目录声明
https://www.ebi.ac.uk/biosamples/ 这个地址,用浏览器打开是网页,用 curl 取回来是一段 Schema.org DataCatalog JSON-LD:
{
"@type": "DataCatalog",
"name": "BioSamples database",
"description": "BioSamples stores and supplies descriptions and metadata about biological samples...",
"provider": {"@type": "Organization", "name": "BioSamples", "email": "biosamples@ebi.ac.uk"},
"sourceOrganization": {"@type": "Organization", "name": "The European Bioinformatics Institute (EMBL-EBI)"},
"dataset": [{"@type": "Dataset", "@id": "https://www.ebi.ac.uk/biosamples/samples"}]
}
这说明站点是按"内容协商"设计的:请求头不同,返回不同格式。这对机器抓取非常友好——不需要解析 HTML,直接拿结构化数据。
这种做法在生物数据库里还不普遍,值得作为一个正面样本记下来。
7.5 局限性一:字段名不统一,这是最硬的一条
characteristics 的键由提交方自填,没有任何受控词表约束。后果:
| 同一个概念 | 可能的键名 |
|---|---|
| 组织 | tissue / Tissue / organism part / organism_part / source_name |
| 细胞类型 | cell_type / cell type / CellType |
| 年龄 | age / Age / donor_age |
| 疾病 | disease / disease_state / disease state |
| 性别 | sex / gender |
这意味着"按字段名拼表"这条路走不通。 想做跨样本分析,必须先做一层键名归一——这是使用这个库最大的隐性成本。
对比一下:Expression Atlas 用 EFO 本体做映射,所以它的字段可以直接聚合。BioSamples 没有这一层,它把归一的责任留给了使用者。
7.6 局限性二:50 个字段的上限,且截断无提示
单条记录上限 50 个特征字段。复杂的临床队列可能超限,超出部分静默丢弃。
看到"正好 50 个字段"的记录时,应当怀疑被截断。这个限制也说明它的定位:描述性登记,不是临床数据库。
7.7 局限性三:自动导入的比例过高,质量取决于上游
PIPELINE_IMPORT 占了很大比例。这类记录:
- 字段完整度由上游决定
- 命名规范由上游决定
- BioSamples 不做人工核对
结果是同一个库内数据的质量参差极大。 有的记录 20 个字段详实规范,有的只有 3 个字段(物种 + 来源 + 编号)。
7.8 局限性四:接口有未文档化的限制
实测到的问题:
| 问题 | 现象 |
|---|---|
/summary 端点 |
已废弃,返回 404(旧代码会挂) |
size 上限 |
1000 返回 400 |
| 深层分页 | page=12000000 返回 400 |
单条 SAMEA 记录 |
部分返回 403 |
| 域值拼写 | 错值返回 200 + 0 条,不报错 |
这些都没写在文档里,只能实测发现。 对一个有 5,610 万条记录的库来说,批量访问的限制应该明确公布——否则使用者会在跑到一半时才发现问题。
7.9 DAIMS 24 项数据就绪度评估
| # | 检查项 | 状态 | 说明 |
|---|---|---|---|
| 1 | 数据集目的明确 | ✅ | DataCatalog 声明与 About 均明示 |
| 2 | 数据来源可溯 | ✅ | domain、submittedVia、INSDC center name |
| 3 | 采集方法说明 | ⚠️ | 描述了聚合机制,但未逐渠道说明同步规则 |
| 4 | 标注者信息 | ⚠️ | 有中心名,但无具体提交人 |
| 5 | 标注一致性 | ❌ | 键名无受控词表,同义不同名普遍 |
| 6 | 版本控制 | ⚠️ | 有 update 时间戳,无版本号 |
| 7 | 许可明确 | ⚠️ | 站点声明清晰,但单条记录条款随来源而变 |
| 8 | 访问方式 | ✅ | 分页列表 + 单条 JSON + 内容协商 |
| 9 | 数据格式规范 | ✅ | JSON 结构统一,字段固定 |
| 10 | 机器可读 | ✅ | JSON 接口 + Schema.org DataCatalog |
| 11 | 术语标准化 | ❌ | 无本体映射层,tag 字段大多为空 |
| 12 | 缺失值处理 | ⚠️ | 字段缺失即不出现,无显式标记 |
| 13 | 样本量披露 | ✅ | 精确到条,totalElements 可查 |
| 14 | 偏倚说明 | ⚠️ | 未讨论物种与地理分布偏斜 |
| 15 | 局限性声明 | ⚠️ | 无集中式说明 |
| 16 | 伦理声明 | ⚠️ | 含人类样本,站内无集中伦理说明 |
| 17 | 隐私保护 | ⚠️ | 人类样本的 age/sex/disease 字段需使用者自行评估 |
| 18 | 使用建议 | ✅ | 有文档与 API 说明 |
| 19 | 引用规范 | ✅ | 提供 PMID 文献 |
| 20 | 维护承诺 | ✅ | EMBL-EBI 长期维护,持续同步 |
| 21 | 社区治理 | ⚠️ | 有联系邮箱,无公开治理机制 |
| 22 | 错误报告机制 | ✅ | biosamples@ebi.ac.uk |
| 23 | 下游兼容 | ✅ | ENA、ArrayExpress、Expression Atlas 均消费 |
| 24 | 可复现性 | ⚠️ | 编号稳定(好),但记录内容会变(update 刷新) |
评估总结:24 项中 11 项满足、11 项部分满足、2 项不满足。
两项不满足是同源的:都在"标准化"这一层(标注一致性、术语标准化)。这不是疏忽,是设计取舍——聚合层选择兼容上游,就放弃了统一。
这个取舍有明确的代价与收益:
| 收益 | 代价 | |
|---|---|---|
| 不做标准化 | 覆盖 5,610 万条,几乎全量 | 使用者必须自己归一 |
| 若做标准化 | 可直接聚合 | 只能收录配合规范的提交方,覆盖会大幅缩水 |
BioSamples 选了前者。 评价这个选择要看用途:做"样本检索"它是对的,做"直接建模"它就不够。
7.10 外部验证矩阵
| 验证维度 | 外部依据 | 结论 |
|---|---|---|
| 机构权威 | EMBL-EBI,INSDC 三家之一 | ✅ |
| 体系地位 | INSDC 样本层的欧洲登记处 | ✅ |
| 学术记录 | 有 PubMed 收录的数据库更新文献(PMID 24265224) | ✅ |
| 上游依赖 | NCBI BioSample、ENA、ArrayExpress | ✅ |
| 领域采纳 | ENA、ArrayExpress、Expression Atlas 均引用其编号 | ✅ |
| 独立性 | 由 EBI 独立运营,但内容大量镜像自 NCBI | ⚠️ 聚合性质决定了非完全独立 |
"独立性"一项保留:它的内容有三分之一明确是 NCBI 的镜像。 这不影响其价值,但读者应知道——它不是一个独立的样本库,是 INSDC 的欧洲入口。
§8 版本与生态
8.1 更新模式:持续同步,无版本号
BioSamples 没有版本号体系。状态由三个时间戳体现(§3.1)。官方的更新是持续进行的同步,不是定期发版。
代价:引用某条记录时必须记录访问日期,因为记录内容会变。编号不变,内容会变——这是这类登记库的固有特性。
8.2 为什么它存在:一个具体的碎片化问题
官方的 DataCatalog 描述里说得很清楚:
Samples are either ‘reference’ samples (e.g. from 1000 Genomes, HipSci, FAANG) or have been used in an assay database such as the European Nucleotide Archive (ENA) or ArrayExpress.
这句话点出了它的存在理由:样本分散在多个检测数据库里,每个库对样本的描述各不相同。
如果一份肺组织样本做了 RNA-seq(数据在 ENA)和甲基化芯片(数据在 ArrayExpress),两个库各自描述它,字段不同、颗粒度不同。BioSamples 把这两份描述收敛到同一个 SAME 号下,从此"这是同一个样本"变成一个可验证的事实,而不是要人去猜。
8.3 为什么这种模式能持续
三个支撑点:
第一,它是 INSDC 的组成部分,不是独立项目。 INSDC 由 ENA、NCBI、DDBJ 三家机构共同维护,样本编号互通是体系性约定。BioSamples 的存续依托于这个跨机构协议,不是某个课题的经费。
第二,成本被分摊了。 1,805 万条来自 NCBI,92 万条来自 ENA——大部分内容不是它自己生产的,它主要承担聚合与接口的工作。这让维护成本远低于"自建 5,610 万条"。
第三,它是别人绕不开的一环。 ENA、ArrayExpress、Expression Atlas 都引用它的编号。一旦成为体系里的一环,就有了存在的基础——即便它自己不产出数据。
8.4 与 NCBI BioSample 的关系:镜像,不是竞争
这是最容易被误解的一点:
| 维度 | BioSamples | NCBI BioSample |
|---|---|---|
| 覆盖 | 欧洲原生(SAMEA)+ NCBI 镜像(SAMN) | 美国原生(SAMN) |
| 编号 | SAME / SAMN / SAMD | SAMN |
| 关系 | 聚合 + 镜像 | 原生发号方之一 |
BioSamples 里的 SAMN 号,本质上就是 NCBI 的号。 它做的是同步,不是重新登记。所以:
- 只在 NCBI 提交的样本,两边都能查到(因为同步)
- 只在 EBI 提交的样本,只有 BioSamples 有(
SAMEA),NCBI 未见得同步
做全球样本检索时,两个都要查,不能只查一个。
8.5 在生态中的位置
样本层: BioSamples(SAME/SAMN) ← 本条目
↑ 同步 ↑ 引用
实验层: ENA / ArrayExpress / SRA
↓ 重处理
结果层: Expression Atlas / Single Cell Expression Atlas
BioSamples 处在最底层,也最容易被忽略的一层——因为它不含任何测量值。但没有这一层,上层的样本描述就会散在各处,无法对齐。
8.6 它为什么不会消失:三个可验证的指标
§8.3 讲的是机制上的理由。这里给三个能从数据本身验证的指标——如果一个库正在萎缩,这三个数会同时下行。
指标一:总量还在不在涨。
56,108,301 条是 2026-10-25 的实测值。这个数字的单次读取没有说服力,有说服力的是它与上游的关系:NCBI 导入 1,805 万 + ENA 导入 93 万 + 迁移 197 万 = 2,095 万,只占总量的 37.3%。剩下的 3,515 万来自其他通道(含大量直接提交与早期数据)。
换句话说,它的大部分内容不在"三大来源"里。 这意味着它不是某个上游库的附庸,而是有独立的存量。
指标二:新样本还在不在进。
看 release 时间。案例样本 SAMN19844094 的 create 是 2021-06-24,release 是 2022-12-13——从创建到公开隔了一年半,这是 NCBI 侧的禁发期设置。而 update 到了 2022-12-15,比 release 晚两天,说明公开后还有一次小幅修订。
这三个时间点的相对顺序(create → submitted → release → update)本身就是活跃度的证据。 一个停止维护的库,不会有 create 与 submitted 之间一年半的间隔。
指标三:私有样本还在不在受理。
§5.7 实测 status:PRIVATE 有 229,824 条。私有样本是"已提交但未公开"的存量——一个只读不写的归档库不会有这一类。22.9 万条私有样本意味着有持续的新提交在进来,只是还没到解禁日期。
这三个指标合起来说明:它不是在收尾,是在继续长。 对使用者的含义很直接——现在投入做键名归一层的成本,不会因为库停摆而作废。 这是评估一个数据基础设施是否值得长期依赖时,最实际的一条判断。
§9 使用指引
9.1 三种角色的路径
| 你是 | 建议路径 |
|---|---|
| 数据工程师 | 用 cursor 分页遍历 → 按 taxId 切片 → 自己建键名归一层 |
| 研究者 | 按登录号单条查 → 看 SRA accession 跳转取数 |
| 提交方 | 通过 ENA/Annotare 提交 → 拿到 SAME 号写进论文 |
9.2 一个含键名归一的筛选流程
1. 确定目标物种的学名(人是 Homo sapiens,不是 taxId:9606,理由见 §5.5)
2. 用 filter=attr:organism:Homo sapiens 缩小范围,配 page=0 拿总数
3. 抽样 100 条,看实际出现的特征键名
└ 不要凭想象写键名(§2.3)
4. 建立键名同义词表(tissue/Tissue/organism part/...)
5. 按同义词表做批量匹配
6. 通过 SRA accession 跳到 ENA 取数据
7. 记录访问日期
└ 编号不变但内容会变
第 3 步是关键:这个库的正确用法是"先观察再写代码",而不是"先写代码再调试"。 因为键名没有规范,只能实证。
第 1 步和第 2 步之所以要这么写,是因为踩过坑:一开始用 attr:taxId:9606 只拿到 12,419 条,看起来像个"人类样本不多"的结论。换成 attr:organism:Homo sapiens 才拿到 1,524 万条——差了 1,227 倍。如果没做交叉验证,整个下游分析都会建立在 0.08% 的错误样本上。
9.3 提交方:怎么把一个样本放进来
如果你手上有一批新样本要登记,路径和上面使用者的路径正好相反。
两条主流通道:
| 通道 | 适用 | 拿到的号 |
|---|---|---|
| ENA / Webin | 欧洲提交,核酸序列数据 | SAMEA 前缀 |
| NCBI BioSample | 美国提交 | SAMN 前缀 |
| DDBJ | 日本提交 | SAMD 前缀 |
三者都是 INSDC 成员,号会自动同步到 BioSamples。 所以你不需要直接向 BioSamples 提交——它没有独立的提交入口,只接收来自三家的同步。
提交时必须填的最小字段集(从样本 C 的 6 个字段反推):
organism / scientific name 物种学名(必填)
collection_date 采集日期
geographic location 采集地
description 一句话说明
这四项之外,能多填就多填。 理由很实际:§5.8 那张表显示,attr:sex:female 只有 504 万条命中,而人类样本有 1,524 万条——超过六成的人类样本没有可筛的性别字段。这些分析需求不会消失,只会变成下游使用者的返工。
一个提交方常犯的错:把信息塞进 title 或 sample comment。
样本 B(SAMD00000593)就是典型:性别(male)和组织(cerebral cortex)都在 title 里,字段层面完全查不到。这对提交方零成本,但对使用者是灾难——因为按字段过滤是唯一可规模化的方式,塞进自由文本等于把这条样本从所有筛选里摘出去了。
正确的填法是把每一条信息放进一个独立字段:
不要:title = "C57BL6 mouse male cerebral cortex poly(A)+ RNA"
而要:organism = Mus musculus
sex = male
tissue = cerebral cortex
strain = C57BL/6
title = "C57BL6 mouse cerebral cortex poly(A)+ RNA"
信息量一样,可筛性完全不同。 这是提交方能为下游做的、成本最低的一件事。
9.4 一个必须自己做的动作:建键名同义词表
§3.5 那三份记录说明了一个事实:BioSamples 不会替你把 tissue 和 Tissue 对齐,也不会告诉你 organ 和 tissue 是一回事。 这件事只能自己做。
一个可以直接用的起步表(按 §5.8 实测命中的字段名整理):
| 目标概念 | 库中实际出现过的键名 | 建议归一到 |
|---|---|---|
| 物种 | organism、Organism、scientific name、species、commonName |
organism |
| 组织 | tissue、Tissue、organism part、body site |
tissue |
| 性别 | sex、Sex、gender |
sex |
| 年龄 | age、Age、dev_stage、developmental stage |
age |
| 疾病 | disease、Disease、disease state、phenotype |
disease |
| 品系 | strain、strain or line、genotype |
strain |
| 采集地 | geographic location (country and/or sea)、geo_loc_name、country |
geo_loc |
| 采集时间 | collection_date、collection date、isolation date |
collection_date |
这张表最重要的用途不是"合并",是"知道什么时候合不了"。
age = 8-week-old(样本 A)、dev_stage = adult(30.4 万条)、age = missing(23.6 万条)——这三个值没法归一到同一个刻度上。一个是具体周龄、一个是粗粒度阶段、一个是缺失。强行合并会造出一个语义混乱的 age 列,问题从"没有年龄"变成"年龄看起来有但不准"。
正确做法是保留原文,另加一列归一结果:
NORM = {"8-week-old": 56, "adult": None, "missing": None} # 单位:天
None 必须区分"未归一"和"缺失",否则下游统计会把"还没处理"算成"没有数据"。
这件事的投入产出比值得算一下:§8.6 的三个指标说明这个库还在长,键名只会更杂,不会更整齐。一次建好同义词表,之后每年维护一次,就能持续用下去——比每次重新摸索便宜得多。
如果不想自己做:Expression Atlas、Single Cell Expression Atlas 这类"重处理层"已经在内部做过了(它们的样本表就是从 BioSamples/ENA 来的,但字段已经对齐)。代价是覆盖面小得多——4,562 个实验 vs 5,610 万条样本。这就是 §4.1 说的"向上路径"的实际含义:多花一层力气,换一套对得齐的字段。
9.3 许可与合规检查清单
| # | 检查项 | 说明 |
|---|---|---|
| 1 | 样本是否 PUBLIC | status 字段 |
| 2 | 是否人类样本 | taxId 9606,涉及隐私评估 |
| 3 | 人类样本的年龄/性别/疾病字段 | 组合起来可能具可识别性 |
| 4 | 原始数据条款 | 回 ENA/SRA,适用其各自规定 |
| 5 | 是否需署名 | 引用 PMID 24265224 |
9.4 在 AI 数据准备五段流程中的位置
| 阶段 | BioSamples 的作用 |
|---|---|
| 1 采集 | 主要作用:找样本、筛队列 |
| 2 归一 | 部分作用:提供键值对,但键名要自己归一 |
| 3 质检 | 不提供质量评级 |
| 4 标注 | 部分作用:有描述字段,但无本体映射 |
| 5 训练 | 不涉及 |
集中在第 1 段。这与 Expression Atlas(第 2、4 段)刚好互补——一个负责"找",一个负责"算"。
9.5 常见误区与纠正
| 常见做法 | 问题 | 正确做法 |
|---|---|---|
| 在 BioSamples 找测序数据 | 它只有元数据 | 用 SRA accession 跳 ENA |
按 tissue 一个键名筛 |
键名有五种写法 | 建同义词表 |
用 update 判断数据新旧 |
会因同步被刷新 | 用 release |
猜域值(self.ENA 等) |
错值返回 0 条不报错 | 抽样看真实值 |
用 size=1000 批量取 |
返回 400 | 用 size=100 或 cursor |
| 假设 5,610 万 = 5,610 万份实验 | 样本与实验一对多 | 分别统计 |
§10 AI 使用声明
| 项目 | 声明 |
|---|---|
| 数据性质 | 样本级元数据,不含测量值 |
| 可否直接用于训练 | 可作样本侧特征,但键名必须自己归一后才可用 |
| 建议用途 | 队列筛选、样本溯源、跨库关联、元分析 |
| 不建议用途 | 当作测量数据源;当作临床变量表(上限 50 字段) |
| 伦理注意 | 含大量人类样本,age/sex/disease 组合具可识别性,使用须评估 |
| 引用要求 | 引用 PMID 24265224,并标注访问日期与登录号 |
| 版本标注 | 无版本号,须注明访问日期 |
| 页面状态 | published |
常见误读(避坑清单)
| 常见说法 | 实际情况 | 证据 |
|---|---|---|
| “BioSamples 能下载测序数据” | 只有元数据,序列在 ENA | DataCatalog 自述:stores descriptions and metadata |
| “5,610 万条样本 = 5,610 万个实验” | 样本与实验一对多,参考样本可能无实验 | 官方定义含 ‘reference’ samples |
| “字段是标准化的,可以直接拼表” | 键名由提交方自填,无受控词表 | cell_type/cell type 等并存 |
| “它是 EBI 自建的样本库” | 1,805 万条镜像自 NCBI | filter=domain:self.BiosampleImportNCBI |
“/summary 接口能查总数” |
已废弃,返回 404 | 实测 https://www.ebi.ac.uk/biosamples/summary |
| “域值可以按常理猜” | 错值返回 200 + 0 条,不报错 | self.ENA 等均返回 0 |
“size=1000 可以一次多取” |
返回 400 | 实测上限约 100 |
| “单条记录可以按 SAME 号随便取” | 部分 SAMEA 记录返回 403 |
实测 SAMEA104688033 |
“update 时间反映样本新旧” |
会因 INSDC 同步被刷新 | 2014 年样本 update 到 2023 年 |
| “字段数没有上限” | 上限 50 个,超出静默丢弃 | 官方记录规范 |
| “它和 NCBI BioSample 是两个竞争的库” | 是镜像/聚合关系,SAMN 号相同 | §8.4 |
FAQ
Q1:BioSamples 和 NCBI BioSample 有什么区别?
NCBI 是原生发号方(SAMN),BioSamples 是欧洲侧入口加聚合层(SAME 自有号 + SAMN 镜像)。同一批 NCBI 样本两边都能查到。
Q2:一共多少条样本?
实测 totalElements = 56,108,301。这个数字每天在变,引用时须标注取数日期。
Q3:能下测序数据吗?
不能。只有元数据。找数据用 SRA accession 或 INSDC 相关字段跳到 ENA/SRA。
Q4:为什么按 tissue 筛不出东西?
键名不统一,同一概念可能是 Tissue、organism part、source_name 等。必须建同义词表。
Q5:SAMEA / SAMN / SAMD 有什么区别?
分别是 EBI、NCBI、DDBJ 发的号。前缀说明样本最初在哪里登记。
Q6:为什么有些 SAMEA 记录直接取会 403?
单条接口的访问控制与列表不完全一致。走列表接口取更稳。
Q7:怎么遍历全库?
用响应里的 cursor 链接,不要用 page=N 递增——深层分页会返回 400。
Q8:update 时间为什么这么新?
INSDC 之间定期同步,同步会刷新 update。想看样本原始时间用 release。
Q9:能用来做机器学习特征吗?
可以,但前提是自己做键名归一。直接用原始键值对拼表,会得到一堆稀疏且语义混乱的列。
Q10:为什么单条记录最多 50 个字段?
官方设计如此。这也说明它的定位是描述性登记,不是临床变量库。
Q11:submittedVia: PIPELINE_IMPORT 意味着什么?
记录是自动管道导入的,字段质量取决于上游,BioSamples 未做人工整理。看到这个值应预期字段命名不统一。
Q12:人类样本有多少条?
用 filter=taxId:9606 可实时统计。本文未固定该数字,因为它随时变化——比它更值得关注的是"人类样本里临床字段的完整度",而这需要按任务自己统计。
术语表
| 术语 | 含义 |
|---|---|
| BioSamples | EMBL-EBI 的样本元数据登记库,本文条目 |
| SAMEA / SAMN / SAMD | 分别由 EBI / NCBI / DDBJ 发出的样本登录号前缀 |
| INSDC | International Nucleotide Sequence Database Collaboration,ENA+NCBI+DDBJ 的协作体系 |
| ENA | European Nucleotide Archive,欧洲核苷酸档案库 |
| SRA | Sequence Read Archive,NCBI 的测序读段库 |
| taxId | NCBI Taxonomy 的物种分类号(9606=人,10090=小鼠) |
| characteristics | 样本记录的特征键值对字段,上限 50 个 |
| domain | 记录的来源通道(如 self.BiosampleImportNCBI) |
| submittedVia | 记录进入 BioSamples 的方式 |
| reference sample | 参考样本,如 1000 Genomes、HipSci、FAANG |
| DataCatalog | Schema.org 的数据目录类型,本站点首页即此声明 |
| cursor 分页 | 用游标而非页码翻页,可绕过深度限制 |
| DAIMS | 数据集文档化框架,本文用于评估 |
附录 A:条目信息速查卡
| 项 | 值 |
|---|---|
| 名称 | BioSamples |
| 维护机构 | EMBL-EBI |
| 地址 | https://www.ebi.ac.uk/biosamples |
| 样本总数 | 56,108,301(2026-10 实测) |
| 最大来源 | NCBI 导入 18,056,239 |
| 唯一键 | SAME / SAMN / SAMD 前缀登录号 |
| 特征字段上限 | 50 |
| 时间戳 | release / submitted / update |
| 列表接口 | /samples?size=N&page=P |
| 单条接口 | /samples/{accession} |
| 已废弃接口 | /summary(404) |
| 站点声明 | Schema.org DataCatalog |
| 文献 | PMID 24265224 |
附录 B:常用 taxId 速查
| 物种 | taxId |
|---|---|
| 人 Homo sapiens | 9606 |
| 小鼠 Mus musculus | 10090 |
| 大鼠 Rattus norvegicus | 10116 |
| 拟南芥 Arabidopsis thaliana | 3702 |
| 果蝇 Drosophila melanogaster | 7227 |
| 斑马鱼 Danio rerio | 7955 |
| 大肠杆菌 K-12 | 511145 |
| 酵母 Saccharomyces cerevisiae | 4932 |
附录 C:接口限制速查
| 项目 | 限制 | 应对 |
|---|---|---|
size 参数 |
>100 返回 400 | 用 100 |
page 深度 |
极深时返回 400 | 用 cursor |
/summary |
404 已废弃 | 用 /samples |
| 单条 SAMEA | 部分 403 | 走列表 |
| 域值拼写 | 错值静默返回 0 | 抽样验证 |
| 特征字段 | 上限 50 | 精简后提交 |
相关数据集导航
以下为站内 AI-Ready 数据集百科中与本词条共享多个主题标签的相关数据集,按相关度降序排列:
- dbsnp — 共享标签:基因组学与多组学 / 变异与注释 / 测序数据
- ensembl — 共享标签:基因组学与多组学 / 变异与注释 / 测序数据
- clingen — 共享标签:基因组学与多组学 / 变异与注释 / 测序数据
- igvf-catalog — 共享标签:基因组学与多组学 / 变异与注释
- decipher — 共享标签:基因组学与多组学 / 变异与注释 / 测序数据
- gnomad — 共享标签:基因组学与多组学 / 测序数据
- uniprot — 共享标签:基因组学与多组学 / 测序数据
- 1000-genomes — 共享标签:基因组学与多组学 / 测序数据
- encode — 共享标签:基因组学与多组学 / 测序数据
- geo — 共享标签:基因组学与多组学 / 测序数据
导航说明:本章节由全站统一标签体系自动计算生成(标签重合度算法),双向可达;点击链接可跳转至对应数据集词条。

