mesclar vários arquivos com base na primeira coluna e salvar problemas de memória

3

Eu tenho vários arquivos fastq delimitados por tabulações. Quero corresponder a segunda linha de cada leitura e somar os valores ao lado dela se corresponder. 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 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";
    }
}

Esse código funciona bem para arquivos pequenos, mas quando eu quero comparar arquivos grandes, ele ocupa toda a memória, resultando no script sendo executado sem resultados. Eu quero modificar o script para que ele não ocupe memória. Eu não quero usar nenhum módulo. Eu acho que se eu carregar apenas um arquivo na memória de cada vez, ele salvará a memória, mas não conseguirá fazê-lo. Por favor, ajude a modificar o meu script.

    
por Umay Kulsum 27.10.2016 / 07:28

2 respostas

0

Você já tentou awk ? Não tenho certeza se ele lidará com arquivos grandes melhor que perl , mas talvez valha 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:

file1

>1
ATGCCGTT      file1:1
+
HHHHKKKK

>2
ATTCCAACg        file2:1
+
=#GJLMNB

file2

>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
    
por 27.10.2016 / 11:18
0

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 será mais residente na memória, mas em um banco de dados no 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á realmente nenhuma razão para não o fazer. Ele vem com todas as instalações do perl prontas para que você não tenha que instalá-lo nem nada.

Eu uso essa abordagem o tempo todo, se meus hashes estão ficando realmente enormes e eu acho que, depois de passar por um ponto definido de grande friabilidade, as coisas aceleram um pouco.

    
por 19.05.2017 / 10:04

Tags