2011-07-08 53 views
0

我現在面臨的問題是用另一列中的值定義滑動窗口來總結數字列。總結一個滑動窗口的數值列在另一列中的值

列表(1)的數據是製表符分隔的,用兩個數字列:

1000 12 
2000 10 
3000 9 
5000 3 
9000 5 
10000 90 
30000 20 
31000 32 
39000 33 
40000 28 

(2)欲與由第一列定義的窗口來總結第二列中,窗口大小爲(第1列+ 3000)。這意味着我需要添加第3列(第3列=總和(第1列的行中第2列的所有值到第1列+3000))。

它看起來像這樣:

1000 12 12+10+9 
2000 10 10+9+3 
3000 9 9 
5000 3 3 
9000 5 5+90 
10000 90 90 
30000 20 20+32 
31000 32 32 
39000 33 33 
40000 28 

(3)我是新編程。我嘗試過awk,但我失敗了。

不知如何控制窗口的第一列

AWK「(I = 1;我< = NR;我++){總和[I] + = $ 2} END {打印$ 1,$ 2 ,sum}'mydata

任何人都可以給我任何關於這個問題的建議/方向?提前致謝。

最佳,

+0

我只看到你的樣本數據一行。也許你可以重新格式化,以便你的樣本數據適合你的問題。使用代碼括號來強制你的格式。 – DavidO

+0

DavidO,對不起。我不知道什麼是代碼括號。我試過但失敗了。我的數據中有兩列(字段)。 –

+0

非常感謝您對編輯帖子DavidO的幫助。 –

回答

2

這不是真的任何語言真正擅長的東西,事實上你要求的是一個相當具有挑戰性的編程任務,尤其是對新手來說。

儘管如此,這裏是一個awk腳本,您:

BEGIN { 
    window = 3000; 
} 

function push(line, sum, n) { 
    n = length(lines); 
    lines[n] = line; 
    sums[n] = sum; 
} 

function pop( n, i) { 
    n = length(lines); 

    if (n > 1) { 
     for(i = 0; i < n - 1; i++) { 
      lines[i] = lines[i + 1]; 
      sums[i] = sums[i + 1]; 
     } 
    } 
    if (n > 0) { 
     delete lines[n - 1]; 
     delete sums[n - 1]; 
    } 
} 

{ 
    cur_line = $1; 
    value = $2; 
    n = length(lines); 
    pops = 0; 
    for (i = 0; i < n; i++) { 
     if (lines[i] + window < cur_line) { 
      print "Sum for " lines[i] " = " sums[i]; 
      pops++; 
     } 
    } 
    for (i = 0; i < pops; i++) { 
     pop(); 
    } 
    push(cur_line, 0); 
    n = length(lines); 
    for (i = 0; i < n; i++) { 
     sums[i] = sums[i] + value; 
    } 
} 

END { 
    n = length(lines); 
    for (i = 0; i < n; i++) { 
     if (lines[i] < cur_line + window) { 
      print "Sum for " lines[i] " = " sums[i]; 
     } 
    } 
} 

這裏對您的樣本數據的運行:

Sum for 1000 = 31 
Sum for 2000 = 22 
Sum for 3000 = 12 
Sum for 5000 = 3 
Sum for 9000 = 95 
Sum for 10000 = 90 
Sum for 30000 = 52 
Sum for 31000 = 32 
Sum for 39000 = 61 
Sum for 40000 = 28 
+0

Thanks Will。我真的很喜歡awk腳本,當我試圖測試它時,我遇到了問題,我將腳本保存在一個「sliding_awk.awk」文件中,並使用一行命令:awk' {print $ 2,$ 3}'test | awk -f sliding_awk.awk。返回的錯誤是: awk:sliding_awk.awk:7:(FILENAME = - FNR = 1)致命:嘗試使用標量'行'作爲數組 –

+0

我會檢查你的代碼版本。它所說的是,不知何故,變量「行」被分配了一個正常值(一個字符串或一個數字),而不是一個數組。如果存在將「行」與「行」變量混淆的錯字,則很容易發生這種情況。 –

3

我不是很好的awk的,但這裏是我在Perl黑客在一起,如果你是在UNIX系統上也應該運行。假設你將它保存爲一個文件名爲window.pl:

#!/usr/bin/perl -w 
use strict; 

# Usage: window.pl < [filepath or text stream] 
# Example: window.pl < window.txt 

my $window = 3000; 
my @lines = <STDIN>; 
my $i = 0; 
my $last_line = $#lines; 

# Start reading each line 
while ($i<= $last_line) 
{ 
    my $current_line = $lines[$i]; 
    my ($col1, $col2) = ($current_line =~ /(\d+)\s+(\d+)/); 
    my $ubound = $col1 + $window; 
    my @sums = $col2; 
    my $lookahead = $i + 1; 

    # Start looking at subsequent lines within the window 
    while ($lookahead <= $last_line) 
    { 
     my $next_line = $lines[$lookahead]; 
     my ($c1, $c2) = ($next_line =~ /(\d+)\s+(\d+)/); 
     if ($c1 <= $ubound) 
     { 
      push @sums, $c2; 
      ++$lookahead; 
     } 
     else 
     { 
      last; 
     } 
    } 

    my $output; 
    if ($#sums > 0) 
    { 
     my $sum = join "+", @sums; 
     $output = "$col1 $sum\n"; 
    } 
    else 
    { 
     $output = "$col1 $col2\n"; 
    } 
    print $output; 
    ++$i; 
} 

輸出:

1000 12+10+9 
2000 10+9+3 
3000 9+3 
5000 3 
9000 5+90 
10000 90 
30000 20+32 
31000 32 
39000 33+28 
40000 28 

這隻能如果輸入文件足夠小,以讀入內存,但也許這將幫助你反正。

祝你好運!

+0

謝謝丹,我只是想學習perl,一個有吸引力的工具 –

2

這裏有一個解決方案的一個更爲簡潔的版本:

#!/usr/bin/perl 
use strict; 
use warnings; 

use constant WIN_SIZE => 3000; 

my @pending; 

while (<>) { 
    my ($pos, $val) = split; 

    # Store line info, sum, and when to stop summing 
    push @pending, { pos => $pos, 
        val => $val, 
        limit => $pos + WIN_SIZE, 
        sum => 0 }; 

    show($_) for grep { $_->{limit} < $pos } @pending; # Show items beyond window 

    @pending =  grep { $_->{limit} >= $pos } @pending; # Keep items still in window 

    $_->{sum} += $val for @pending;      # And continue their sums 
} 

# and don't forget those items left within the window when the data ran out 
show($_) for @pending; 

sub show { 
    my $pending = shift; 
    print join("\t", $pending->{pos}, $pending->{val}, $pending->{sum}), "\n"; 
} 

只需將其放在腳本中,並將數據文件放在同一行上,例如:

$ perl script.pl mydata 
1000 12 31 
2000 10 22 
3000 9 12 
5000 3 3 
9000 5 95 
10000 90 90 
30000 20 52 
31000 32 32 
39000 33 61 
40000 28 28 
+0

謝謝馬克。你給了我一個很好的Perl指南。 –

3

這裏是一個Perl的解決方案:

use warnings; 
use strict; 

my (%data, @ids); 
while (<DATA>) { # read in the data 
    /^(\d+)\s+(\d+)$/ or die "bad input: $_"; 
    push @ids, $1; 
    $data{$1} = [$2] 
} 
for (0 .. $#ids) { # slide window over data 
    my ($i, $id) = ($_ + 1, $ids[$_]); 

    push @{$data{$id}}, $data{ $ids[$i++] }[0] 
     while $i < @ids and $ids[$i] <= $id + 3000; 
} 

$" = '+';                #" 
print "$_: @{$data{$_}}\n" for @ids; 

__DATA__ 
1000 12 
2000 10 
3000 9 
5000 3 
9000 5 
10000 90 
30000 20 
31000 32 
39000 33 
40000 28 

它打印:

 
1000: 12+10+9 
2000: 10+9+3 
3000: 9+3 
5000: 3 
9000: 5+90 
10000: 90 
30000: 20+32 
31000: 32 
39000: 33+28 
40000: 28 
+0

謝謝埃裏克,我親眼目睹了這麼好的perl解決方案。 –

相關問題