Thread Marker fürs Bearbeiten bestimmter Bereiche in Textdatei gesucht (11 answers)
Opened by Pauline25 at 2011-08-10 14:06

murphy
 2011-08-10 18:59
#151494 #151494
User since
2004-07-19
1776 Artikel
HausmeisterIn
[Homepage]
user image
Deiner Beschreibung und den Beispielen nach zu Urteilen nehme ich folgendes an:
  • Ein logischer Datensatz besteht aus genau zwei physischen Datensätzen, wobei der erste von diesen mit dem Datensatztyp 'gene' markiert ist.
  • Ein physischer Datensatz besteht aus genau einer Zeile in der Datendatei, welche sich wie folgt aufteilt:
    • Mehrere durch Leerzeichen separierte Kopfdatenfelder, wobei das zweite und dritte Feld den Datensatztyp angeben,
    • ein Trennzeichen zwischen den Kopffeldern und den Datenfeldern, welches entweder '. + .' oder '. + 0' ist und
    • mehrere durch Strichpunkte separierte Datenfelder, wobei jedes Datenfeld einen Schlüssel und einen Wert enthält, die durch ein Gleichheitszeichen getrennt werden.


Dieses Format lässt sich relativ einfach lesen: Zunächst bauen wir eine Hilfsfunktion, die einen physischen Datensatz einliest und die Kopffelder von den Datenfeldern trennt:
Code (perl): (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
sub read_record {
    my ($in) = @_;
    
    if (my $line = <$in>) {
        chomp $line;
        my ($head, $data) = split /\s*[.]\s+[+]\s+[.0]\s*/, $line, 2;
        
        return [split(/\s+/, $head // '')], $data;
    }
    else {
        return;
    }
}

Anschließend bauen wir eine zweite Funktion, welche die Schlüssel und Werte der Datenfelder extrahiert und in einem Hash ablegt:
Code (perl): (dl )
1
2
3
4
5
6
7
8
9
sub push_record {
    my ($record, $data) = @_;
    foreach my $item (split /\s*;\s*/, $data // '') {
        my ($key, $value) = split /\s*=\s*/, $item, 2;
        
        push @{$record->{$key}}, $value;
        @{$record->{$key}} = uniq @{$record->{$key}};
    }
}

Ich speichere hier zu jedem Schlüssel eine Liste von Werten, denn die Schlüssel können sich in mehreren Datensätzen offenbar wiederholen, jedoch sorge ich mittels CPAN:List::MoreUtils::uniq dafür, dass keine identischen Werte doppelt vorkommen.

Das verarbeiten der gesamten Datei in einer Schleife ist jetzt trivial:
Code (perl): (dl )
1
2
3
4
5
6
7
8
9
10
11
12
my %info;
while (my ($head, $data) = read_record($in)) {
    if ($head->[1] eq 'RefSeq' and $head->[2] eq 'gene') {
        my $id = join '/', @{$head}[0,3,4];
        
        my $record = {};
        push_record($record, $data);
        push_record($record, (read_record($in))[-1]);
        
        $info{$id} = $record;
    }
}

Für jeden physischen Datensatz der den korrekten Typ aufweist wird ein zweiter physischer Datensatz eingelesen und die Datenfelder der beiden Zeilen werden in einem Hash kombiniert.

Ich speichere hier alle gefundenen logischen Datensätze wiederum in einem Hash, so dass die endgültige Datenstruktur ein Hash of Hashes of Arrays ist. Als Schlüssel für den äußersten Hash verwende ich eine Konkatenation aller Kopffelder des ersten, mit dem Typ 'gene' markierten physischen Datensatzes, die für mich irgendwie wie Identifikationsnummern aussahen.

Zusammenfassend noch einmal ein komplettes lauffähiges Skript mit Testdaten und der produzierten Ausgabe:
more (27.0kb):
Code (perl): (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#!/usr/bin/env perl
use 5.012;
use warnings;

use List::MoreUtils qw/uniq/;
use Data::Dumper;

sub read_record {
    my ($in) = @_;
    
    if (my $line = <$in>) {
        chomp $line;
        my ($head, $data) = split /\s*[.]\s+[+]\s+[.0]\s*/, $line, 2;
        
        return [split(/\s+/, $head // '')], $data;
    }
    else {
        return;
    }
}

sub push_record {
    my ($record, $data) = @_;
    foreach my $item (split /\s*;\s*/, $data // '') {
        my ($key, $value) = split /\s*=\s*/, $item, 2;
        
        push @{$record->{$key}}, $value;
        @{$record->{$key}} = uniq @{$record->{$key}};
    }
}

my $in = \*DATA; # TODO: open real data file or read from ARGV or STDIN

my %info;
while (my ($head, $data) = read_record($in)) {
    if ($head->[1] eq 'RefSeq' and $head->[2] eq 'gene') {
        my $id = join '/', @{$head}[0,3,4];
        
        my $record = {};
        push_record($record, $data);
        push_record($record, (read_record($in))[-1]);
        
        $info{$id} = $record;
    }
}

print Dumper \%info; # TODO: output or store in the format that you actually need

__DATA__
NC_014171.1 RefSeq gene 11341 14260 . + . ID=NC_014171.1:rrl_1;locus_tag=BMB171_C5090;db_xref=GeneID:9190897
NC_014171.1 RefSeq exon 11341 14260 . + . ID=NC_014171.1:rrl_1:unknown_transcript_1;Parent=NC_014171.1:rrl_1;gbkey=rRNA;locus_tag=BMB171_C5090;product=23S ribosomal RNA;db_xref=GeneID:9190897;exon_number=1
foo bar garbage
NC_014171.1 RefSeq gene 14311 14425 . + . ID=NC_014171.1:rrs_1;locus_tag=BMB171_C5091;db_xref=GeneID:9190898
NC_014171.1 RefSeq exon 14311 14425 . + .
foo bar garbage . + . more=garbage
foo bar garbage
foo bar garbage
NC_014171.1 RefSeq gene 18622 19509 . + . locus_tag=BMB171_C0010;db_xref=GeneID:9191051
NC_014171.1 RefSeq CDS 18622 19506 . + 0 locus_tag=BMB171_C0010;transl_table=11;product=pyridoxine biosynthesis protein;protein_id=YP_003662548.1;db_xref=GI:296500848;db_xref=GeneID:9191051;exon_number=1
NC_014171.1 RefSeq gene 22134 22526 . + . locus_tag=BMB171_C0013;db_xref=GeneID:9190939
NC_014171.1 RefSeq CDS 22134 22523 . + 0 locus_tag=BMB171_C0013;transl_table=11;product=hypothetical protein;protein_id=YP_003662551.1;db_xref=GI:296500851;db_xref=GeneID:9190939;exon_number=1


Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
$VAR1 = {
'NC_014171.1/22134/22526' => {
'exon_number' => [
'1'
],
'db_xref' => [
'GeneID:9190939',
'GI:296500851'
],
'locus_tag' => [
'BMB171_C0013'
],
'protein_id' => [
'YP_003662551.1'
],
'product' => [
'hypothetical protein'
],
'transl_table' => [
'11'
]
},
'NC_014171.1/14311/14425' => {
'db_xref' => [
'GeneID:9190898'
],
'locus_tag' => [
'BMB171_C5091'
],
'ID' => [
'NC_014171.1:rrs_1'
]
},
'NC_014171.1/18622/19509' => {
'exon_number' => [
'1'
],
'db_xref' => [
'GeneID:9191051',
'GI:296500848'
],
'locus_tag' => [
'BMB171_C0010'
],
'protein_id' => [
'YP_003662548.1'
],
'product' => [
'pyridoxine biosynthesis protein'
],
'transl_table' => [
'11'
]
},
'NC_014171.1/11341/14260' => {
'exon_number' => [
'1'
],
'gbkey' => [
'rRNA'
],
'db_xref' => [
'GeneID:9190897'
],
'locus_tag' => [
'BMB171_C5090'
],
'ID' => [
'NC_014171.1:rrl_1',
'NC_014171.1:rrl_1:unknown_transcript_1'
],
'product' => [
'23S ribosomal RNA'
],
'Parent' => [
'NC_014171.1:rrl_1'
]
}
};


Ich hoffe, das hilft Dir weiter und lässt sich leicht für den tatsächlichen Anwendungsfall anpassen :-)
When C++ is your hammer, every problem looks like your thumb.

View full thread Marker fürs Bearbeiten bestimmter Bereiche in Textdatei gesucht