4. attribute属性格式:key=value; 列表,ID、Name、Parent等核心键
好,咱们进入GFF3里最容易出幺蛾子的部分——attribute。说白了,第9列就是整个注释信息的灵魂。你前面几列再规范,第9列一乱,整个文件就会崩,谁也救不了。
我记得第一次处理一个超大基因组注释文件时,就因为attribute里少了一个分号,结果下游工具全部报错。查了一整天,最后发现是一个换行符把属性给截断了。从那以后,我对这列就格外上心。
先看标准格式长什么样。第9列是一组由分号分隔的key=value对,key和value之间是等号,属性之间是分号,末尾可以加分号也可以不加。我个人的习惯是统一加,保持整洁。
ID=gene00001;Name=BRCA1;description=breast cancer 1;Note=partial
ID=exon001;Parent=gene00001;Name=exon_1;number=1
4.1 核心键的职责与规则
GFF3约定死了三个核心键:ID、Name、Parent。其他都属于自定义属性,但这三个你有义务遵守规则,否则工具不认。
| 键 | 作用 | 是否必须 | 常见陷阱 |
|---|---|---|---|
| ID | 唯一标识当前特征 | 是(顶级特征外可选) | 重复ID、ID包含空格 |
| Name | 人类可读的名称 | 否(强烈建议有) | 和ID混用、包含非法字符 |
| Parent | 指向父特征的ID | 是(子特征必须) | Parent指向不存在的ID、多个Parent用逗号时语法错误 |
你想想看,如果ID重复了,所有指向它的Parent就全都乱了。我在项目中见过最离谱的,是一个基因的CDS挂了两个不同的mRNA做Parent,导致提取转录本时直接崩掉。
4.2 多值属性的处理
有些属性允许有多个值,比如一个exon可以属于多个转录本。这时候标准做法是用逗号分隔多个值,不需要加引号,不需要加空格。但逗号前不能有空格,这是很多人翻车的地方。
# 正确写法:多个Parent用逗号分隔,无空格
Parent=transcript001,transcript002
# 错误写法:逗号后出现空格
Parent=transcript001, transcript002
这里我得多说一句。有些工具解析时对空格特别敏感,一旦碰到Parent=A, B这种写法,它会把空格也算进去,导致Parent指向一个叫" B"的ID——前面多了个空格,当然找不到。
4.3 URL编码与特殊字符
GFF3规定,attribute的value里如果包含特殊字符——分号、等号、逗号、控制字符等——必须进行URL编码。说白了就是转义。
| 原始字符 | 编码形式 | 场景 |
|---|---|---|
| ; | %3B | 描述中带分号 |
| = | %3D | 描述中带等号 |
| , | %2C | 多值属性,如需逗号本身而非分隔 |
| & | %26 | 描述中带and符号 |
| 空格 | %20 | ID或Name中包含空格(强烈不建议) |
# 描述中包含特殊字符时的正确写法
ID=gene001;Name=BRCA1;description=tumor%20suppressor%3Bbreast%20cancer
# 原始意图:description = "tumor suppressor; breast cancer"
我曾经看过一个文件,description里直接写了分号,解析器把整个属性截断了,后面的属性全丢了。那场事故导致我们批量下数据时,有一半基因注释不完整。说起来都是泪。
4.4 属性的结构约束
除了特殊字符,GFF3对attribute还有几个硬性约束,我用一个清单帮你理清楚:
- 原子性:每个
key=value对必须是一个整体,不能拆分到多行 - 顺序无关:属性之间的顺序没有约束,解析器应容忍任意顺序
- 大小写敏感:key是大小写敏感的,
ID=和id=是两个不同的key - 禁止嵌套:value中不要再出现
key=value的结构 - 引号歧义:GFF3不允许用引号包裹value,
ID="gene001"是错误的
ID、Name、Parent这三个大写格式。如果你写成id或parent,它们会直接忽略。规范里虽没规定大小写,但实践中请永远用大写开头。
4.5 Parent的指向约束
Parent是最容易出连锁问题的地方。你要记住一个核心规则:Parent指向的ID必须在文件或配套文件中有定义。简单说就是——你不能写一个孤儿ID。
来看几种典型错误:
# 错误1:CDS指向不存在的mRNA
chr1 . CDS 100 200 . + . ID=cds001;Parent=mRNA001
# 假设mRNA001从未在任何地方定义过
# 错误2:CDS指向mRNA,但mRNA在同文件中定义在CDS之后
chr1 . CDS 100 200 . + . ID=cds001;Parent=mRNA001
chr1 . mRNA 50 300 . + . ID=mRNA001;Parent=gene001
# 错误3:mRNA的Parent指向的gene未定义
chr1 . mRNA 50 300 . + . ID=mRNA001;Parent=gene_ghost
真实项目里,我见过一个文件里CDS对应了500个mRNA,但其中20个mRNA的ID拼写错误。结果导致这20个CDS成了孤儿,后续转录组定量全部缺失。你想想看,这样的数据拿去发表,结果可信度能高吗?
4.6 attribute金三角:ID-Name-Parent的一致性
GFF3注释结构的核心,就是ID、Name、Parent这个金三角。我画了一张图帮你理解它们之间的关系:
从上图你能看出来,Gene用ID定义自己,mRNA用Parent指向Gene,CDS和exon再通过Parent指向mRNA。这形成了一条完整的层级链。一旦中间任何一环断了,整个结构就塌了。
4.7 避坑指南:我自己的踩坑记录
最后,我把这些年踩过的坑集中列出来,每个括号里是解决心得,你看看能不能少走弯路:
- 用逗号分隔Parent时记得去掉空格(用
sed 's/, /,/g'批量清一遍) - 描述中不要直接写分号或等号(用
%3B和%3D逃避) - 同一个特征不要既做ID又做Parent的别名(ID保持唯一性)
- 不要在一个GFF3里混用大小写key(统一用
ID、Name、Parent) - 末尾分号虽然合法,但转工具时可能被解读差异(我统一加,但保持内部一致)
- 多行attribute在规范里是禁止的(换行前我记得加续行符
\?不,GFF3不支持续行符,一条annotation必须在一行内)
\做换行续接,但GFF3标准否定了这种做法。每个feature必须独占一行,不能拆成两行。如果你想写很长的description,请直接用URL编码压缩到一行里。
总之,attribute的处理原则就四个字——严谨、一致。规范是死的,但人的习惯是活的。你养成一个固定的写法风格,自然就不会踩坑。