【问题标题】:How can I sum data over five minute time intervals in Perl?如何在 Perl 中对五分钟时间间隔内的数据求和?
【发布时间】:2009-10-29 08:46:54
【问题描述】:

我有一个格式如下的文件。

DATE Time, v1,v2,v3
05:33:25,n1,n2,n3
05:34:25,n4,n5,n5
05:35:24,n6,n7,n8
and so on upto 05:42:25.

我想每隔 5 分钟计算一次值 v1、v2 和 v3。我已经编写了以下示例代码。

while (<STDIN>) {
    my ($dateTime, $v1, $v2, $v3) = split /,/, $_;
    my ($date, $time) = split / /, $dateTime;
}

我可以读取所有值,但需要帮助以每 5 分钟间隔对所有值求和。谁能建议我每 5 分钟添加一次时间和值的代码。

需要的输出

05:33 v1(sum 05:33 to 05:37) v2(sum 05:33 to 05:33) v3(sum 05:33 to 05:33)
05:38 v1(sum 05:38 to 05:42) v2(sum 05:38 to 05:42) v3(sum 05:38 to 05:42)
and so on..

【问题讨论】:

  • 我完全不明白你想要做什么或为什么。
  • 使用适当的统计数据包来执行此操作不是更好吗?我会去问这里的 R 人。
  • 我认为 v2 和 v3 所需输出的第一行也应为 05::33 到 05::37?

标签: perl datetime


【解决方案1】:

代码是下面 Sinan Ünür 的 previous 答案的变体,除了:

(1) 函数 timelocal 将允许您读取 Day,Month,Year - 因此您可以总结任何五分钟的间隔。

(2) 应处理最终时间间隔

#!/usr/bin/perl -w
use strict;
use warnings;
use Time::Local;
use POSIX qw(strftime);

my ( $start_time, $end_time, $current_time );
my ( $totV1,      $totV2,    $totV3 );          #totals in time bands

while (<DATA>) {
    my ( $hour, $min, $sec, $v1, $v2, $v3 ) =
      ( $_ =~ /(\d+)\:(\d+)\:(\d+)\,(\d+),(\d+),(\d+)/ );

    #convert time to epoch seconds
    $current_time =
      timelocal( $sec, $min, $hour, (localtime)[ 3, 4, 5 ] );    #sec,min,hr

    if ( !$end_time ) {
        $start_time = $current_time;
        $end_time   = $start_time + 5 * 60;    #plus 5 min
    }
    if ( $current_time <= $end_time ) {
        $totV1 += $v1;
        $totV2 += $v2;
        $totV3 += $v3;
    }
    else {
        print strftime( "%H:%M:%S", localtime($start_time) ),
          " $totV1,$totV2,$totV3\n";
        $start_time = $current_time;
        $end_time   = $start_time + 5 * 60;    #plus 5 min
        ( $totV1, $totV2, $totV3 ) = ( $v1, $v2, $v3 );
    }
}

#Print results of final loop (if required)
if ( $current_time <= $end_time ) {
    print strftime( "%H:%M:%S", localtime($start_time) ),
      " $totV1,$totV2,$totV3\n";
}

__DATA__
05:33:25,29,74,96
05:34:25,41,69,95
05:35:25,24,38,55
05:36:25,96,63,70
05:37:25,84,65,74
05:38:25,78,58,93
05:39:25,51,38,19
05:40:25,86,40,64
05:41:25,80,68,65
05:42:25,4,93,81

输出:

05:33:25 352,367,483
05:39:25 221,239,229

【讨论】:

    【解决方案2】:

    显然,由于缺乏样本数据,没有进行太多测试。要解析 CSV,请使用 Text::CSV_XSText::xSV 而不是下面的天真 split

    注意:

    • 如果输入数据有间隙,此代码确保输出具有所有连续的五分钟块。

    • 如果存在多天的时间戳,您将遇到问题。事实上,如果时间戳不是 24 小时格式,即使数据来自一天,也会有问题。

    有了这些警告,它仍然应该为您提供一个起点。

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    my $split_re = qr/ ?, ?/;
    my @header = split $split_re, scalar <DATA>;
    my @data;
    
    my $time_block = 0;
    
    while ( my $data = <DATA> ) {
        last unless $data =~ /\S/;
        chomp $data;
        my ($ts, @vals) = split $split_re, $data;
    
        my ($hr, $min, $sec) = split /:/, $ts;
        my $secs = 3600*$hr + 60*$min + $sec;
    
        if ( $secs > $time_block + 300 ) {
            $time_block = $secs;
            push @data, [ $time_block ];
        }
    
        for my $i (1 .. @vals) {
            $data[-1]->[$i] += $vals[$i - 1];
        }
    }
    
    print join(', ', @header);
    for my $row ( @data ) {
        my $ts = shift @$row;
        print join(', ',
            sprintf('%02d:%02d', (localtime($ts))[2,1])
            , @$row
        ), "\n";
    }
    
    
    __DATA__
    DATE Time, v1,v2,v3
    05:33:25,1,3,5
    05:34:25,2,4,6
    05:35:24,7,8,9
    05:55:24,7,8,9
    05:57:24,7,8,9
    

    输出:

    日期时间,v1,v2,v3 05:33, 10, 15, 20 05:55, 14, 16, 18

    【讨论】:

      【解决方案3】:

      这是 Perl 解决的好问题。最困难的部分是从 datetime 字段中获取值并确定它属于哪个 5 分钟存储桶。其余的只是哈希。

      my (%v1,%v2,%v3);
      while (<STDIN>) {
          my ($datetime,$v1,$v2,$v3) = split /,/, $_;
          my ($date,$time) = split / /, $datetime;
          my $bucket = &get_bucket_for($time);
          $v1{$bucket} += $v1;
          $v2{$bucket} += $v2;
          $v3{$bucket} += $v3;
      }
      foreach my $bucket (sort keys %v1) {
          print "$bucket $v1{$bucket} $v2{$bucket} $v3{$bucket}\n";
      }
      

      这是实现&amp;get_bucket_for的一种方法:

      my $first_hhmm;
      sub get_bucket_for {
          my ($time) = @_;
          my ($hh,$mm) = split /:/, $time;  # looks like seconds are not important
      
          # buckets are five minutes apart, but not necessarily at multiples of 5 min
          # (i.e., buckets could go 05:33,05:38,... instead of 05:30,05:35,...)
          # Use the value from the first time this function is called to decide
          # what the starting point of the buckets is.
          if (!defined $first_hhmm) {
              $first_hhmm = $hh * 60 + $mm;
          }
      
          my $bucket_index = int(($hh * 60 + $mm - $first_hhmm) / 5);
          my $bucket_start = $first_hhmm + 5 * $bucket_index;
          return sprintf "%02d:%02d", $bucket_start / 60, $bucket_start % 60;
      
      }
      

      【讨论】:

        【解决方案4】:

        我不确定您为什么要使用从第一次开始的时间,而不是大约 5 分钟的间隔(00 - 05、05 - 10 等),但这是一种快速而肮脏的方式方式:

        my %output;
        my $last_min = -10; # -10 + 5 is less than any positive int.
        while (<STDIN>) {
            my ($dt, $v1, $v2, $v3) = split(/,/, $_);
            my ($h, $m, $s) = split(/:/, $dt);
            my $ts = $m + ($h * 60);
            if (($last_min + 5) < $ts) {
                $last_min = $ts;
            }
            $output{$last_min}{1} += $v1;
            $output{$last_min}{2} += $v2;
            $output{$last_min}{3} += $v3;
        }
        foreach my $ts (sort {$a <=> $b} keys %output) {
            my $hour = int($ts / 60);
            my $minute = $ts % 60;
            printf("%01d:%02d v1(%i) v2(%i) v3(%i)\n", (
                    $hour,
                    $minute,
                    $output{$ts}{1},
                    $output{$ts}{2},
                    $output{$ts}{3},
                ));
        }
        

        不知道你为什么要这样做,但你可以在过程 Perl 中进行举例。如果您需要更多关于printf 格式的信息,请go here

        【讨论】:

          猜你喜欢
          • 2021-12-07
          • 1970-01-01
          • 2019-05-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-07-05
          • 2016-08-18
          • 1970-01-01
          相关资源
          最近更新 更多