gpt4 book ai didi

unix - 将 file2 中第一列中的模式替换为 file1 中 file2 中第二列中的模式

转载 作者:行者123 更新时间:2023-12-04 00:00:40 25 4
gpt4 key购买 nike

我已尝试解决以下问题:我有一个 .gff3 文件,我想将其基因 header 替换为简化名称。原始基因标题和新基因名称都在一个单独的文件中给出,原始名称在第 1 列,新名称在第 2 列。如何使用 sed(我认为 sed 最适合这里)来替换所有出现在 .gff3 文件的第二列中使用新的缩短名称?

示例行 .gff3 文件:

tulip_contig_65_pilon_pilon .   contig  1   93354   .   .   .   ID=tulip_contig_65_pilon_pilon;Name=tulip_contig_65_pilon_pilon
tulip_contig_65_pilon_pilon maker gene 19497 23038 . + . ID=maker-tulip_contig_65_pilon_pilon-augustus-gene-0.4;Name=maker-tulip_contig_65_pilon_pilon-augustus-gene-0.4
tulip_contig_65_pilon_pilon maker mRNA 19497 23038 . + . ID=maker-tulip_contig_65_pilon_pilon-augustus-gene-0.4-mRNA-1;Parent=maker-tulip_contig_65_pilon_pilon-augustus-gene-0.4;Name=maker-tulip_contig_65_pilon_pilon-augustus-gene-0.4-mRNA-1;_AED=0.00;_eAED=0.00;_QI=418|1|1|1|0|0|3|2100|206

示例行替换文件:

augustus_masked-tulip_contig_306_pilon_pilon-processed-gene-0.1   gene1
maker-tulip_contig_306_pilon_pilon-augustus-gene-0.12 gene2
maker-tulip_contig_65_pilon_pilon-augustus-gene-0.4 gene3

预期结果:

tulip_contig_65_pilon_pilon   .   contig  1   93354   .   .   .   ID=tulip_contig_65_pilon_pilon;Name=tulip_contig_65_pilon_pilon
tulip_contig_65_pilon_pilon maker gene 19497 23038 . + . ID=gene3;Name=gene3
tulip_contig_65_pilon_pilon maker mRNA 19497 23038 . + . ID=gene3-mRNA-1;Parent=gene3;Name=gene3-mRNA-1;_AED=0.00;_eAED=0.00;_QI=418|1|1|1|0|0|3|2100|206

我试过用:

while read -r pattern replacement; do sed -i "s/$pattern/$replacement/" file.gff3 ; done < rename.txt

但根据下面的答案,我现在改用 AWK。我使用脚本(与 Ed Morton 给出的缩进完全相同,但在此处复制会稍微改变它):

NR==FNR {
map[$1] = $2
next
}
{
for (old in map) {
gsub(old,map[old])
}
print
}

要运行,我使用:

awk -f tst.awk rename.txt original.gff3 > new.gff3 

但是,此脚本适用于部分正则表达式匹配,但它应该是完全匹配的。如何更改此 awk 脚本以使其完全匹配?

gff 文件长 7369803 行。 rename.txt 文件长 18477 行。

这里欢迎任何建议!

最佳答案

这会从 = 之后进行完整的字符串匹配到-gene=<number>结尾在 .gff3 的每一行上,并且应该比我们之前所做的要快几个数量级,并且更健壮,因为它只替换 original.gff3 文件的每一行中实际找到的 1-3 个字符串,而不是尝试替换所有 18,000 个字符串+ rename.txt 文件中存在的字符串:

$ cat tst.awk
NR==FNR {
map[$1] = $2
next
}
{
head = ""
tail = $0
while ( match(tail,/((ID|Parent|Name)=)([^;]+-gene-[0-9]+\.[0-9]+)(.*)/,a) ) {
old = a[3]
head = head substr(tail,1,RSTART-1) a[1] (old in map ? map[old] : old)
tail = a[4]
}
print head tail
}

.

$ awk -f tst.awk rename.txt original.gff3
tulip_contig_65_pilon_pilon . contig 1 93354 . . . ID=tulip_contig_65_pilon_pilon;Name=tulip_contig_65_pilon_pilon
tulip_contig_65_pilon_pilon maker gene 19497 23038 . + . ID=gene3;Name=gene3
tulip_contig_65_pilon_pilon maker mRNA 19497 23038 . + . ID=gene3-mRNA-1;Parent=gene3;Name=gene3-mRNA-1;_AED=0.00;_eAED=0.00;_QI=418|1|1|1|0|0|3|2100|206

它使用 GNU awk 作为第三个参数来 match() - 我假设你有 GNU awk 可用(或可以安装它),因为你使用的是 GNU sed。

所以,match()正在从 old 的当前行中隔离一个字符串(然后存储在 original.gff3 中) 可能rename.txt (存储在第一个 block 中的 map[] 中),然后是 old in map正在测试该字符串是否真的在 rename.txt 中与否,如果是,替换old具有来自 map[] 的相应新值.这一切都在 while 中。循环只要 match()不断寻找新的字符串,这些字符串可以在当前行被替换。

因此,而不是下面的原始 awk 脚本(以及您问题中的 sed 脚本)为 rename.txt 中的 18,000 多行中的每一行循环一次, 以上仅对 original.gff3 的当前行中的每个字符串循环一次可能需要更换,根据您发布的示例输入,最多只能更换 3 次。


基于加快调用 sed 的 shell 循环的原始答案:

你需要这样的东西:

$ cat tst.awk
NR==FNR {
map[$1] = $2
next
}
{
for (old in map) {
gsub(old,map[old])
}
print
}

.

$ awk -f tst.awk repl.txt foo.gff3
tulip_contig_65_pilon_pilon . contig 1 93354 . . . ID=tulip_contig_65_pilon_pilon;Name=tulip_contig_65_pilon_pilon
tulip_contig_65_pilon_pilon maker gene 19497 23038 . + . ID=gene3;Name=gene3
tulip_contig_65_pilon_pilon maker mRNA 19497 23038 . + . ID=gene3-mRNA-1;Parent=gene3;Name=gene3-mRNA-1;_AED=0.00;_eAED=0.00;_QI=418|1|1|1|0|0|3|2100|206

关于字符串与正则表达式匹配以及完全与部分匹配的一些决定也适用于您的 shell+sed 循环,因此请考虑您的全部要求并提供示例输入/输出以进行测试,然后我们可以对其进行调整以适应这并不完全符合您的要求。现在它正在做部分正则表达式匹配,就像您问题中的 sed 命令一样。

关于unix - 将 file2 中第一列中的模式替换为 file1 中 file2 中第二列中的模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62428959/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com