0

私はかなりユニークな問題で立ち往生しています。私が読んでいる2つのファイルがあります。これら 2 つのファイルの小さいバージョンは、次のようになります。

ファイル1

chr1    9873    12227   11873   2354    +   NR_046018   DDX11L1
chr1    760970  763155  762970  2185    +   NR_047520   LOC643837

ファイル2

chr1    9871    0   
chr1    9872    1
chr1    9873    1
chr1    9874    2
chr1    9875    1
chr1    9876    3
chr1    9877    3
chr1    760970  1
chr1    760971  1
chr1    760972  1
chr1    760973  2
chr1    760974  3
chr1    760975  3
chr1    760976  4
chr1    760977  5
chr1    760978  6
chr1    760979  7
chr1    760980  6
chr1    760981  7
chr1    760982  8
chr1    760983  9
chr1    760984  10
chr1    760985  11
chr1    760986  12
chr1    760987  10
chr1    760988  9
chr1    760989  6

問題

  1. 最初のファイルから、各行から 2 番目の要素を取得し、それを$start. 終了位置は によって決定され$end = $start + 10ます。

  2. に基づいて$start、2 番目のファイルを取得し、各行の 2 番目の要素を確認する必要があります。が見つかったら$start、3 番目の要素の次の 5 つの対応する値を 5 個ずつ合計する必要があります$end

そのまま$end$start + 10、5 つのグループで合計すると、2 つの合計値が得られます。


$end2 番目のファイルの 2 番目の要素に最大の値が存在しない場合、コードは停止せず、合計を実行し続け、合計を 0 として表示する必要があります (連続する 5 つの要素のグループが存在しない場合)。

ここでファイルの例を挙げると、File19873から、に割り当てられている2 番目の要素 = $start。したがって$end$start+10つまり 9883 になります。

File2から、行の 2 番目の要素で1 回$start検出され、次の 5 行の 3 番目の要素を 1 つのグループとして合計し、次の 5 つの値を 2 番目のグループとして合計する必要があります$end

ノート

ここでFile2に見られるように、$endつまり 9883 は存在しません。したがって、9879 から 9883 までの値の合計は でなければなりませんzero。760970 以降の値を合計してはなりません...

望ましい出力

chr1    9873    12227   11873   2354    +   NR_046018   DDX11L1      10   0
chr1    760970  763155  762970  2185    +   NR_047520   LOC643837    8   25

注意事項

  1. 実際のファイルを扱っている間、 $end = $start+10,000( $end = $start+10 の代わりに)
  2. また、同じメモで、25 の値のグループが ( 5 ではなく) 合計され、実際のファイルの操作中に合計 400 の値が取得されます。
  3. $file2 の 2 番目の要素に存在しない値の範囲がある場合、25 個の値の連続したペアが存在しない場合は、合計が通常どおり続行され、0出力されます。
  4. ファイルには、それぞれ 100 万行を超える行が含まれています。

コード

これまでに書いたコードは、次のことを管理しています。

  1. ファイルから読み取ります。
  2. 割り当て$start$endfile1から
  3. file2から、すべての 2 番目の要素を array にプッシュし@c_posnます。すべての 3 番目の要素を array に@peak
  4. $startが存在するかどうかを確認します@c_posn

合計部分の実行方法がわかりません。2番目のファイルのすべての2番目の要素がキーになり、3番目の要素がになるハッシュを作成することを考えていました。しかし、ハッシュは順不同です。そこで、2 番目の要素と 3 番目の要素の 2 つ@c_posnの配列を作成しました@peaks。しかし、今では2つの配列を同時に比較する方法がわかりません(760970の値が合計されないようにするため

use 5.012;
use warnings;
use List::Util qw/first/;

my $file1 = 'chr1trialS.out';
my $file2 = 'b1.wig';

open my $fh1,'<',$file1 or die qw /Can't_open_file_$file1/;
open my $fh2,'<',$file2 or die qw /Can't_open_file_$file2/;

my($start, $end);
while(<$fh1>){
    my @val1 = split;
    $start = $val1[1]; #Assign start value
    $end = $start + 10; #Assign end value
    say $start,"->",$end; #Can be commented out
}

my @c_posn;
my @peak;

while(<$fh2>){
    my @val2 = split;   
    push @c_posn,$val2[1]; #Push all 2nd elements 
    push @peak, $val2[2];  #Push all 3rd elements        
}           

if (first { $_ eq $start} @c_posn) { say "I found it! " } #To check if $start is present in @c_posn

say "@c_posn"; #just to check all 2nd elements are obtained
say "@peak"; #just to check all 3rd elements are obtained   

私の問題を解決するために時間を割いていただきありがとうございます。説明が必要な場合は、私に尋ねてください。すべてのコメント/回答に感謝します。

4

3 に答える 3

2

b1.wigがメモリ内のハッシュに読み込まれ、列 2 からキーを取得し、列 3 から値を取得するのに十分小さい場合、これを行うのは簡単です。対応するハッシュ要素は存在しません (したがって、それにアクセスすると が返されますundef)。

新しい合計を既存のデータからどのように分離したいかを言っていないchr1trialS.outので、スペースを使用しました。もちろん、これは必要に応じて簡単に変更できます。

use strict;
use warnings;

use constant SAMPLE_SIZE => 10;
use constant CHUNK_SIZE => 5;

my $file1 = 'chr1trialS.out';
my $file2 = 'b1.wig';

my %data2;
{
  open my $fh, '<', $file2 or die $!;

  while (<$fh>) {
    my ($key, $val) = (split)[1,2];
    $data2{$key} = $val;
  }
}

open my $fh, '<', $file1 or die $!;

while (<$fh>) {
  chomp;
  my $key = (split)[1];
  my @totals;
  my $n = 0;
  while ($n < SAMPLE_SIZE) {
    push @totals, 0 if $n++ % CHUNK_SIZE == 0;
    $totals[-1] += $data2{$key++} // 0;
  }
  print "$_ @totals\n";
}

出力

chr1    9873    12227   11873   2354    +   NR_046018   DDX11L1 10 0
chr1    760970  763155  762970  2185    +   NR_047520   LOC643837 8 25
于 2013-01-02T18:52:00.200 に答える
2

あなたはハッシュについて正しい考えを持っていました。順序付けられているかどうかは特に関係ありません。私の理解が正しければ、ファイル内の開始値と次の 10 の値 (9873) ではなく、11 の特定の値 (9873、9874、9875... 9883) を探しているからです。 、... 9877、760970、... 760975)。

あなたの説明に基づいて、私はそれをどのように行うかを次に示します。

#!/usr/bin/env perl

use strict;
use warnings;

my $sum_interval = 5;   # number of lines to group into each sum
my $sum_count = 2;      # number of sums to generate
my @sums;               # final results of the operation

my %lookup;
open my $fh2, '<', 'file2.txt' or die "Can't open file 2: $!";
while (<$fh2>) { 
  my @data = split;
  $lookup{$data[1]} = $data[2];
}
close $fh2;

open my $fh1, '<', 'file1.txt' or die "Can't open file 1: $!";
while (my $line = <$fh1>) { 
  my @line_sums;
  my $start = (split /\s+/, $line)[1];
  for my $interval_num (0 .. $sum_count - 1) {
    my $cur_sum = 0;
    my $interval_start = $start + ($sum_interval * $interval_num);
    for (0 .. $sum_interval - 1) {
      # use || instead of // for Perl older than 5.10
      $cur_sum += $lookup{$interval_start + $_} // 0;
    }
    push @line_sums, $cur_sum;
  }
  push @sums, \@line_sums;
}
use Data::Dumper; print Dumper(\@sums);

変数名はおそらく改善される可能性がありますが、25 と 400 に変更するだけ$sum_interval$sum_count、実際のアプリケーションで同じように機能するはずです。

提供したサンプル データを および に配置するfile1.txtfile2.txt、次の出力が生成されます。

$VAR1 = [
          [
            10,
            0
          ],
          [
            8,
            25
          ]
        ];

この出力は、手動で合計した場合に得られる結果と一致します。

5 つの 2 つのグループを合計する必要があり、toが 11 項目であると述べたからではなく、$starttoから合計するという点で、仕様から少し逸脱していることに注意してください。$start + 9$start + 10$start$start + 10

編集:初期の疑似コードを完全で実行可能なプログラムに修正しました。

于 2013-01-02T11:04:16.957 に答える
1

これが私の現在の解決策です:

#!/usr/bin/perl

use 5.012; use warnings;

my $file1 = Reader->open("<", "filename1");
my $file2 = Reader->open("<", "filename2");

my $groupsize = 5;
my $step = 10;
my $sum_number = int($step / $groupsize) + ($step % $groupsize ? 1 : 0); # ceil($step/$groupsize)

use constant DEBUG_FLAG => 0;
sub DEBUG (@)   { say STDERR "DEBUG: ", @_ if DEBUG_FLAG }

LINE1:
while (my $line1 = $file1->readline) {
    my (undef, $start) = split ' ', $line1, 3;
    my $end = $start + $step;
    my @sums = (0) x $sum_number; # initialize all fields to zero
    my $i = 0;
    my $last;
    LINE2:
    while (my $line2 = $file2->readline) {
        my (undef, $key, $val) = split ' ', $line2, 4;
        if ($start > $key) { # throw away all keys that are too small
            DEBUG "key $key too small for start $start";
        } elsif ($key >= $end) { # termination condition
            DEBUG "key $key too large for end $end";
            $file2->pushback($line2);
            last LINE2;
        } else {
            $last = $key unless defined $last;
            $i += $key - $last; # get interval. This may be set to "1" as an optimization
            DEBUG "counting ($i): $sums[$i/$groupsize] + $val at $key";
            $sums[$i/$groupsize] += $val;
            $last = $key;
        }
    }
    DEBUG "inner loop broken";
    say join "\t", $line1, @sums; # assuming tab-seperated output
}

{
    package Reader;
    # There is probably a CPAN module for this ... :/
    use Carp;
    use constant DEBUG_FLAG => 0;
    sub open :method {
        my ($class, $mode, $filename) = @_;
        open my $fh, $mode, $filename or die qq(Can't open "$filename": $!);
        bless [$fh, []] => $class;
    }
    sub readline :method {
        my $self = shift;
        return shift @{ $self->[1] } if @{ $self->[1] };
        my $line = scalar readline $self->[0];
        chomp $line if defined $line;
        carp "readline: " . ($line // "undef") if DEBUG_FLAG;
        return $line;
    }
    sub pushback {
        my ($self, $line) = @_;
        carp "pushback: " . ($line // "undef") if DEBUG_FLAG;
        unshift @{ $self->[1] }, $line;
        return $self;
    }
    sub eof :method {
        my $self = shift;
        eof $self->[0];
    }
}

出力:

chr1    9873    12227   11873   2354    +   NR_046018   DDX11L1         10      0
chr1    760970  763155  762970  2185    +   NR_047520   LOC643837       8       25

このソリューションは、両方の入力ファイルが 2 番目のフィールドで昇順で並べ替えられ、重複するシーケンスが要求されないことを前提としています。これらの条件が満たされる場合、一定のメモリと線形時間で実行されます。そうでない場合は、ガベージが生成され、他の答え (線形メモリ、線形時間、制限なし) を使用することに興味があるかもしれません。実際、Dave Sherohman による回答は、一般的にそれほど脆弱ではなく、おそらくほとんどの入力でより高速に実行されます。

システムによっては、すべてのオブジェクト指向を破棄し、行 (または行) をバッファリングするためのコードをインライン化すると、速度が向上する場合があります。

について$i = $key - $last: キーがスキップされてもコードは機能し続け、番号は正しいバケットに追加されます。キーがスキップされない、または正しい合計が無関係であると断言できる場合 (次の 5 つの ID ではなく、ID が より小さい最初の 5 行を$end追加する必要がある)、$last変数を削除して単純に 1 ずつインクリメント$iするだけで問題ありません。

于 2013-01-02T12:44:21.593 に答える