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这个金三角。我画了一张图帮你理解它们之间的关系:

GFF3 attribute 金三角关系 Gene ID=gene001 mRNA ID=mRNA001;Parent=gene001 CDS ID=CDS001;Parent=mRNA001 Exon Parent=mRNA001 核心原则: ID唯一、Parent链完整

从上图你能看出来,Gene用ID定义自己,mRNA用Parent指向Gene,CDS和exon再通过Parent指向mRNA。这形成了一条完整的层级链。一旦中间任何一环断了,整个结构就塌了。

实战技巧:我建议你写一个简单的脚本,先提取文件中所有ID,再检查字段中每个Parent是否都在ID集合里。这一步虽然简单,但能拦截掉80%以上的结构错误。

4.7 避坑指南:我自己的踩坑记录

最后,我把这些年踩过的坑集中列出来,每个括号里是解决心得,你看看能不能少走弯路:

  • 用逗号分隔Parent时记得去掉空格(用sed 's/, /,/g'批量清一遍)
  • 描述中不要直接写分号或等号(用%3B和%3D逃避)
  • 同一个特征不要既做ID又做Parent的别名(ID保持唯一性)
  • 不要在一个GFF3里混用大小写key(统一用ID、Name、Parent)
  • 末尾分号虽然合法,但转工具时可能被解读差异(我统一加,但保持内部一致)
  • 多行attribute在规范里是禁止的(换行前我记得加续行符\?不,GFF3不支持续行符,一条annotation必须在一行内)
关于“续行符”的误解:我曾经看到有人用\做换行续接,但GFF3标准否定了这种做法。每个feature必须独占一行,不能拆成两行。如果你想写很长的description,请直接用URL编码压缩到一行里。

总之,attribute的处理原则就四个字——严谨、一致。规范是死的,但人的习惯是活的。你养成一个固定的写法风格,自然就不会踩坑。