1

私は、次の形式の生物学的配列データ (FASTA) を頻繁に使用しています。先頭の左山かっこは、新しい配列ヘッダーを示す区切り文字として使用されます。これらのファイルには、多くの場合、テキストの折り返しがあります (ヘッダーを除く)。

>header1
ACTGACTGACTGACTG
ACTGACTGACTGACTG
>header2
CTGGGACTAGGGGGAG
CTGGGACTAGGGGGAG

ファイル全体が数 MB (場合によっては GB) になる可能性があるため、ファイル全体をメモリに読み込むことを避けたいことがよくあります。そのため、while ループと 1 行ずつの読み込みに集中するようにしています。ただし、ファイルの先頭または末尾に独自の処理を行うためにコードを追加する必要があることがよくあります。たとえば、今日、一部のファイルのテキスト ラッピングを削除したいと考えました。

while (my $line = <$inputfasta_fh>) {
    chomp($line);
    if ($line =~ /^>/) {
        print $outputfasta_fh "$line\n";
    }
    else {
        print $outputfasta_fh $line;
    }
}

しかし、最初のヘッダーを除くすべてのヘッダーの前に改行が必要であることに気付きました (そうしないと、前のシーケンスの最後に連結されます)。だから、これは私の大雑把な回避策です。

my $switch = 0;
while (my $line = <$inputfasta_fh>) {
    chomp($line);
    if ($line =~ /^>/) {
        if ($switch == 1) {
            print $outputfasta_fh "\n";
        }
        print $outputfasta_fh "$line\n";
        $switch = 1;
    }
    else {
        print $outputfasta_fh $line;
    }
}

以前は、最後の行で何かをする必要があるという他の問題がありました。たとえば、fasta を読み取り、各ヘッダーを保存し、そのシーケンスの長さを (再び行ごとに) カウントし、指定した範囲内にある場合は、別のファイルに保存するスクリプトがありました。長さが最大を超えるとカウントは中止されますが、別のヘッダーまたはファイルの終わりに到達するまで、長さが最小を超えているかどうかはわかりません。後者の場合、while ループの下で長さチェック サブルーチンを繰り返さなければなりませんでした。最後の部分を繰り返すことは避けたいと思います。

my $length = 0;
my $header;
my @line_array;

while (my $line = <$inputfasta_fh>) {
    chomp($line);
    if ($line =~ /^>/) {
        # check if previous sequence had a length within range
        if (check_length($length, $minlength, $maxlength) == 1) {
            print $outputfasta_fh "$header\n";
            print $outputfasta_fh join ("\n", @line_array), "\n";
        }
        undef @line_array;
        $header = $line;
        $length = 0;
    }
    else {
        if ($length <= $maxlength) { # no point in measuring any more
            push (@linearray, $line);
            $length += length($line);
        }
    }
}

#and now for the last sequence
if (check_length($length, $minlength, $maxlength) == 1) {
    print $outputfasta_fh "$header\n";
    print $outputfasta_fh join ("\n", @line_array), "\n";
}

sub check_length {
    my ($length, $minlength, $maxlength) = @_;
    if (($length >= $minlength) && ($length <= $maxlength)) {
        return 1;
    }
    else {
        return 0;
    }
}

それで、私の基本的な質問は、カウンターに頼ったり、ループの外でコードを繰り返したりせずに、ループ内で何かをしたいことをどのように示すかということです。助けてくれてありがとう!

4

2 に答える 2

3

あなたが説明した2つの問題の解決策を次に示します。それらは、BioPerlディストリビューションのモジュールを使用して解決されます。この場合、ファイルを開くためのBio::SeqIOモジュールと、それが提供するいくつかのメソッド (長さ、幅) のためのBio::Seqモジュール。ソリューションがいかに単純化されているかがわかります。

#!/usr/bin/perl
use strict;
use warnings;
use Bio::SeqIO;

my $in  = Bio::SeqIO->new( -file   => "input1.txt" ,
                           -format => 'fasta');
my $out = Bio::SeqIO->new( -file   => '>test.dat',
                           -format => 'fasta');

while ( my $seq = $in->next_seq() ) {
    $out->width($seq->length); # sequence on 1 line.
    $out->write_seq($seq);
}

my ($minlen, $maxlen) = (40, 1000);

while ( my $seq = $in->next_seq() ){
    my $len = $seq->length;
    out->write_seq($seq) if $minlen <= $len && $len <= $maxlen;
}

モジュールを調べてみる価値はあります。これら 2 つの例からわかるように、結果のコードははるかに簡潔で理解しやすいものになっています。BioPerl wikiを見回すことができます。HOWTOには、すぐに使用できる例がいくつか示されています。

于 2013-07-06T04:27:18.797 に答える
1

正確に何を達成したいのかは明確ではありません。
ただし、特殊なケースが最初の行または最後の行であることが確実にわかっている場合は、それに対処する方法がいくつかあります。

通常の加工が不要な特殊な1行目

Process first line
$line = <$INPUT>;
... process line

Regular processing
while(<$INPUT>) {
... process lines
}

通常の処理も必要な特別な最初の行

Process first line
$line = <$INPUT>;
... process line

Regular processing
do {
... process lines
} while(<$INPUT>);

特別な最後の行、

ここでは、最後の行を事前に特定する方法がないため、ループで実行する必要があります (行数が正確にわかっていforて、最初の N-1 に対してループを使用し、最後の行を個別に処理しない限り) )

while(<$INPUT>) {
   break if islastline();
   ... process lines
}
... process last line

また

while(<$INPUT>) {
   ... process lines
   break if islastline();
}
... process last line

また

for($i=0; $i<N-1 ; $i++) {
   $line = <$INPUT>;
   ...process lines
}
$line = <$INPUT>
... process last line

あなたが説明する他の状況では、カウントする必要があり、完了した後、ループは続行されますが、もうカウントする必要はありません。カウントが「クリーン」に見えるコードが気になる場合は、ループを 2 つに分割します。

内部仮処理

first part does the whole package
while(<$INPUT>) {
   ...regular processing
   ...special processing
   break if specialProcessingDone();
}

second part does not need to do special processing anymore
while(<$INPUT>) {
   ...regular processing
}
于 2013-07-06T02:45:12.193 に答える