Calculate average depending on number of instances on each line - Bash - AWK

Viewed 102

How can you work out the average on each line when each row has a variation on the number of alphanumeric instances including a final total average?

ie. pie.txt:

0.8pie 0.7pie 0.6pie            
34.2pie 34.1pie 35.8pie 
42pie 43pie 43pie
44pie 41pie 38pie
0.8pie
34.2pie 34.1pie
42pie 43pie 43.0pie
44.1pie 41pie 38.7pie 58.2pie

Desired Output:

Instances of Pie:               Avg:
0.8pie 0.7pie 0.6pie            Avg pie:  0.7pie
34.2pie 34.1pie 35.8pie         Avg pie:  34.7pie
42pie 43pie 43pie               Avg pie:  42.7pie
44pie 41pie 38pie               Avg pie:  41pie
0.8pie                          Avg pie:  0.8pie
34.2pie 34.1pie                 Avg pie:  34.15pie
42pie 43pie 43.0pie             Avg pie:  42.7pie
44.1pie 41pie 38.7pie 58.2pie   Avg pie:  45.5pie

Total Average:                            30.28pie

If there was always a set amount on each line like 2 or lets say 3 pies then yeah I can easily utilise awk print ($1+$2+$3)/3 etc. But I'm struggling when there is a variance on each line.

I would imagine the use of conditional statements to determine the number on each line. ie. If row one has 3 then divide by 3, if row two has 4 then divide by 4 etc.

Can anyone kindly assist?

2 Answers

You may use this awk:

awk 'function prt(s1, s2) {printf "%-40s %s\n", s1, s2} NR==1 {prt("Instances of Pie:", "Avg:")} NF {s=0; for (i=1; i<=NF; ++i) s+=$i; avg=s/NF; ++rec; sum+=avg; prt($0, "Avg pie:  " avg "pie")} !NF; END {prt("\nTotal Average:", sum/rec "pie")}' file

Instances of Pie:                        Avg:
0.8pie 0.7pie 0.6pie                     Avg pie:  0.7pie
34.2pie 34.1pie 35.8pie                  Avg pie:  34.7pie
42pie 43pie 43pie                        Avg pie:  42.6667pie
44pie 41pie 38pie                        Avg pie:  41pie
0.8pie                                   Avg pie:  0.8pie
34.2pie 34.1pie                          Avg pie:  34.15pie
42pie 43pie 43.0pie                      Avg pie:  42.6667pie
44.1pie 41pie 38.7pie 58.2pie            Avg pie:  45.5pie

Total Average:                           30.2729pie

A more readable form:

awk '
function prt(s1, s2) {
   printf "%-40s %s\n", s1, s2
}
NR == 1 {
   prt("Instances of Pie:", "Avg:")
}
NF {
   s = 0
   for (i=1; i<=NF; ++i)
      s += $i
   avg = s/NF
   prt($0, "Avg pie:  " avg "pie")
   ++rec
   sum += avg
}
!NF
END {
   prt("\nTotal Average:", sum/rec "pie")
}' file

Perl, but it really offers no advantage over anubhava's answer

perl -MList::Util=sum,max -lane '
      sub avg {sum(@_) / @_}
      push @lines, $_;
      push @avg, avg(@F);
  } END {
      $maxl = max map {length} @lines;
      printf "%-*s Avg:\n", $maxl, "Instances of Pie:";
      printf "%-*s Avg pie: %.1fpie\n", $maxl, $lines[$_] , $avg[$_]
          for 0..$#lines;
      printf "\n%-*s          %.2fpie\n", $maxl, "Total Average:", avg(@avg);
' pie.txt
Related