簡體   English   中英

Perl對數組中值的滑動窗口求和

[英]Perl to sum sliding windows of values in an array

我想根據第四列組織的制表符分隔的數據數組創建一個求和的移動窗口。 為簡單起見,我將不相關的字段替換為X,並添加了第一行中顯示的標題:

ID-Counts    X      X     Start    X      X     Locations      XXXX
 X-5000     [X]    [X]     0      [X]    [X]      1           [X...]
 X-26       [X]    [X]     1      [X]    [X]      1           [X...]
 X-34       [X]    [X]     1      [X]    [X]      0           [X...]
 X-3        [X]    [X]     20     [X]    [X]      9           [X...]
 X-200      [X]    [X]     30     [X]    [X]      0           [X...]
 X-1        [X]    [X]     40     [X]    [X]      5           [X...]

第一列包含數字ID,並以連字符連為單位對該ID進行計數。 第四列包含我想用來對數據進行分組的所有起始站點。 第七列包含我需要用來對計數進行歸一化的位置數。

我想為每一行求和的總值是通過將ID中的計數除以位置數+ 1來確定的(例如,第一行的值為2500,第2行為13,第3行與34)。 然后,我想對第4列中的值彼此相隔20個單位的每一行加總這些計數/(位置+1),從值0-19開始,然后從1-20、2-21等開始。例如,窗口0(第4列值在0-19之間)將對1-3行進行求和,窗口1將對2-4行進行求和,窗口2僅對4行進行求和,依此類推。

我的理想輸出將是兩列:第一列具有20unit-window(0,1,2,...)的開頭,第二列具有每個窗口的總和(在上面的數據2547、47.3等中) )。

我制作了一個perl腳本,該腳本將數據過濾和組織為這種格式,並希望添加代碼以在20個單位的窗口中進行求和。 作為一個perl新手,我將不勝感激任何幫助和解釋。 我熟悉跨列的拆分和算術函數,但是我完全不知道如何在數組的移動窗口中進行拆分。 謝謝。

希望我能很好地理解您的問題。 您如何看待這些實現?

解決方案1:每當輸出文件到達單位窗口(20)時,就將其寫入。

#Assuming that you have an array of sums (@sums) and name of file ($filename)
my $window_no  = 20;
my $window_sum = 0;
my @window_nos = ();

for (my $i = 1; $i <= $#sums; $i++) {
    push (@window_nos, $i);
    if ( i % window_no == 0 ) {
        write_file($filename, join(',', @window_nos) . "\t" . $window_sum . "\n");
        $window_sum = 0;
        @window_nos = ();
    }
}


if (scalar @window_nos > 1) {
    write_file($filename, join(',', @window_nos) . "\t" . $window_sum) . "\n");
} 

解決方案2:將值追加到標量變量,並使用該值一次將其寫入輸出文件。

#Assuming that you have an array of sums (@sums) and name of file ($filename)
my $window_no     = 20;
my $window_sum    = 0;
my @window_nos    = ();
my $file_contents = '';

for (my $i = 1; $i <= $#sums; $i++) {
    push (@window_nos, $i);
    if (i % window_no == 0) {            
        $file_contents .= join(',', @window_nos) . "\t" . $window_sum . "\n";
        $window_sum = 0;
        @window_nos = ();
    }
}

if (scalar @window_nos > 1) {
    $file_contents .= join(',', @window_nos) . "\t" . $window_sum . "\n";
}

write_file($filename, $file_contents);

查看以下代碼,看看它是否滿足您的要求。 也許可以進行一些優化,但是我基本上是在當前Start上方20個單位窗口內對所有Start進行了蠻力搜索。

輸出:

0-19:  2547.000000
1-20:  47.300000
20-39:  200.300000
30-49:  200.166667
40-59:  0.166667


use strict;
use warnings;

#  Hash indexed by Start
#  Each value contains the sum of all ( Counts/Locations+1 ) for
#     this Start value
my %sum;

while (<DATA>)
{
    #  ignore comments
    next if /^\s*#/;
    my ( $id_count,undef,undef,$start,undef,undef,$numLocations ) = 
       split ' ';
    my ($id,$count) = split '-',$id_count;
    $sum{$start} += $count / ( $numLocations + 1 );
}  

foreach my $start ( sort keys %sum )
{
   my $totalSum = 0;
   #  Could probably be optimized.
   foreach my $start2 ( $start .. $start+19 )
   {
      $totalSum += $sum{$start2} if defined($sum{$start2});      
   }
   printf "%d-%d:  %f\n", $start, $start+19, $totalSum;
}

__DATA__
#ID-Counts    X      X     Start    X      X     Locations      XXXX
 X-5000     [X]    [X]     0      [X]    [X]      1           [X...]
 X-26       [X]    [X]     1      [X]    [X]      1           [X...]
 X-34       [X]    [X]     1      [X]    [X]      0           [X...]
 X-3        [X]    [X]     20     [X]    [X]      9           [X...]
 X-200      [X]    [X]     30     [X]    [X]      0           [X...]
 X-1        [X]    [X]     40     [X]    [X]      5           [X...]

這個怎么樣?

#!/usr/bin/perl -Tw

use strict;
use warnings;
use Data::Dumper;

my %sum_for;

while ( my $line = <DATA> ) {

    if ( $line !~ m{\A [#] }xms ) {

        $line =~ s{\A \s* ( [^-]+ ) - }{$1 }xms;    # separate the ID

        my @columns = split /\s+/, $line;    # assumes no space in values

        my $count = $columns[1];
        my $start = $columns[4];
        my $locat = $columns[7] + 1;

        $sum_for{$start} += $count / $locat;
    }
}

print Dumper( \%sum_for );

my @start_ranges;
{
    my ($max_start) = sort { $b <=> $a } keys %sum_for;

    # max => range count
    #  10 => 1
    #  20 => 2
    #  30 => 2
    #  40 => 3
    #  50 => 3
    #  ...
    my $range_count = $max_start / 20;

    push @start_ranges, [ 0, 19 ];

    for ( 1 .. $range_count ) {

        push @start_ranges, [ map { $_ + 20 } @{ $start_ranges[-1] } ];
    }
}

my %total_for;

for my $range_ra (@start_ranges) {

    my $range_key = sprintf '%d-%d', @{$range_ra};

    for my $start ( $range_ra->[0] .. $range_ra->[1] ) {

        if ( exists $sum_for{$start} ) {

            $total_for{$range_key} += $sum_for{$start};
        }
    }
}

print Dumper( \%total_for );

__DATA__
#ID-Counts    X      X     Start    X      X     Locations      XXXX
 X-5000     [X]    [X]     0      [X]    [X]      1           [X...]
 X-26       [X]    [X]     1      [X]    [X]      1           [X...]
 X-34       [X]    [X]     1      [X]    [X]      0           [X...]
 X-3        [X]    [X]     20     [X]    [X]      9           [X...]
 X-200      [X]    [X]     30     [X]    [X]      0           [X...]
 X-1        [X]    [X]     40     [X]    [X]      5           [X...]

輸出結果如下:

$VAR1 = {
          '1' => 47,
          '40' => '0.166666666666667',
          '0' => 2500,
          '30' => 200,
          '20' => '0.3'
        };
$VAR1 = {
          '40-59' => '0.166666666666667',
          '20-39' => '200.3',
          '0-19' => 2547
        };

在計算起始范圍時花費了一些時間。 感謝您提出有趣的問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM