4、第一列(seqid)常见错误:缺失、非法字符
基因注释文件的第一列 seqid,说白了就是记录“这条记录属于哪个序列”。它通常是染色体名、scaffold ID 或 contig 编号。别小看这一列,出问题会让整个注释文件没法用。
我手头处理过超过 200 个 GFF/GTF 文件,至少四分之一在第一列上栽过跟头。今天咱们集中聊聊 seqid 的坑:缺失和非法字符。
4.1 seqid 缺失:空字段、点号陷阱、全空格
最直接的问题就是 seqid 整列为空。有些人手动编辑时不小心删掉了,有些脚本输出时漏掉了。GFF3 规范里这一列不能为空,但很多工具遇到空字段会默认填成 .,一样报错。
识别起来不难,你跑一下 awk 就能看到:
awk '$1 == "" || $1 == "." || $1 ~ /^ +$/' input.gff3 | head -5
结果出来全是空、点、或空格——嗯,基本可以扔掉了。
.,在 gffread 验证阶段卡了三天才被我发现。
修复方法?看情况补充正确的染色体/序列名。如果已知参考基因组,可以用以下脚本批量替换:
awk 'BEGIN{OFS="\t"} $1 == "." || $1 == "" {$1 = "chr1"} {print}' contaminated.gff3 > fixed.gff3
4.2 非法字符:空格、括号、逗号、中文标点
seqid 允许的字符其实很窄:大小写字母、数字、下划线、横线、点。超出这个范围就算非法。我遇到最多的是空格和逗号。
为什么会有空格?很多人从 Excel 复制粘贴时,一行里有空格,或者序列名本身带空格(比如 "chr1 2q")。GFF3 规定每一列用制表符分隔,但有些工具用空格做分隔符,结果空格被当成了列分隔,seqid 就只剩一半了。
用 grep 检查哪些行包含非预期字符:
grep -P '^[^\t]+\t' input.gff3 | awk -F'\t' '$1 ~ /[^a-zA-Z0-9_.\-]/ {print $0}' | head
这条命令会找出 seqid 里含有非法字符的行。我建议你把它存成一个文件,逐条核实。
常见非法字符及修复建议:
| 非法字符 | 典型来源 | 修复方法 |
|---|---|---|
| 空格 | Excel 编辑、脚本输出 | sed 's/ /_/g' 替换成下划线 |
| 逗号、分号 | 人工添加备注 | 去掉或替换成点号 |
| 中文括号、引号 | Windows 环境复制粘贴 | 转成 ASCII 字符 |
| 制表符(作为 seqid 内部) | 分隔符错误 | 用 tr -d '\t' 删除 |
| Unicode 控制字符 | 某些文本编辑器保存 | 用 iconv -f utf8 -t ascii//TRANSLIT |
cat -A 才暴露出来。全角空格 \u00a0 是非常隐蔽的坑。
4.3 序号与序列名不匹配:假阳性错误
还有一种“合法但不正确”的 seqid 错误:你在文件中写的是 "chr1",但参考序列的 fasta 头叫 "NC_000001.11" 或 "1"。GFF/GTF 里的 seqid 必须与参考序列文件名完全一致,否则下游比对、提取序列时全部失败。
我个人习惯在写脚本前先跑一下 samtools dict 或 faidx 拿到参考序列的 seqid 列表,然后去掉 GFF 中不在此列表的行。这个步骤虽然简单,但能避免后续莫名其妙的问题。
# 提取参考序列名
samtools faidx reference.fasta
cut -f1 reference.fasta.fai > ref_seqs.txt
# 筛选有效 seqid 的行
awk 'NR==FNR {seqs[$1]; next} $1 in seqs' ref_seqs.txt input.gff3 > valid.gff3
4.4 大小写与命名风格不一致
比如文件里写 "Chr1" 而参考序列是 "chr1",或者 "scaffold_0001" 跟 "Scaffold_0001" 不匹配。这类问题单靠肉眼很难扫全,最好用比较脚本统一大小写再分组统计。
用这个命令快速统计 seqid 的唯一值:
cut -f1 input.gff3 | sort | uniq -c | sort -nr
如果出现 "chr1" 和 "CHR1" 同时存在,多半是大小写不一致导致的重复。我建议在做任何正式分析之前,先对 seqid 做归一化:全部小写、去除前后空格、替换非法字符。
4.5 自动修复脚本模板
当你拿到一堆问题文件,手动修会很痛苦。我用过一段时间的自动修复脚本,这里分享给你做参考:
#!/bin/bash
# seqid_cleanup.sh
# 用法: ./seqid_cleanup.sh dirty.gff3 > clean.gff3
INFILE=$1
awk -F'\t' -v OFS='\t' '
{
# 1. 去前后空格
gsub(/^[ \t]+|[ \t]+$/, "", $1);
# 2. 如果为空或 . 则赋予默认值
if ($1 == "" || $1 == ".") $1 = "unknown";
# 3. 替换非法字符为下划线
gsub(/[^a-zA-Z0-9_.\-]/, "_", $1);
# 4. 转小写(可选)
$1 = tolower($1);
print $0;
}' $INFILE
seqid 列表与参考序列 .fai 对比一次,确保无遗漏。
4.6 整个排查流程(一张图说清楚)
下面这张图就是我平时检查第一列的完整步骤:
4.7 多说一句
很多新手会忽略第一列,觉得“反正都是染色体名,随便写写就好”。实际上,seqid 出错的检查成本是最低的——跑几条命令就能定位,但修复后的收益巨大。你想想,一个乱写的 seqid 可能会导致整个下游分析链全部跑偏,那才是真正的灾难。
我个人习惯每次拿到新注释文件后,第一时间跑一遍上图的排查流程。哪怕文件是官方发布的,也值得确认一次。毕竟,官方也有手滑的时候嘛。