суммировать числовой столбец со скользящим окном, определяемым значениями в другом столбце - PullRequest
0 голосов
/ 09 июля 2011

Сейчас я сталкиваюсь с проблемой суммирования числового столбца со скользящим окном, заданным значениями в другом столбце.

(1) Мои данные разделены табуляцией, с двумя числовыми столбцами:

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

(2) Я хочу суммировать 2-й столбец с окном, определенным 1-м столбцом, размер окна (1-й столбец + 3000).Это означает, что мне нужно добавить 3-й столбец (3-й столбец = сумма (все значения 2-х столбцов в строках 1-го столбца в 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, но мне это не удалось.

Я не знаю, как управлять окном для 1-го столбца

awk '(i = 1; i <= NR; i ++) {sum[i] + = $ 2} END {print $ 1, $ 2, sum} 'mydata </p>

Может кто-нибудь дать мне какой-нибудь совет / указания по проблеме?Заранее спасибо.

Лучший,

Ответы [ 4 ]

3 голосов
/ 09 июля 2011

Вот решение 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
3 голосов
/ 09 июля 2011

Я не очень хорош в 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

Это работает только в том случае, если входной файл достаточно мал для чтения в память, но, возможно, это поможет вам в любом случае.

Удачи!

2 голосов
/ 09 июля 2011

Вот несколько более компактная версия решения:

#!/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
2 голосов
/ 09 июля 2011

Это не то, что действительно любой из языков действительно адепт, и на самом деле то, что вы спрашиваете, является довольно сложной задачей программирования, особенно для начинающего.

Тем не менее, вот сценарий 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...