BioSamples — 5,610 万条生物样本元数据的中枢登记处

把分散在 ENA、NCBI、ArrayExpress 的样本记录按一个键聚合,每条最多 50 个特征字段

来源 欧洲分子生物学实验室-欧洲生物信息研究所(EMBL-EBI)https://www.ebi.ac.uk/biosamples发布时间: 2026-10-25最后更新: 2026-10-25 阅读 4
BioSamples — 5,610 万条生物样本元数据的中枢登记处

信息速览

数据集名称BioSamples — 5,610 万条生物样本元数据的中枢登记处
数据类型样本元数据,结构化注释表
规模不直接涉及患者个体;但 56M+ 样本中包含大量人类样本(taxId 9606),其 age、cell_type、disease 等字段属可识别性敏感信息,使用须评估
接入方式欧洲分子生物学实验室-欧洲生物信息研究所(EMBL-EBI)https://www.ebi.ac.uk/biosamples
AI 就绪度

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 — 共享标签:基因组学与多组学 / 测序数据

导航说明:本章节由全站统一标签体系自动计算生成(标签重合度算法),双向可达;点击链接可跳转至对应数据集词条。

返回 AI-Ready 数据集