File Coverage

blib/lib/BenchmarkAnything/Evaluations.pm
Criterion Covered Total %
statement 90 111 81.0
branch 17 48 35.4
condition 6 11 54.5
subroutine 8 8 100.0
pod 2 2 100.0
total 123 180 68.3


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