基因组坐标系转换器
BED 的坐标和 GFF、VCF 的坐标差 1,这是生信里最经典的 off-by-one 来源。 这个工具把一段区间在两套坐标系之间换算,并直接给出可粘贴的 BED 与 GFF 行。
两套坐标系
规范里对这两套系统有明确定义(原文见文末出处):
| 首碱基编号 | 区间写法 | 使用这套的格式 | |
|---|---|---|---|
| 1-based | 1 | 闭区间 [start, end] |
SAM、VCF、GFF/GTF、Wiggle |
| 0-based | 0 | 半开区间 [start, end) |
BAM、BCFv2、BED、PSL |
规范自己举的例子是:第 3 到第 7 个碱基(含两端),
在 1-based 里写作 [3, 7],在 0-based 里写作 [2, 7)。
只有起点差 1,终点是一样的
这是最该记住的一句话。拿上面那个例子:
GFF start = 3 end = 7
BED start = 2 end = 7
↑ 两者相同
BED 的 end 和 GFF 的 end 数值完全相同,差别只在 start。 原因是 0-based 半开区间把起点往左挪了一位,同时终点不含在内, 两个偏移正好抵消。
最常见的错误是把 end 也加 1,结果区间凭空长出一个碱基。 这种错误不会报错,只会让你的统计悄悄偏掉——每个区间多算一个碱基, 几万个区间累积起来就是可观的偏差。
长度公式也因此不同
BED 长度 = end − start (不加 1)
GFF 长度 = end − start + 1 (要加 1)
两个公式算出来是同一个数。上面那段都是 5 bp:7 − 2 = 5,7 − 3 + 1 = 5。
记混的典型症状是所有区间长度都差 1——如果你发现自己算出来的外显子长度
总比数据库里少 1 或多 1,多半就是套错了公式。
单碱基位点
一个 SNP 在 VCF 里写 POS = 1000。它在 BED 里是:
chr1 999 1000
看起来像跨了两个位置,其实长度正好是 1000 − 999 = 1,就是一个碱基。
把 SNP 列表转成 BED 时,start 要减 1,end 保持不变。
BED 能表示零长度区间,GFF 不能
BED 里 start == end(例如 chr1 100 100)是合法的,表示一个零长度的插入点——
插入发生在第 100 与第 101 个碱基之间。
1-based 闭区间没法表示零长度:[101, 100] 这种 start > end 的写法在 GFF 里是非法的
(规范明确要求 start ≤ end)。所以这类区间无法直接转换,只能改用文字描述插入位置。
本工具遇到这种输入会明说转不了,而不是给一个看似合理的数。
顺带一提:还有一层坑在链方向
坐标系只是第一层。BED 的第 6 列和 GFF 的第 7 列都是链方向, 但坐标始终是相对正链给出的——负链基因的 start 仍然小于 end, 不会因为在负链上就把两者对调。 需要「基因自身 5′ 端」时要自己根据链方向决定取 start 还是 end。
数据出处
两套坐标系的定义与各格式的归属,取自 htslib 官方规范仓库中
SAM 规范的术语表(Samtools/hts-specs,SAMv1 第 1 节):
其中明确写出「SAM、VCF、GFF 与 Wiggle 使用 1-based 坐标系」
以及「BAM、BCFv2、BED 与 PSL 使用 0-based 坐标系」,并给出了 [3,7] / [2,7) 这个例子。
另外两处独立印证:VCF 规范(VCFv4.3)写明「POS 是参考位置,首碱基位置为 1」; GFF3 规范(Sequence Ontology)写明坐标是「positive 1-based integer coordinates」 且「start 始终小于等于 end」。
相关工具
常见问题
BED 和 GFF 的坐标到底差在哪?
只差在 start。BED 是 0-based 半开区间,GFF 是 1-based 闭区间,BED 的 start 比 GFF 小 1,而两者的 end 数值完全相同。以第 3 到第 7 个碱基为例:GFF 写 start=3、end=7,BED 写 start=2、end=7。把 end 也加 1 是最常见的错误,会让每个区间多出一个碱基。
为什么 BED 的长度不用加 1?
因为半开区间的终点不包含在内。BED 长度 = end − start,GFF 长度 = end − start + 1,两个公式算的是同一个数。如果你发现自己算出来的区间长度总比数据库里差 1,基本可以断定是把两个公式套混了。
SNP 怎么从 VCF 转成 BED?
start 减 1,end 保持不变。VCF 里 POS=1000 的位点,BED 行是 chr1 999 1000。看起来跨了两个数字,实际长度是 1000−999=1,正好一个碱基。
BED 里 start 和 end 相等是什么意思?
是一个零长度的插入点,表示插入发生在该位置与下一个位置之间,这在 BED 里合法。但 1-based 闭区间无法表示零长度区间(GFF3 规范要求 start ≤ end),所以这类区间转不成 GFF,只能用文字描述插入位置。本工具遇到会明说转不了。
负链上的基因,start 和 end 要对调吗?
不要。无论 BED 还是 GFF,坐标都是相对正链给出的,负链基因的 start 依然小于 end。链方向单独记在 BED 第 6 列或 GFF 第 7 列。需要「基因自身的 5′ 端」时,要自己根据链方向决定取 start 还是 end。
还有哪些格式用 0-based?
按 SAM 规范的术语表:0-based 半开的有 BAM、BCFv2、BED、PSL;1-based 闭区间的有 SAM、VCF、GFF/GTF、Wiggle。注意 SAM 和 BAM 是同一份数据的两种存储形式,但坐标约定不同——文本的 SAM 是 1-based,二进制的 BAM 是 0-based,转换由工具在读写时处理。
samtools 和 IGV 里输入的区间是哪一套?
是 1-based 闭区间,也就是 chr1:1000-2000 表示第 1000 到第 2000 个碱基、含两端,共 1001 bp。这和 BED 文件里的写法不同,从 BED 复制坐标去 samtools 查区间时,start 要加 1。