zer*_*mer 4 awk text-processing
我有一个 4 列制表符分隔的文件,最后一列有时有重复项。这是该文件的摘录:
chr7 116038644 116039744 GeneA
chr7 116030947 116032047 GeneA
chr7 115846040 115847140 GeneA
chr7 115824610 115825710 GeneA
chr7 115801509 115802609 GeneA
chr7 115994986 115996086 GeneA
chrX 143933024 143934124 GeneB
chrX 143933119 143934219 GeneB
chrY 143933129 143933229 GeneC
Run Code Online (Sandbox Code Playgroud)
对于该列中的每一组重复项,我想将它们转换为这样的(不真正触及该列中的非重复值):
chr7 116038644 116039744 GeneA-1
chr7 116030947 116032047 GeneA-2
chr7 115846040 115847140 GeneA-3
chr7 115824610 115825710 GeneA-4
chr7 115801509 115802609 GeneA-5
chr7 115994986 115996086 GeneA-6
chrX 143933024 143934124 GeneB-1
chrX 143933119 143934219 GeneB-2
chrY 143933129 143933229 GeneC
Run Code Online (Sandbox Code Playgroud)
我怎样才能用awkorsed或 Bash 的for循环来做到这一点?
尝试这个
awk -F'\t' -v OFS='\t' '{$4=$4 "-" (++count[$4])}1' file.tsv
Run Code Online (Sandbox Code Playgroud)
这将在计数器数组中存储第 4 个字段的每个值的出现count(其中第 4 个字段的值用作“索引”),并将该计数器的预递增值附加到第 4 个字段,由一个分隔短跑。
上面的“简单”示例有一个缺点:它会为第 4 列中仅在文件中出现一次的那些值添加一个消歧数字。为了抑制这种情况,以下双通道方法将起作用(命令通过两行分解\以提高可读性):
awk -F'\t' -v OFS='\t' 'NR==FNR{f[$4]++}\
NR>FNR{if (f[$4]>1) {$4=$4 "-" (++count[$4])}; print}' file.tsv file.tsv
Run Code Online (Sandbox Code Playgroud)
请注意,要处理的文件作为参数声明了两次,因此将被读取两次。
FNR,每个文件的行计数器,等于NR,全局行计数器),我们简单地计算第 4 列的每个不同值在文件中出现的频率,并将其存储在数组中f。这种方法避免了缓冲整个文件,如果文件非常大,这可能是一个优势。处理时间当然更长,因为文件被读取了两次。
作为一般规则,很少需要使用 shell 循环进行文本处理,因为awk 例如可以以更有效的方式自行执行循环操作。