suggus has asked for the wisdom of the Perl Monks concerning the following question:

Monks,

I've been agonizing over this problem for a while...

I have this database and I need to calculate the average of the values in a particular column (that value is $temp1[38] in my array). The calculation of the average depends on one criteria;

for all the rows if $temp1[36] = "In Production";

then I would take those rows $temp1[38] values and calculate their average. Does that make sense?


Here's my code so far:

#!/usr/local/bin/perl5 print "Content-type: text/html\n\n"; open(HANDLE,"script_integration_records"); while (<HANDLE>) { @temp1 = split(/:/); $domain = unpack ("A4", $temp1[40]); if ($domain eq "ATM") { if ($temp1[36] eq "In Production") { print "$temp1[38]\n"; # This is my break point...I think here's +where I need help } } } close(HANDLE);


The output of this code works, but it just does prints out a list of values that is in the array I have...the values are correct, I just need to take those values and find their average. Here's the output:

34 114 123 2 5 48 71 67 71

Thanks so much for enlightening me!

Replies are listed 'Best First'.
Re: Calculating the average of a column in a flat-file database
by Masem (Monsignor) on Oct 26, 2001 at 02:03 UTC
    Instead of printing, you can do a summing calculation to determine the total, then average over those.
    my ( $total , $number ); if ($domain eq "ATM") { if ($temp1[36] eq "In Production") { $total += $temp1[38]; $number++; } } } my $average = ( $number ) ? $total/$number : 0 ; #catch in case of no +data

    -----------------------------------------------------
    Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain
    "I can see my house from here!"
    It's not what you know, but knowing how to find it if you don't know that's important

      Masem,

      But my main goal is to display the average of that calculation...BTW I'm new to perl.

      I have a hard time with loops and I just wanted the script to look through the DB and find the instances of "In Production". With the instances found, I would like to find the average of the values in my array $temp1[38] for the row that contained "In Production" and print out the average.
        But my main goal is to display the average of that calculation

        Well, that's not hard :) - actually more than trivial, you output values by printing them. So after Masem's code put either one of those

        # with string interpolation print "The average is $average.\n"; # or with string concat print "The average is " . $average . ".\n"; # or with multiple arguments to print print "The average is ", $average, ".\n"; # or using printf and rounding to 2 decimals printf "The average is %.2f.\n", $average;

        I suggest you try to get a good book on perl, e.g. Learning Perl (3rd edition) by our one and only merlyn. Also have a look onto our New Monks Info Page. There you will find lot's of sources of information on perl and tips to get started.

        -- Hofmator

Re: Calculating the average of a column in a flat-file database
by gryphon (Abbot) on Oct 26, 2001 at 02:55 UTC

    Greetings suggus,

    Try this...

    open(HANDLE,"script_integration_records"); while (<HANDLE>) { @temp1 = split(/:/); $domain = unpack ("A4", $temp1[40]); if (($domain eq "ATM") && ($temp1[36] eq "In Production")) { $sum += $temp1[38]; $count++; } } close(HANDLE); $average = $sum / $count; print $average, "\n";

    -gryphon
    code('Perl') || die;

    UPDATE:

    Well, here's a smaller version of the above. I'm sure I could use map to get this in a line or two less, but I'm still having difficulties really getting my mind around complicated multi-calls of map. (Anyone out there know a tutorial on map?)

    open(HANDLE, 'script_integration_records'); foreach (grep /^(?:[^:]*:){36}In Production:/, <HANDLE>) { my @line = split(/:/); push @numbers, $line[38] if (unpack("A4", $line[40]) eq "ATM"); } close(HANDLE); print eval(join('+',@numbers))/($#numbers+1), "\n";

    Theoretically, you could run multiple greps against eachother to get just a single array. Then just average the array. Also, I have a general "ungood" feeling about using eval anywhere for any reason. Perhaps it's just a personal problem, but I have this fear of stuff being executed without my pre-knowledge of its contents.

    -gryphon
    code('Perl') || die;

      Let me add a few comments and suggestions:

      • I would declare all variables with my, otherwise you might run into problems later - apart from the fact that it won't run under use strict.
      • You were looking for a map solution, here you go:
        #!/usr/bin/perl use strict; use warnings; # always check if open fails open(DB, '<test.data') or die "Couldn't open file: $!"; my @numbers = map { my ($prod, $num, $dom) = (split /:/)[36,38,40]; (unpack("A4",$dom) eq 'ATM' && $prod eq 'In Production')?$num:() } <DB>; close DB; my $sum; $sum += $_ foreach (@numbers); printf "The average is %.2f.", @numbers ? ($sum/@numbers) : 0;
        I'm not saying that this is a better solution, I'd go - for the sake of readability/maintainability - for something straight forward like your first version.
      • Instead of the eval you can use a loop like I did or have a look at List::Utils.

      -- Hofmator

      THANKS gryphon! It works! You rock!