Eu tenho vários arquivos fastq delimitados por tabulações. Quero corresponder a segunda linha de cada leitura e somar os valores próximos a ela, se corresponderem. por exemplo:
file1.fq
>1
ATGCCGTT file1:1
+
HHHHKKKK
file2.fq
>2
ATGCCGTT file2:3
+
JJKHHTTT
>3
ATTCCAAC file2:1
+
=#GJLMNB
A saída que eu quero é como:
output.txt
ATGCCGTT file1:1 file2:3 count:4
ATTCCAAC file2:1 count:1
O código que escrevi é:
#!/usr/bin/env perl
use strict;
use warnings;
no warnings qw( numeric );
my %seen;
$/ = "";
while () {
chomp;
my ($key, $value) = split ('\t', $_);
my @lines = split /\n/, $key;
my $key1 = $lines[1];
$seen{$key1} //= [ $key ];
push (@{$seen{$key1}}, $value);
}
foreach my $key1 ( sort keys %seen ) {
my $tot = 0;
my $file_count = @ARGV;
for my $val ( @{$seen{$key1}} ) {
$tot += ( split /:/, $val )[0];
}
if ( @{ $seen{$key1} } >= $file_count) {
print join( "\t", @{$seen{$key1}});
print "\tcount:". $tot."\n\n";
}
}
Este código funciona bem para arquivos pequenos, mas quando quero comparar arquivos grandes ele ocupa toda a memória, resultando na execução do script sem resultados. Quero modificar o script para que não ocupe memória. Não quero usar nenhum módulo. Acho que se eu carregar apenas um arquivo na memória por vez, isso economizará memória, mas não será possível. Por favor, ajude a modificar meu script.
Responder1
Você tentou awk
? Não tenho certeza se ele lidará melhor com arquivos grandes, perl
mas pode valer a pena tentar:
Dentro do seu script awk:
BEGIN {
RS=">[0-9]+"
}
FNR==1{next}
NR==FNR {
a[$1]++
next
}
$1 in a {
b[$1]++
next
}
{
c[$1]++
}
END {
for (key in a) {
if (b[key] == "") {
printf key"\tfile1:"a[key]"\t\tcount:"a[key]"\n"
} else {
printf key"\tfile1:"a[key]"\tfile2:"b[key]"\tcount:"a[key]+b[key]"\n"
}
}
for (key in c) {
printf key"\t\tfile2:"c[key]"\tcount:"c[key]"\n"
}
}
Para executá-lo:
$ awk -f myscript.awk file1 file2 > output.txt
Testei com:
arquivo1
>1
ATGCCGTT file1:1
+
HHHHKKKK
>2
ATTCCAACg file2:1
+
=#GJLMNB
arquivo2
>2
ATGCCGTT file2:3
+
JJKHHTTT
>3
ATTCCAAC file2:1
+
=#GJLMNB
Saída no terminal:
ATTCCAACg file1:1 count:1
ATGCCGTT file1:1 file2:1 count:2
ATTCCAAC file2:1 count:1
Responder2
Adicione esses encantamentos místicos ao seu programa
use DB_File;
my %seen;
unlink '/tmp/translation.db';
sleep 2;
tie ( %seen, 'DB_File', '/tmp/translation.db' )
or die "Can't open /tmp/translation.db\n";
e seu hash não residirá mais na memória, mas em um banco de dados em disco. Você pode deixar o resto do seu código exatamente como está. É verdade que usei o módulo DB_File, mas não há razão para não fazê-lo. Ele vem com cadaperlinstalação pronta para uso para que você não precise instalá-lo nem nada.
Eu uso essa abordagem o tempo todo se meus hashes estão ficando realmente grandes e descubro que, depois de passar por algum ponto de abraço vagamente definido, as coisas aceleram um pouco.