2012-06-28 4 views
1

ниже программа выводит следующие данные:Как найти максимальное значение для каждого 5-минутного интервала?

Wed,Jun,13,10:37:34,2012,759,41,0,30,10,0,0,1 
Wed,Jun,13,10:38:34,2012,767,33,0,25,6,0,0,2 
Wed,Jun,13,10:39:34,2012,758,42,0,32,10,0,0,0 
Wed,Jun,13,10:40:35,2012,758,42,0,29,11,0,0,2 
Wed,Jun,13,10:41:35,2012,761,39,0,34,5,0,0,0 
Wed,Jun,13,10:42:35,2012,769,31,0,22,6,0,0,3 
Wed,Jun,13,10:43:35,2012,754,46,0,29,17,0,0,0 

мне нужно выводить максимальное значение (. Ех 769) для каждого 5-минутного интервала. В идеале это будет 10:00:00 - 10:05:00 и т. Д. Время - военное время (24 часа). Каков наилучший способ сделать это? Обратите внимание, что я новичок в Perl. Ниже мой код:

#!/usr/bin/perl 

# This program displays the max thread count at 5 minute intervals and writes the lines to a CSV file. 

use strict; 
use warnings; 
use diagnostics; 

# Initialize functions 
my @data; 
my $line; 
my @L1; 
#my $outFivemin = "log_5min.csv"; 
#open (FiveMin, ">> $outFivemin"); 

# Open the error_log 
open(FH, "error_log"); 
@data = <FH>; 

# Filter the results to MPMStats only 
sub findLines { 
    my @return =(); 
    foreach $line (@data) { 
     if (($line =~ /notice/) && ($line =~ /rdy/)) { 
       $line =~ s/ /,/g; 
       my @L1 = split(/|notice|\[|,mpmstats:,|\t|rdy,|bsy,|rd,|wr,|ka,|log,|dns,|cls,/, $line); 
       $line =~ s/|notice|\[|,mpmstats:,|\t|rdy,|bsy,|rd,|wr,|ka,|log,|dns,|cls,//g;     
       push @return, join("", @L1); 
     } 
    } 
    return @return; 
} 

# Initializers for my data 
my($dayOfWeek1,$month1,$dayOfMonth1,$time,$year1,$rdy,$bsy,$rd,$wr,$ka,$log,$dns); 
my($cls); 

# Create a 2D array 
my @L2 = &findLines; 
foreach my $line (@L2){ 
    ($dayOfWeek1, $month1, $dayOfMonth1, $time, $year1, $rdy, $bsy, $rd, $wr, $ka, $log, $dns, $cls) = split(/,/, $line); 
    print "$dayOfWeek1,$month1,$dayOfMonth1,$time,$year1,$rdy,$bsy,$rd,$wr,$ka,$log,$dns,$cls"; 
} 
+0

Не удалось настроить mpmstats для регистрации каждые 5 минут, а не 1? это может облегчить вам работу – Arcadien

+0

Это не сработает, так как мне нужно найти это максимальное значение каждые 5 минут, а не только значение на этом 5-минутном интервале. – rupes0610

+0

'Инициализаторы для моих данных' - нет, не делайте этого. Ключевое слово 'my' имеет специальную функцию для создания * лексической * переменной, которая видна только в текущем блоке. Вы должны использовать это как можно чаще: создавать только переменные в блоке, которые будут использоваться. – TLP

ответ

-1

Это работает (не проверял), и она начинается с цикла сразу после my @L2 = &findLines.

my %interval; 
my %month; 
@month{qw/ jan feb mar apr may jun jul aug sep oct nov dec /} = '01' .. '12'; 

# Create a 2D array 
my @L2 = &findLines; 
foreach my $line (@L2){ 
    #($dayOfWeek1, $month1, $dayOfMonth1, $time, $year1, $rdy, $bsy, $rd, $wr, $ka, $log, $dns, $cls) = split(/,/, $line); 
    #print "$dayOfWeek1,$month1,$dayOfMonth1,$time,$year1,$rdy,$bsy,$rd,$wr,$ka,$log,$dns,$cls"; 
    my ($dow, $mon, $day, $hr, $min, $sec, $yr, $amt) = split /[:,]/, $line, 9; 
    my $key = sprintf "%4d-%02d-%02d %02d:%02d", 
       $yr, $month{lc $mon}, $day, $hr, int($min/5) * 5; 

    if (exists $interval{$key}) { 
     if ($interval{$key}{amt} < $amt) { 
      $interval{$key}{amt} = $amt; 
      $interval{$key}{data} = [split ",", $line]; 
     } 
    } 
    else { # first time in this 5 minute interval 
     $interval{$key}{amt} = $amt; 
     $interval{$key}{data} = [split ",", $line]; 
    } 
} 

my $csv = Text::CSV_XS->new ({ binary => 1 }) or 
    die "Cannot use CSV: ".Text::CSV_XS->error_diag(); 

$csv->eol ("\r\n"); 
open my $fh, ">", 'junk.csv' or die $!; 

for my $time (sort keys %interval) { 
    $csv->print($fh, $interval{$time}{data}); 
} 

close $fh or die $!; 

Надеюсь, это приблизит вас к хорошему решению вашей проблемы.
Обновление: добавлено первое поле для разделения и изменение с 8 до 9 порций.

+0

Работал даже лучше, чем последний! Только он не выводит данные столбца cls (последний столбец). Кроме того, как я могу потянуть за последний 5-минутный интервал? Мне нужно уметь помещать XML-форматирование вокруг него. Большое вам спасибо за вашу помощь !!! – rupes0610

+0

Вау, не знаю почему. Может быть, его строка заканчивается, '$ csv-> eol (" \ r \ n ");'. Это будет для компьютера Windows, который я считаю. Если на unix/linux, возможно, эта строка должна быть: '$ csv-> eol ("\ n");'. Строка '$ interval {$ key} {data} = [split", ", $ line];' должна включать последний столбец, 'cls', когда он разбивает строку. Извините, я не могу больше помочь. –

+0

Вы случайно знаете, как тянуть только последние 5-минутный интервал (ряд)? – rupes0610

3

Что-то вдоль этих линий следует сделать трюк ...

#!/usr/bin/perl 

use strict; 
use warnings; 
use 5.010; 

# Somewhere to store the data 
my %data; 

# Process the input a line at a time 
while (<DATA>) { 
    # Split the input line on commas and colons. 
    # Assign the bits we need to variables. 
    my ($mon,$day,$hr,$min,$sec,$yr,$val) = (split /[,:]/)[1 .. 7]; 

    # Normalise the minute value to five-minute increments 
    # i.e 37 becomes 35, 42 becomes 40 
    $min = int($min/5) * 5; 

    # Create push the value onto an array that is stored in %data using 
    # a key generated from the timestamp. 
    # Note that we use the 5-min normalised value of the minute so that 
    # all values from the same five minute period end up in the same array. 
    push @{$data{"$yr-$mon-$day $hr:$min"}}, $val; 
} 

# For each key in the array (i.e. each five minute increment... 
foreach (sort keys %data) { 
    # ... sort the array numerically and grab the last element 
    # (which will be the largest) 
    my $max = (sort { $a <=> $b } @{$data{$_}})[-1]; 
    # Say something useful 
    say "$_ - $max"; 
} 

__DATA__ 
Wed,Jun,13,10:37:34,2012,759,41,0,30,10,0,0,1 
Wed,Jun,13,10:38:34,2012,767,33,0,25,6,0,0,2 
Wed,Jun,13,10:39:34,2012,758,42,0,32,10,0,0,0 
Wed,Jun,13,10:40:35,2012,758,42,0,29,11,0,0,2 
Wed,Jun,13,10:41:35,2012,761,39,0,34,5,0,0,0 
Wed,Jun,13,10:42:35,2012,769,31,0,22,6,0,0,3 
Wed,Jun,13,10:43:35,2012,754,46,0,29,17,0,0,0 
+1

Это покажет результаты в очень неестественном порядке, поставив «2012-Дек-25 00: 00» перед «2012-июл-04 00: 00». – Borodin

+0

Да, я знаю. Исправление, которое остается для упражнения читателем :-) –

+0

это очень хромает. Правильное решение включает отказ от вашего кода и начало снова. – Borodin

4

Я предлагаю вам манипулировать даты/время в каждой записи на поставку пяти минут ключа, и поддерживать максимум для каждого ключа.

Например, если запись начинается Wed,Jun,13,10:37:34,2012, тогда соответствующий ключ Jun 13 10:35 2012.

Обычно это будет хэш, но, поскольку вероятность того, что выход нужен в хронологическом порядке, требует дополнительной работы и модулей для создания сортируемой строки даты/времени, в приведенной ниже программе используется массив пар.

Программа работает с использованием подзадачи регулярного выражения s/// в поле времени (четвертое), которое заменяет минуты и секунды первой двузначной минутой, предшествующей времени: секунды игнорируются, а минуты округляются до кратного 5.

Пара [$range, $value] вводится в массив @maxima, если массив пуст или мы находимся в другом $range. В противном случае элемент $value последней пары обновляется, если мы нашли новый максимум.

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

use strict; 
use warnings; 

@ARGV = ('error_log') unless @ARGV; 

my @maxima; 

while (<>) { 

    my @fields = /([^,\s]+)/g; 
    next unless @fields; 
    $fields[3] =~ s|(\d+):\d\d$|5*int($1/5)|e; 

    my $range = join ' ', @fields[1..4]; 
    my $value = $fields[5]; 

    if (@maxima == 0 or $range ne $maxima[-1][0]) { 
    push @maxima, [$range, $value]; 
    } 
    else { 
    $maxima[-1][1] = $value if $maxima[-1][1] < $value; 
    } 
} 

for (@maxima) { 
    printf "Maximum for five minutes starting %s is %d\n", @$_; 
} 

выход

Maximum for five minutes starting Jun 13 10:35 2012 is 767 
Maximum for five minutes starting Jun 13 10:40 2012 is 769 

Update

Теперь, когда я понимаю, что вы хотите, чтобы вся запись, содержащая максимальное значение поля 6 для каждого пятиминутки Я написал этот пересмотренный код.

Он также работает с содержимым вашего массива @L2 вместо чтения из файла.

Я уверен, что это было бы намного лучше кодировано для чтения из файла в цикле while и генерировать вывод непосредственно оттуда, но если вы не покажете нам некоторые данные файла журнала, я не могу предложить лучшую альтернативу этому.

Эта программа продолжается с того места, где вы заполняете @L2 в своей собственной программе.

my @L2 = findLines(); 

my @maxima; 

for my $record (@L2) { 

    my @fields = $record =~ /([^,\s]+)/g; 
    next unless @fields; 

    my @range = @fields[1..4]; 
    $range[2] =~ s|(\d+):\d\d$|5*int($1/5)|e; 
    my $range = join ' ', @range; 
    my $value = $fields[5]; 

    if (@maxima == 0 or $range ne $maxima[-1][0]) { 
    push @maxima, [$range, $value, $record]; 
    } 
    else { 
    @{$maxima[-1]}[1,2] = ($value, $record) if $maxima[-1][1] < $value; 
    } 
} 

print $_->[2] for @maxima; 

выход

Wed,Jun,13,10:38:34,2012,767,33,0,25,6,0,0,2 
Wed,Jun,13,10:42:35,2012,769,31,0,22,6,0,0,3 
+0

Это сработало отлично. Огромное спасибо!! – rupes0610

+0

@ user1488984: Я удивлен, учитывая мой комментарий к вашему образцу кода. Но вы более чем рады. – Borodin

+0

есть ли способ перечислить оставшиеся значения для этой строки? ex.Wed, Jun, 13,10: 38: 34,2012,767,33,0,25,6,0,0,2 – rupes0610

-1

Упс, я ошибочно думал, что ваш вывод CSV был файл данных разбираемый.

Игнорируйте ответ ниже.

Это решение, которое печатает оригинальную линию, разделенную запятой. Максимальное значение и время также доступны для печати. Но вместо этого я создал файл с разделителями-запятыми. :-)

#!/usr/bin/perl 
use strict; 
use warnings; 
use Text::CSV_XS; 

my %interval; 
my $csv = Text::CSV_XS->new ({ binary => 1 }) or 
    die "Cannot use CSV: ".Text::CSV_XS->error_diag(); 

open my $fh, "<", "o33.txt" or die "o33.txt: $!"; 
while (my $row = $csv->getline ($fh)) { 
    my ($time, $amt) = @$row[3,5]; 
    my ($hr, $min) = split /:/, $time; 
    my $key = sprintf "%02d:%02d", $hr, int($min/5) * 5; 

    if (exists $interval{$key}) { 
     if ($interval{$key}{amt} < $amt) { 
      $interval{$key}{amt} = $amt; 
      $interval{$key}{data} = $row; 
     } 
    } 
    else { # first time in this 5 minute interval 
     $interval{$key}{amt} = $amt; 
     $interval{$key}{data} = $row; 
    } 
} 
$csv->eof or $csv->error_diag(); 
close $fh or die $!;; 


$csv->eol ("\r\n"); 
open $fh, ">", 'junk.csv' or die $!; 

for my $time (sort keys %interval) { 
    $csv->print($fh, $interval{$time}{data}); 
} 

close $fh or die $!; 

Выход на 'junk.csv' является: (?)

Wed,Jun,13,10:38:34,2012,767,33,0,25,6,0,0,2 
Wed,Jun,13,10:42:35,2012,769,31,0,22,6,0,0,3 
+0

Спасибо!Но я получаю следующие ошибки: Использование неинициализированного значение $ времени в разделенном Использование неинициализированного значение $ мин в разделении Использование неинициализированного значение $ в час Sprintf Использование неинициализированным значение $ АМТ в числовом ЛТ (<) Использование неинициализированного значения в числовом lt (<) – rupes0610

+0

@ user1488984 Извините, он работает в вашем примере файла здесь нормально. Я отправляю еще одно решение, которое напрямую связано с вашим кодом, создающим исходный вывод. Единственное, о чем я говорю, это не способ проверить это, потому что у вас есть данные, из которых вы его вытащили. –

Смежные вопросы