我有一个简单的bash脚本如下
#!/bin/bash
#This script reads a file of row identifiers separated by new lines
# and outputs all query FASTA sequences whose headers contain that identifier.
# usage filter_fasta_on_ids.sh fasta_to_filter.fa < seq_ids.txt; > filtered.fa
while read SEQID; do
sed -n -e "/$SEQID/,/>/ p" $1 | head -n -1
done
fasta文件具有以下格式:
> HeadER23217;count=1342
ACTGTGCCCCGTGTAA
CGTTTGTCCACATACC
>ANotherName;count=3221
GGGTACAGACCTACAC
CAACTAGGGGACCAAT
修改更改了标题名称,以便更好地显示文件中的实际结构
我上面制作的脚本确实正确过滤了文件,但速度非常慢。我的输入文件有~20,000,000行包含〜4,000,000个序列,我有一个80,000个标题列表,我想要过滤。有没有更快的方法使用bash / sed或其他工具(如python或perl?)上面的脚本需要几个小时才能完成?
答案 0 :(得分:1)
您正在扫描大文件80k次。我会建议使用不同的工具采用不同的方法:awk
。将选择列表加载到散列映射(awk数组)中,并在扫描大文件时(如果任何序列与print匹配)。
例如
$ awk -F"\n" -v RS=">" 'NR==FNR{for(i=1;i<=NF;i++) a["Sequence ID " $i]; next}
$1 in a' headers fasta
-F"\n"
标志将输入文件中的field separator
设置为新行。 -v RS=">"
将记录分隔符设置为">"
Sequence ID 1
ACTGTGCCCCGTGTAA
CGTTTGTCCACATACC
Sequence ID 4
GGGTACAGACCTACAT
CAACTAGGGGACCAAT
头文件包含
$ cat headers
1
4
并且fasta文件包含更多相同格式的记录。
如果您的标题已包含“序列ID”前缀,请按此调整代码。我没有为大文件测试这个,但是应该比你的代码快得多,只要你没有内存限制来保存80K大小的数组。在这种情况下,将标题拆分为多个部分并合并结果应该是微不足道的。
要允许任何格式的标题并使结果文件成为有效的FASTA文件,您可以使用以下命令:
awk -F"\n" -v RS=">" -v ORS=">" -v OFS="\n" 'NR==FNR{for(i=1;i<=NF;i++) a[$i]; next} $1 in a' headers fasta > out
ORS
和OFS
标志设置输出字段和记录分隔符,在这种情况下与输入的fasta文件相同。
答案 1 :(得分:0)
你应该利用这个事实(你没有明确说明,但我认为)巨大的fasta文件按顺序包含序列(按ID排序)。
我还假设头文件按ID排序。如果不是这样,那就这样做 - 排序80k整数并不昂贵。
当两者都被分类时,它归结为通过两个文件的单个同时线性扫描。而且由于它在恒定内存中运行,因此与其他awk示例不同,它可以使用任何大小。我在python中给出了一个例子,因为我对awk中的手动迭代感到不舒服。
import sys
fneedles = open(sys.argv[1])
fhaystack = open(sys.argv[2])
def get_next_id():
while True:
line = next(fhaystack)
if line.startswith(">Sequence ID "):
return int(line[len(">Sequence ID "):])
def get_next_needle():
return int(next(fneedles))
try:
i = get_next_id()
j = get_next_needle()
while True:
if i == j:
print(i)
while i <= j:
i = get_next_id()
while i > j:
j = get_next_needle()
except StopIteration:
pass
当然它有点冗长,但它在我的旧机器上在大约10秒钟内找到80k的4M序列(339M输入)。 (它也可以用awk重写,这可能会快得多)。我用这种方式创建了fasta文件:
for i in range(4000000):
print(">Sequence ID {}".format(i))
print("ACTGTGCCCCGTGTAA")
print("ACTGTGCCCCGTGTAA")
print("ACTGTGCCCCGTGTAA")
print("ACTGTGCCCCGTGTAA")
标题(“针”)这样:
import random
ids = list(range(4000000))
random.shuffle(ids)
ids = ids[:80000]
ids.sort()
for i in ids:
print(i)
答案 2 :(得分:0)
它很慢,因为当你可以读取一次并处理所有模式时,你正在读同一个文件几次。因此,您需要生成一个sed脚本,其中包含每个ID的语句和/>/b
以替换head -n -1
。
while read ID; do
printf '/%s/,/>/ { />/b; p }\n' $ID;
done | sed -n -f - data.fa