Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Re^10: Computing results through Arrays

by yasser8@gmail.com (Novice)
on Jun 24, 2015 at 09:49 UTC ( [id://1131778]=note: print w/replies, xml ) Need Help??


in reply to Re^9: Computing results through Arrays
in thread Computing results through Arrays

Sorry for visiting back on this Aaron Sir.

Actually I need to calculate AVERAGE and MAXIMUM within each group the same way we did for SUM earlier. I was able to draft AVERAGE logic and it works fine, but I am not able to derive MAXIMUM. Could you please help me on this and guide me please if my approach for AVERAGE can be written in still more efficient way.

AVERAGE logic :- SUM($speed)/ DISTINCT($servers) * 60 for Hour

MAXIMUM logic :- MAX($speed) across all the $servers within each Hour

#!/usr/bin/env perl use strict; use warnings; my %h; my %m; my %db; my %sr; sub round { $_[0] > 0 ? int($_[0] + .5) : -int(-$_[0] + .5) } sub fnd_max (\%) { my $hash = shift; my ($key, @keys) = keys %$hash; my ($big, @vals) = values %$hash; for (0 .. $#keys) { if ($vals[$_] > $big) { $big = $vals[$_]; $key = $keys[$_]; } } $big } sub set_column_widths { my $h = shift; my $databases = shift; my $names = shift; for my $key (keys %$h){ for my $db (@$names){ my $l = length $h->{$key}{$db}; $databases->{$db} = $databases->{$db} > $l ? $databases->{ +$db} : $l; } } for my $db (@$names){ # check the width of the database names the +mselves too my $l = length $db; $databases->{$db} = $databases->{$db} > $l ? $databases->{$db} + : $l; } } while(<DATA>){ next unless /\w/; my($server,$datetime,$database,$speed) = (split)[0,1,2,3]; my $ddhhmm = substr $datetime,0,16; my $ddhh = substr $datetime,0,13; $h{$ddhh }{$database} += $speed; $m{$ddhhmm}{$database} += $speed; $db{$database} = 1; $sr{$server } = 1; } my @db = sort keys %db; # sort and save database names as array since +we'll be looping through them many times my $count = keys %sr; # HOUR SECTION START - AVG for my $key (sort keys %h){ for (@db) { $h{$key}{$_} = round($h{$key}{$_} / ($count * 60))} ; } set_column_widths(\%h, \%db, \@db); print "Frequency Hour:\ncollectionTime"; printf " %$db{$_}s", $_ for (@db); print "\n"; for my $key (sort keys %h){ print "$key "; printf " %$db{$_}s", $h{$key}{$_} for (@db); print "\n"; } # HOUR SECTION END - AVG # MINUTE SECTION START - AVG for my $key (sort keys %m){ for (@db) { $m{$key}{$_} = round($m{$key}{$_} / ($count))} ; } set_column_widths(\%m, \%db, \@db); print "\nFrequency Minute:\n collectionTime"; printf " %$db{$_}s", $_ for (@db); print "\n"; for my $key (sort keys %m){ print $key; printf " %$db{$_}s", $m{$key}{$_} for (@db); print "\n"; } # MINUTE SECTION END - AVG # HOUR SECTION START - MAX set_column_widths(\%h, \%db, \@db); print "Frequency Hour:\ncollectionTime"; printf " %$db{$_}s", $_ for (@db); print "\n"; for my $key (sort keys %h){ print "$key "; printf " %$db{$_}s", max (values $h{$key}{$_}) for (@db); print "\n"; } # HOUR SECTION END - MAX # MINUTE SECTION START - MAX print fnd_max %m ; set_column_widths(\%m, \%db, \@db); print "\nFrequency Minute:\n collectionTime"; printf " %$db{$_}s", $_ for (@db); print "\n"; for my $key (sort keys %m){ print $key; printf " %$db{$_}s", max (values $m{$key}{$_}) for (@db); print "\n"; } # MINUTE SECTION END - MAX

Replies are listed 'Best First'.
Re^11: Computing results through Arrays
by aaron_baugher (Curate) on Jun 24, 2015 at 10:46 UTC

    robby_dobby already pointed out the actual mistake: you need to pass fnd_max() a reference to the hash, since that's what fnd_max() is expecting. See how I pass my hashes to set_column widths().

    Now beyond that: First, I'd use a more descriptive subroutine name, like "max_value_of_hash", and drop the prototype. Prototypes are advanced juju and shouldn't be used most of the time. Second, if you want to get the largest value from a hash, you don't need to access the keys at all. Here are some examples, starting with the simplest and wordiest:

    #!/usr/bin/env perl use 5.010; use strict; use warnings; # newbie but clean version sub max_value_of_hash { my $h = shift; my $max = 0; for my $v (values %$h){ if ($v > $max){ $max = $v; # keep setting $max to larger value } } return $max; } # more perlish and elegant version sub max_value_of_hash2 { my $h = shift; my $max = 0; $_ > $max ? $max = $_ : undef for values %$h; return $max; } # let a module do it sub max_value_of_hash3 { use List::Util qw(max); return max values %{$_[0]}; } my %hash = ( a => 1, b => 2, c => 5, d => 3 ); say max_value_of_hash( \%hash); say max_value_of_hash2(\%hash); say max_value_of_hash3(\%hash);

    Aaron B.
    Available for small or large Perl jobs and *nix system administration; see my home node.

      Sorry I was not clear with my requirement, also I made worst mistake in my code.

      My requirement is to print the Maximum value the same way Average value printing works. So the assignment of values to hash array keys should be max value instead of sum of the values in that group.

      while(<DATA>){ next unless /\w/; my($server,$datetime,$database,$speed) = (split)[0,1,2,3]; my $ddhhmm = substr $datetime,0,16; my $ddhh = substr $datetime,0,13; $h{$ddhh }{$database} += $speed; $m{$ddhhmm}{$database} += $speed; $db{$database} = 1; $sr{$server } = 1; }

      Is there a way to assign maximum value in this while loop shown above to $h{$ddhh }{$database} and $m{$ddhhmm}{$database} ??

      If this is possible then I can follow the same procedure to print Max values the same way we did for Average.

      One more doubt... All I did to find Average is to loop through the Hash Array one more time and assign the Average value to it as shown below

      for my $key (sort keys %h){ for (@db) { $h{$key}{$_} = round($h{$key}{$_} / ($count * 60))} ; }

      Can it be done efficiently without looping the Hash Array again ? I mean can this be done in while loop itself ?

        I may still not understand exactly what you're trying to do. But if you want to keep track of a max value for each time/database at the same time that you're keeping a total for the purpose of averaging, you could do something like this inside your while() loop:

        $h{$ddhh }{$database}{total} += $speed; $m{$ddhhmm}{$database}{total} += $speed; $h{$ddhh }{$database}{max} = max(($h{$ddmm}{$database}{max} || 0), + $speed); $m{$ddhhmm}{$database}{max} = max(($h{$ddmm}{$database}{max} || 0), + $speed);

        You can use the max() function from List::Util or write your own. Now later in the code where you used to access the total speed with $h{$timestamp}{$database}, you'll change that to access it as $h{$timestamp}{$database}{total}. And that makes room to keep track of the maximum value for each one in $h{$timestamp}{$database}{max}. Make sense?

        Aaron B.
        Available for small or large Perl jobs and *nix system administration; see my home node.

Re^11: Computing results through Arrays
by robby_dobby (Hermit) on Jun 24, 2015 at 10:02 UTC
    Hello yasser8@gmail.com,

    The problem here is in this line:

    print fnd_max %m

    You see, the fnd_max sub requires a hashref, while you're passing a full hash into it. Also, why are you using prototypes for your subroutine? Unless you're using v5.20, where subroutine signatures were introduced, don't use them. To correct your problem, change the %m to \%m - make it clear that it's a sub call, print fnd_max(\%m). Not really important, this is just my convention.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://1131778]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others surveying the Monastery: (4)
As of 2024-04-24 21:32 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found