5

簡単なデータファイルがあります。ファイルの各行には4つの要素があります。一部の行は空白のエントリで埋められていません。他の行には最初のエントリがあり、残りの3行は空白、つまりスペースで「埋められ」ています。タブ区切りのファイルです。

入力ファイルの例:

    .
    .
    .
    30  13387412    34.80391242 sSN_FIRST
    30  13387412    34.80391242 sSN5_40
    30.1             
    30.2             
    30.3             
    30.4             
    31  14740248    65.60590089 s32138223_44
    31  14740248    65.60590089 s321382_LAST
    .
    .
    .

繰り返しになりますが、これが重要な場合、私のファイルの「空白」には実際には1つのスペースが含まれています。

私の全体的な目標は、ファイル全体で2番目と3番目の列(4番目の列は無視されます)を「埋める」ことです。これを行うには、空白の連続する行のセットに加えて、連続する空白行のセットの直前の行と直後の行を識別するスクリプトが必要です。上記の例では、これは2行目から7行目です。これができたら、隣接する行の情報を使用して、間にある行の欠落しているエントリを「埋める」のに役立てることができます。

私はこの関数を実験してきましたuntilが、データ行を行ごとに読み取るループと結合することに成功していません。たとえば、行を読んで空白行を見つけることができます。

open( my $FILE, "<$mapfile" );
my @file = <$FILE>;
close $FILE;

for ( my $i = 1 ; $i < scalar @file ; $i++ ) 
    {
     my @entries = split( '\t', $file[ $i ] );
     if ( $entries[ 1 ] =~ m/ / ) 
        {
         print $file[ $i ]."\n";
        }
    }

しかし、私はこのuntil関数を使用して、行を読み取り、探している行の連続セット(「空白」行と隣接する2つの「完全」行)を検索しようとしています。例えば:

until ( $file[ a line ] =~ m/ / && $file[ another line ] =~ m/ / )   
    {
     my linear interpolation here;
    }

誰かが配列を読み取り、行を比較してファイル全体で必要なセットを見つける方法を組み合わせる方法についてのヒントを教えてもらえますか?

4

2 に答える 2

3

実装したいのは、キャッシュアルゴリズムです。これは、以前の値を記憶(キャッシュ)し、新しい値が表示されない場合にそれらを使用するものです。このための正規表現も必要ありません。:)

古い値をキャッシュすることに加えて、その間の行をキャッシュする必要があります。必要なのはラベルだけなので、それらを保持するだけで済みます。次に、次のフルラインに到達したら、補間を実行して結果を出力できます。

これが私がそれをする方法です。これは私の元の例よりも少し複雑ですが、同じ原則が適用されます。中間行を保存し、ターミナルに到達したときに結果を出力します。

use strict;
use warnings;
use feature 'say';


# Get start conditions, and cache those numbers.

sub read_block
{
   my $line = <DATA>;
   return 1 unless defined $line; # we're done if nothing more to read

   # Process and store data from the first line in the block.
   chomp $line;
   my ($last_label, $last_num1, $last_num2, $last_label2) = split /\t/, $line;

   # Keep reading lines until we find the end of the block.
   my @label_cache;
   my $found_last = 0;
   my ($label1, $num1, $num2, $label2);
   while (!$found_last)
   {
      $line = <DATA>;
      chomp $line;
      ($label1, $num1, $num2, $label2) = split /\t/, $line;
      if (defined $num1 && defined $num2)
      {
         $found_last = 1; # We have final numbers!  We can interpolate now.
      }
      else
      {
         push @label_cache, $label1; 
      }
   }

   # Begin display.  Show the first line of the block.
   say "$last_label\t$last_num1\t$last_num2\t$last_label2";

   # Calculate the slope for interpolation: (last - first) / difference
   my $slope1 = ($num1 - $last_num1) / (@label_cache + 1);
   my $slope2 = ($num2 - $last_num2) / (@label_cache + 1);
   my $distance = 0;

   # Display each label and the lines inside.
   foreach my $label (@label_cache)
   {
      ++$distance;
      say $label, "\t",
          $slope1 * $distance + $last_num1, "\t",
          $slope2 * $distance + $last_num2;
   }

   # Display the final line in the block.
   say "$label1\t$num1\t$num2\t$label2";

   # Not done yet, so return a 'false' value.
   return 0;
}

# Main part of the script

my $done = 0;
while (! $done)
{
   $done = read_block();
}


__DATA__
a   3   4   end
e
f
g
h
i
k   15  26  start
k   15  26  end
o
p
q
r
s   3   5   start
s   3   5   end
v
w
x
y   14  16  start

放出:

a       3       4       end
e       5       7.66666666666667
f       7       11.3333333333333
g       9       15
h       11      18.6666666666667
i       13      22.3333333333333
k       15      26      start
k       15      26      end
o       12.6    21.8
p       10.2    17.6
q       7.8     13.4
r       5.4     9.2
s       3       5       start
s       3       5       end
v       5.75    7.75
w       8.5     10.5
x       11.25   13.25
y       14      16      start

もちろん、必要な数値の丸めや書式設定を行うこともできます。:)

于 2013-01-09T20:26:17.150 に答える
2

おそらく、次のことが役立つでしょう:

use strict;
use warnings;

my ( $last, $oneColumn );

my @file = <DATA>;

for my $line (@file) {
    my @entires = split ' ', $line;

    if ( @entires == 4 ) {
        if ($oneColumn) {
            print $line;    # Succeeding line
            $oneColumn = 0;
        }
        $last = $line;
        next;
    }

    print $last if $last;    # Preceeding line
    undef $last;
    print $line;             # One-column line
    $oneColumn = 1;

}

__DATA__
30  13387412    34.80391242 sSN_FIRST
30  13387412    34.80391242 sSN5_40
30.1             
30.2             
30.3             
30.4             
31  14740248    65.60590089 s32138223_44
31  14740248    65.60590089 s321382_LAST

出力:

30  13387412    34.80391242 sSN5_40
30.1
30.2
30.3
30.4
31  14740248    65.60590089 s32138223_44

「完全な」行には、に4つの要素が含まれている必要が@entriesあり、それがif ( @entires == 4 )検索対象です。見つかった場合は、1列の行が印刷されている場合にのみ、後続の行として印刷されます。次に、行を保存します。if行に3つのタブがない場合にのみ、行が外側に印刷されます。

次の短いスクリプトは同じ出力を生成します。

use strict;
use warnings;

my @file = <DATA>;

for ( my $i = 1 ; $i < $#file ; $i++ ) {

    if ( $file[$i] =~ /(?:\t\s){3}/ ) {
        print $file[ $i - 1 ];    # Preceeding line

        while ( $file[$i] =~ /(?:\t\s){3}/ and $i < $#file ) {
            print $file[ $i++ ]    # One-column line
        }

        print $file[$i];           # Succeeding line
    }
}

__DATA__
30  13387412    34.80391242 sSN_FIRST
30  13387412    34.80391242 sSN5_40
30.1             
30.2             
30.3             
30.4             
31  14740248    65.60590089 s32138223_44
31  14740248    65.60590089 s321382_LAST

タブとスペースの/(?:\t\s){3}/3つの連続したセットに一致します。これは、1列だけの行にのみ表示されます。そのパターンが見つかると、前の行を印刷し、次にwhile、完全な行が見つかるまで、または配列の最後に1列の行が見つかるまで、1列の行を印刷するループに入ります。最後に、次の行が印刷されます。

于 2013-01-09T21:03:32.860 に答える