A maioria das soluções postadas até agora são lentas ou podem produzir resultados falsos em certos casos. (Não testei Picard, que presumo que funcione conforme o planejado. Mas, pessoalmente, tenho tendência a recuar quando tenho que configurar uma JVM!)
Vamos abordar o problema de um ângulo diferente:
Boas funções hash têm complexidade ~ O (1) para uma pesquisa. Se pudermos criar uma tabela hash de nossos QNAMEs, a pesquisa de um arquivo BAM deve ser O (n) no número de registros. Os desenvolvedores Python têm muito orgulho da função hash em cpython, então vamos usá-la:
#! / Usr / bin / env python3import syswith open (sys.argv [1], 'r' ) como indexfile: ids = set (l.rstrip ('\ r \ n') para l em indexfile) para linha em sys.stdin: qname, _ = line.split ('\ t', 1) se qname em ids : sys.stdout.write (line)
Com um conjunto de teste consistindo em um arquivo BAM de aproximadamente 3 GB e um qnames.txt
com aproximadamente 65 mil entradas, executando samtools view input.bam | ./idfilter.py qnames.txt
leva cerca de 6,5 segundos no meu servidor de teste. Em comparação, canalizar o SAM para fgrep -f qnames.txt
(usando GNU grep) leva cerca de 8,5 segundos (mas pode produzir resultados espúrios).
(Por que grep
produz resultados espúrios? Se você tiver um QNAME foo
em seu qnames.txt
, mas seu arquivo BAM também contém fooX
e fooY
, eles serão correspondidos por fgrep
. A solução é ancorar cada um de seus padrões de pesquisa entre o início da linha e uma guia, por exemplo, ^ foo \ t
, mas então você tem que usar o grep
padrão que terá que construir um NFA para cada padrão, e não o fgrep
que implementa Aho-Corasick, e você será ordens de magnitude mais lento.)
Reescrevi meu pequeno script Python em Rust usando std :: Collections :: HashMap
para a função hash e usando a caixa rust_htslib para ler e escrever diretamente do BAM, e isso é mais que duas vezes mais rápido que o Python, mesmo ao ler e escrever BAM. No entanto, meu Rust não é realmente adequado para consumo público ...