File Coverage

blib/lib/BenchmarkAnything/Evaluations.pm
Criterion Covered Total %
statement 99 120 82.5
branch 18 50 36.0
condition 8 17 47.0
subroutine 8 8 100.0
pod 2 2 100.0
total 135 197 68.5


line stmt bran cond sub pod time code
1 1     1   473 use 5.010; # Perl 5.10+ needed for PDL
  1         4  
2 1     1   6 use strict;
  1         2  
  1         19  
3 1     1   4 use warnings;
  1         2  
  1         49  
4             package BenchmarkAnything::Evaluations;
5             # git description: v0.004-2-g53c7b4c
6              
7             our $AUTHORITY = 'cpan:SCHWIGON';
8             # ABSTRACT: Evaluation support for BenchmarkAnything data
9             $BenchmarkAnything::Evaluations::VERSION = '0.005';
10 1     1   786 use PDL::Core;
  1         39177  
  1         6  
11 1     1   745 use PDL::Stats;
  1         235  
  1         5  
12 1     1   2252131 use PDL::Ufunc;
  1         5  
  1         8  
13              
14              
15             sub multi_point_stats
16             {
17 12     12 1 24 my ($values) = @_;
18              
19 12         38 my $data = pdl(@$values);
20 12         750 my $avg = average($data); # average
21 12         115 my $stdv = stdv($data); # standard deviation
22 12         46 my $min = min($data); # min
23 12         544 my $max = max($data); # max
24 12         573 my $se = se($data); # standard error of the mean (::Stats::Basic)
25             return {
26 12   33     108 count => scalar(@$values),
27             avg => $avg,
28             stdv => $stdv,
29             min => $min,
30             max => $max,
31             se => $se,
32             ci_95_upper => $avg + 1.96 * $se,
33             ci_95_lower => $avg - 1.96 * $se,
34             badflag => ($avg->badflag || $stdv->badflag || $min->badflag || $max->badflag || $se->badflag),
35             };
36             }
37              
38              
39             sub transform_chartlines
40             {
41 1     1 1 679 my ($chartlines, $options) = @_;
42              
43 1         3 my $x_key = $options->{x_key};
44 1         2 my $x_key_short = $options->{x_key_short};
45 1         3 my $x_type = $options->{x_type};
46 1         2 my $y_key = $options->{y_key};
47 1         3 my $y_type = $options->{y_type};
48 1         2 my $aggregation = $options->{aggregation};
49 1         3 my $verbose = $options->{verbose};
50 1         3 my $scalars = $options->{scalars};
51 1         2 my $debug = $options->{debug};
52 1         3 my $dropnull = $options->{dropnull};
53 1         2 my $textlines = "";
54 1         3 my @textlines = ();
55              
56             # from all chartlines collect values into buckets for the dimensions we need
57             #
58             # chartline = title
59             # x = perlconfig_version
60             # y = VALUE
61 1         2 my @titles;
62             my %VALUES;
63             CHARTLINE:
64 1         2 foreach my $chartline (@$chartlines)
65             {
66 3         16 my $title = $chartline->{title};
67 3         11 my $results = $chartline->{results};
68 3         5 my $NAME = $results->[0]{NAME};
69              
70             # skip typical empty results
71 3 50 33     14 if (not @$results or (@$results == 1 and not $results->[0]{NAME}))
      33        
72             {
73 0 0       0 print STDERR "benchmarkanything: transform_chartlines: ignore empty chartline '$title'\n" if $verbose;
74 0         0 next CHARTLINE;
75             }
76 3         6 push @titles, $title;
77              
78 3         12 my $rawline = sprintf("* %-20s - %-40s", $title, $NAME);
79 3 50       88 print STDERR $rawline."\n" if $verbose;
80 3         21 $textlines .= "$rawline\n";
81              
82 3 50       9 print STDERR " VALUE_IDs: ".join(",", map {$_->{VALUE_ID}} @$results)."\n" if $debug;
  0         0  
83              
84 3 50       15 $VALUES{$title}{NAME} = $NAME if @$results;
85             POINT:
86 3         7 foreach my $point (@$results)
87             {
88 12         25 my $x = $point->{$x_key};
89 12         21 my $y = $point->{$y_key};
90 12 50       24 if (not defined $x)
91             {
92 0         0 require Data::Dumper;
93 0 0       0 print STDERR "benchmarkanything: transform_chartlines: chartline '$title': ignore data point (missing key '$x_key'): ".Data::Dumper::Dumper($results) if $verbose;
94 0         0 next POINT;
95             }
96 12         19 push @{$VALUES{$title}{xvalues}{$x}{values}}, $y; # maybe multiple for same X - average them later
  12         45  
97             }
98             }
99              
100             # statistical aggregations of multi points
101 1         3 foreach my $title (@titles)
102             {
103 3         162 foreach my $x (sort keys %{$VALUES{$title}{xvalues}})
  3         21  
104             {
105 12         882 my $multi_point_values = $VALUES{$title}{xvalues}{$x}{values};
106 12         30 $VALUES{$title}{xvalues}{$x}{stats} = multi_point_stats($multi_point_values);
107             }
108             }
109              
110             # find out all available x-values from all chartlines
111 1         71 my %all_x;
112 1         2 foreach my $title (@titles)
113             {
114 3         4 foreach my $x (sort keys %{$VALUES{$title}{xvalues}})
  3         12  
115             {
116 12         22 $all_x{$x} = 1;
117             }
118             }
119 1         5 my @all_x = keys %all_x;
120             @all_x =
121 4         47 $x_type eq 'version' ? sort {version->parse($a) <=> version->parse($b)} @all_x
122 0         0 : $x_type eq 'numeric' ? sort {$a <=> $b} @all_x
123 0         0 : $x_type eq 'string' ? sort {$a cmp $b} @all_x
124 1 0       6 : $x_type eq 'date' ? sort { die "TODO: sort by date" ; $a cmp $b} @all_x
  0 0       0  
  0 0       0  
    50          
125             : @all_x;
126              
127             # drop complete chartlines if it has gaps on versions that the other chartlines provide values
128 1         3 my %clean_chartlines;
129 1 50       6 if ($dropnull) {
130 0         0 foreach my $title (@titles) {
131 0         0 my $ok = 1;
132 0         0 foreach my $x (@all_x) {
133 0 0       0 if (not @{$VALUES{$title}{xvalues}{$x}{values} || []}) {
  0 0       0  
134 0 0       0 print STDERR "skip: $title (missing values for $x)\n" if $verbose;
135 0         0 $ok = 0;
136             }
137             }
138 0 0       0 if ($ok) {
139 0         0 $clean_chartlines{$title} = 1;
140 0 0       0 print STDERR "okay: $title\n" if $verbose;
141             }
142             }
143             }
144              
145             # intermediate debug output
146 1         3 foreach my $title (@titles)
147             {
148 3         10 foreach my $x (sort keys %{$VALUES{$title}{xvalues}})
  3         14  
149             {
150 12   50     33 my $count = scalar @{$VALUES{$title}{xvalues}{$x}{values} || []} || 0;
151 12 50       26 next if not $count;
152 12         25 my $avg = $VALUES{$title}{xvalues}{$x}{stats}{avg};
153 12         24 my $stdv = $VALUES{$title}{xvalues}{$x}{stats}{stdv};
154 12         29 my $ci95l = $VALUES{$title}{xvalues}{$x}{stats}{ci_95_lower};
155 12         19 my $ci95u = $VALUES{$title}{xvalues}{$x}{stats}{ci_95_upper};
156 12         91 my $rawline = sprintf(" %-20s . %-7s . (ci95l..avg..ci95u) = (%2.2f .. %2.2f .. %2.2f) +- stdv %5.2f (%3d points)", $title, $x, $ci95l, $avg, $ci95u, $stdv, $count);
157 12         1586 $textlines .= "$rawline\n";
158 12         30 push @textlines, $rawline;
159 12 50       375 print STDERR $rawline."\n" if $verbose;
160             }
161             }
162              
163             # result data structure, as needed per chart type
164 1         4 my @RESULTMATRIX;
165              
166 1 50       2 @titles = grep { !$dropnull or $clean_chartlines{$_} } @titles; # dropnull
  3         13  
167              
168 1         4 for (my $i=0; $i<@all_x; $i++) # rows
169             {
170 4         9 my $x = $all_x[$i];
171 4         10 for (my $j=0; $j<@titles; $j++) # columns
172             {
173 12         21 my $title = $titles[$j];
174 12         27 my $value = $VALUES{$title}{xvalues}{$x}{stats}{$aggregation};
175             # stringify to unbless from PDL, then numify for type-aware JSON
176 12 50       33 $value = $value ? (0+sprintf("%6.2f", $value)) : undef;
177 12 100 33     1121 $RESULTMATRIX[0] [0] = ($x_key_short // $x_key) if $i == 0 && $j == 0;
      100        
178 12 100       41 $RESULTMATRIX[0] [$j+1] = $title if $i == 0;
179 12 100       29 $RESULTMATRIX[$i+1] [0] = $x if $j == 0;
180 12         43 $RESULTMATRIX[$i+1] [$j+1] = $value;
181             }
182             }
183              
184 1 50       4 if (wantarray) {
185 0         0 return (\@RESULTMATRIX, $textlines, \@textlines, \%VALUES);
186             } else {
187 1         123 return \@RESULTMATRIX;
188             }
189             }
190              
191             1;
192              
193             __END__