从Fasta文件中删除多个序列

时间:2019-04-11 15:24:50

标签: bash awk sed fasta

我有一个字符序列的文本文件,该文件由两行组成:标题和下一行的序列本身。该文件的结构如下:

>header1
aaaaaaaaa
>header2
bbbbbbbbbbb
>header3
aaabbbaaaa
[...]
>headerN
aaabbaabaa

在另一个文件中,我有一个要删除的序列标头的列表,如下所示:

>header1
>header5
>header12
[...]
>header145

这个想法是从第一个文件中删除这些序列,因此所有这些标题和下一行。我使用sed进行了如下操作,

while read line; do sed -i "/$line/,+1d" first_file.txt; done < second_file.txt

它可以工作,但是要花很长时间,因为我用sed多次加载了整个文件,而且它很大。关于如何加快此过程的任何想法?

7 个答案:

答案 0 :(得分:1)

$ awk 'NR==FNR{a[$0];next} $0 in a{c=2} !(c&&c--)' list file
>header2
bbbbbbbbbbb
>header3
aaabbbaaaa
[...]
>headerN
aaabbaabaa

c是您要从刚匹配的那一行开始跳过的几行。参见https://stackoverflow.com/a/17914105/1745001

或者:

$ awk 'NR==FNR{a[$0];next} /^>/{f=($0 in a ? 1 : 0)} !f' list file
>header2
bbbbbbbbbbb
>header3
aaabbbaaaa
[...]
>headerN
aaabbaabaa

f是在目标数组>...中是否找到最近读取的a[]行。 f=($0 in a ? 1 : 0)可以缩写为f=($0 in a),但为了清晰起见,我更喜欢三元表达式。

第一个脚本取决于您知道每个记录有多少行,而第二个脚本则取决于从>开始的每个记录。如果两者都知道,那么选择哪种样式就可以。

答案 1 :(得分:1)

您可以使用此awk

awk 'NR == FNR{seen[$0]; next} /^>/{p = !($0 in seen)} p' hdr.txt details.txt

答案 2 :(得分:1)

您遇到的问题很容易回答,但是当您处理常规fasta文件时不会有帮助。 Fasta文件具有序列标头,后跟一行或多行,可以将其连接起来以表示序列。 Fasta文件格式大致遵循以下规则:

  
      
  • 字符(>开头的描述行(defline)或标头/标识符行,给出了序列的名称和/或唯一标识符,并且还可能包含其他内容信息。
  •   
  • 在描述行之后是标准的单字母字符串中的实际序列本身。除有效字符外的任何其他字符(包括空格,制表符,星号等)都将被忽略。
  •   
  • 序列可以跨越多行。
  •   
  • 通常通过在两个后续序列之间留空行来将多个单个序列FASTA文件连接到一个公共文件中,从而获得多序列FASTA格式。
  •   

大多数提出的方法将在具有多行序列的多fasta上失败

以下内容将始终有效:

awk '(NR==FNR) { toRemove[$1]; next }
     /^>/ { p=1; for(h in toRemove) if ( h ~ $0) p=0 }
    p' headers.txt file.fasta

这与EdMortonAnubahuva的答案非常相似,但是这里的区别在于文件headers.txt只能包含标头的一部分。

答案 3 :(得分:0)

一种选择是创建一个长sed表达式:

sedcmd=
while read line; do sedcmd+="/^$line\$/,+1d;"; done < second_file.txt
echo "sedcmd:$sedcmd"
sed $sedcmd first_file.txt

这将只读取一次文件。请注意,我在sed模式中添加了^$(因此>header1>header123不匹配...)


如果您有成千上万个文件,则使用文件(如@daniu所建议的)可能会更好,因为使用此方法可能会导致达到命令行最大计数。

答案 4 :(得分:0)

使用第二个文件中的删除命令创建脚本:

void clear()

然后将该文件应用于第一个

sed 's#\(.*\)#/\1/,+1d#' secondFile.txt > commands.sed

答案 5 :(得分:0)

awk可能对您有用:

awk 'FNR==NR{a[$0]=1;next}a[$0]{getline;next}1' input2 input1

答案 6 :(得分:0)

尝试gnu sed,

sed -E ':s $!N;s/\n/\|/;ts ;s~.*~/&/\{N;d\}~' second_file.txt| sed -E -f -  first_file.txt

在两个脚本前附加time命令以比较速度,
看起来time while read line;do...time sed -....的结果在我的测试中完成了,不到OP的一半时间