fusionar varios archivos según la primera columna y ahorrar problemas de memoria

fusionar varios archivos según la primera columna y ahorrar problemas de memoria

Tengo varios archivos fastq delimitados por tabulaciones. Quiero hacer coincidir la segunda línea de cada lectura y sumar los valores junto a ella si coinciden. Por ejemplo:


file1.fq
>1
ATGCCGTT      file1:1
+
HHHHKKKK

file2.fq
>2
ATGCCGTT       file2:3
+
JJKHHTTT

>3
ATTCCAAC        file2:1
+
=#GJLMNB

El resultado que quiero es como:


output.txt

ATGCCGTT      file1:1    file2:3     count:4
ATTCCAAC      file2:1          count:1

El código que he escrito es:



#!/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 bien para archivos pequeños, pero cuando quiero comparar archivos grandes, ocupa toda la memoria, lo que hace que el script se ejecute sin resultados. Quiero modificar el script para que no ocupe memoria. No quiero utilizar ningún módulo. Creo que si cargo solo un archivo en la memoria a la vez, ahorraré memoria pero no podré hacerlo. Por favor ayuda a modificar mi script.

Respuesta1

Has probado awk? No estoy seguro de que maneje mejor archivos grandes, perlpero podría valer la pena intentarlo:

Dentro de tu 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 ejecutarlo:

$ awk -f myscript.awk file1 file2 > output.txt

Lo probé con:

archivo1

>1
ATGCCGTT      file1:1
+
HHHHKKKK

>2
ATTCCAACg        file2:1
+
=#GJLMNB

archivo2

>2
ATGCCGTT       file2:3
+
JJKHHTTT

>3
ATTCCAAC        file2:1
+
=#GJLMNB

Salida en terminal:

ATTCCAACg   file1:1         count:1
ATGCCGTT    file1:1 file2:1 count:2
ATTCCAAC            file2:1 count:1

Respuesta2

Añade estos encantamientos místicos a tu 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";

y su hash ya no residirá en la memoria sino en una base de datos en el disco. Puedes dejar el resto de tu código exactamente como está. Es cierto que utilicé el módulo DB_File pero realmente no hay razón para no hacerlo. Viene con cadaperlaInstalación lista para usar para que no tenga que instalarla ni nada.

Utilizo este enfoque todo el tiempo si mis hashes se vuelven realmente enormes y encuentro que, después de pasar un punto de amplitud vagamente definido, las cosas se aceleran bastante.

información relacionada