analisar um arquivo enorme com o awk e extrair um subconjunto

3

Eu tenho um arquivo enorme que se parece com isso

chr10   98072   1
chr10   98073   1
chr10   98074   1
chr10   98075   2
chr10   98076   2
chr10   98077   3
chr10   98078   5
chr10   98079   5
chr11   98080   5
chr12   98081   5

Eu tenho muitas entradas para cada cromossomo. Eu só quero extrair linhas que tenham chr10. Como meu arquivo é muito grande, usei este comando para extrair apenas as linhas chr10

awk '$1 ~ /^chr10$/{print}; $1 !~ /^chr10$/{exit}' cov.txt > subset.txt

Esta é uma boa maneira de fazer o awk não passar pelo arquivo inteiro. Meu arquivo já está classificado no cromossomo

Obrigado

    
por user3138373 30.06.2015 / 23:58

5 respostas

1

Usando sed :

sed -n '/^chr10[^0-9]/ { p; b; }; q' cov.txt > subset.txt

Isso ainda pressupõe que o chr10 group está no início do arquivo.

    
por 01.07.2015 / 00:43
4
awk '$1=="chr10"{print; next}{exit}' cov.txt > subset.txt

Testes: Redirecionados para /dev/null para > 12.947.909 chr10 registros mais mais alguns chr11 , chr12 e mais para um total de 99.063.774 linhas - saídas são todas idênticas (mesmo md5sum). A contagem da linha de saída = 12.947.909 - ordenou a mais rápida a mais lenta:

steve: awk '{ if($1 == "chr10") { print } else { exit } }' cov.txt >/dev/null

real  0m5.963s
user  0m5.896s
sys   0m0.064s

Peter.O: awk '$1=="chr10"{print; next}{exit}' cov.txt >/dev/null

real  0m6.553s
user  0m6.484s
sys   0m0.068s

kos: perl -pe '!/chr10/&&exit' cov.txt >/dev/null

real  0m8.658s
user  0m8.545s
sys   0m0.112s

steve: sed -n '/^chr10[^0-9]/ { p; b; }; q' cov.txt >/dev/null

real  0m17.130s
user  0m17.077s
sys   0m0.052s

user3138373: awk '$1 ~ /^chr10$/{print}; $1 !~ /^chr10$/{exit}' cov.txt >/dev/null

real  0m18.621s
user  0m18.541s
sys   0m0.080s
    
por 01.07.2015 / 00:14
2

Tente isso, pareceu um pouco mais rápido no meu teste rudimentar. Evita o processamento regular de expressões.

[root@localhost tmp]# wc -l cov.txt
34970568 cov.txt
[root@localhost tmp]# time awk '$1 ~ /^chr10$/{print}; $1 !~ /^chr10$/{exit}' cov.txt > subset.txt

real    0m23.897s
user    0m22.031s
sys     0m1.556s
[root@localhost tmp]# time awk '{ if($1 == "chr10") { print } else { exit } }' cov.txt > subset.txt

real    0m16.784s
user    0m14.731s
sys     0m1.661s
[root@localhost tmp]#

Tentei a abordagem sed de lcd047 também

[root@localhost tmp]# time sed -n '/^chr10[^0-9]/ { p; b; }; q' cov.txt > subset.txt

real    0m38.343s
user    0m36.609s
sys     0m1.546s
[root@localhost tmp]#

Usando o grep antigo, o mais rápido, apesar de ler o arquivo inteiro

[root@localhost tmp]# time grep "^chr10" cov.txt >subset.txt

real    0m6.546s
user    0m4.932s
sys     0m1.577s
[root@localhost tmp]#

Teria pensado grep -F para ser mais rápido novamente, mas não parecia ser. Consistentemente por mais de 7 segundos.

[root@localhost tmp]# time grep -F chr10 cov.txt >subset.txt

real    0m7.317s
user    0m6.109s
sys     0m1.173s
[root@localhost tmp]#
    
por 01.07.2015 / 00:23
2

Mais eficientemente, faça isso com egrep :

egrep '^chr10{space or tab}' cov.txt

Ou se o conteúdo for parecido com o que você mostrou,

grep -w chr10 cov.txt
    
por 01.07.2015 / 03:44
0

Como o arquivo está classificado e de acordo com seu comentário , as linhas que começam com chr10 estão sempre no início do arquivo, usando Perl:

< cov.txt perl -pe '!/chr10/&&exit' > subset.txt

Desta forma, o script sairá na primeira ocorrência de um não-correspondência.

Um teste dessa execução em um arquivo de linha de correspondência de 1000000 armazenado na memória (obtido ao anexar o chr10 98072 1 linha 1000000 vezes a um arquivo vazio) é executado em pouco tempo:

~/tmp$ < cov.txt wc -l
1000000
~/tmp$ time < cov.txt perl -pe '!/chr10/&&exit' > subset.txt

real    0m0.631s
user    0m0.624s
sys 0m0.004s
~/tmp$ < subset.txt wc -l
1000000
    
por 01.07.2015 / 15:47

Tags