Zusammenführen mehrerer Dateien basierend auf der ersten Spalte und Speichern von Speicherproblemen

Zusammenführen mehrerer Dateien basierend auf der ersten Spalte und Speichern von Speicherproblemen

Ich habe mehrere durch Tabulatoren getrennte Fastq-Dateien. Ich möchte die zweite Zeile jedes Lesevorgangs abgleichen und die daneben liegenden Werte addieren, wenn sie übereinstimmen. Beispiel:


file1.fq
>1
ATGCCGTT      file1:1
+
HHHHKKKK

file2.fq
>2
ATGCCGTT       file2:3
+
JJKHHTTT

>3
ATTCCAAC        file2:1
+
=#GJLMNB

Die gewünschte Ausgabe sieht etwa so aus:


output.txt

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

Der Code, den ich geschrieben habe, ist:



#!/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";
    }
}

Dieser Code funktioniert gut für kleine Dateien, aber wenn ich große Dateien vergleichen möchte, belegt er den gesamten Speicher, sodass das Skript ohne Ergebnisse ausgeführt wird. Ich möchte das Skript so ändern, dass es keinen Speicher belegt. Ich möchte keine Module verwenden. Ich denke, wenn ich jeweils nur eine Datei in den Speicher lade, wird zwar Speicher gespart, aber ich kann es nicht. Bitte helfen Sie mir, mein Skript zu ändern.

Antwort1

Haben Sie es versucht awk? Ich bin nicht sicher, ob es mit großen Dateien besser zurechtkommt als, perlaber einen Versuch könnte es wert sein:

In Ihrem Awk-Skript:

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"
    }
}

So führen Sie es aus:

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

Getestet mit:

Datei1

>1
ATGCCGTT      file1:1
+
HHHHKKKK

>2
ATTCCAACg        file2:1
+
=#GJLMNB

Datei2

>2
ATGCCGTT       file2:3
+
JJKHHTTT

>3
ATTCCAAC        file2:1
+
=#GJLMNB

Ausgabe im Terminal:

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

Antwort2

Fügen Sie diese mystischen Beschwörungen zu Ihrem Programm hinzu

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";

und Ihr Hash wird nicht mehr im Speicher, sondern in einer Datenbank auf der Festplatte gespeichert. Sie können den Rest Ihres Codes genau so lassen, wie er ist. Ich habe zwar das Modul DB_File verwendet, aber es gibt wirklich keinen Grund, das nicht zu tun. Es ist in jedemperlDie Installation ist sofort einsatzbereit, Sie müssen es also nicht installieren oder ähnliches.

Ich verwende diesen Ansatz ständig, wenn meine Hashes wirklich riesig werden, und ich stelle fest, dass sich die Dinge nach dem Überschreiten eines vage definierten Größenpunkts erheblich beschleunigen.

verwandte Informationen