File Coverage

blib/lib/App/St.pm
Criterion Covered Total %
statement 75 78 96.1
branch 25 42 59.5
condition 9 16 56.2
subroutine 19 19 100.0
pod 14 16 87.5
total 142 171 83.0


line stmt bran cond sub pod time code
1             package App::St;
2              
3 9     9   219728 use strict;
  9         22  
  9         326  
4 9     9   46 use warnings;
  9         15  
  9         12517  
5              
6             #use bignum;
7              
8             our $VERSION = '1.1.1';
9              
10             sub new {
11 8     8 1 1920 my ($class, %opt) = @_;
12              
13 8   50     73 my $delimiter = $opt{'delimiter'} || "\t";
14 8   100     50 my $format = $opt{'format'} || '%.2f';
15              
16 8 50       48 if ($delimiter =~ /^\\[a-z]$/) {
17 0 0       0 $delimiter = $delimiter eq '\t' ? "\t"
    0          
18             : $delimiter eq '\n' ? "\n"
19             : die "Invalid delimiter: '$delimiter'\n";
20             }
21              
22 8 50       83 if ($format =~ m{( \s* \% [\s+-]? [0-9]*\.?[0-9]* [deEfgGi] \s* )}x) {
23 8         35 $format = $1;
24             } else {
25 0         0 die "Invalid format: '$format'\n";
26             }
27              
28              
29 8         171 bless {
30             %opt,
31             N => 0,
32             sum => 0,
33             sum_square => 0,
34             mean => 0,
35             stddev => 0,
36             stderr => 0,
37             min => undef,
38             q1 => 0,
39             median => 0,
40             q3 => 0,
41             max => undef,
42             M2 => 0,
43             delimiter => $delimiter,
44             format => $format,
45             data => [],
46             }, $class;
47             }
48              
49             sub validate {
50 91     91 1 11479 my ($self, $num) = @_;
51              
52 91         486 return ($num =~ m{^
53             [+-]?
54             (?: \. ? [0-9]+
55             | [0-9]+ \. [0-9]*
56             | [0-9]* \. ? [0-9]+ [Ee] [+-]? [0-9]+
57             )
58             $}x);
59             }
60              
61             sub process {
62 60     60 1 213 my ($self, $num) = @_;
63              
64 60 50       139 die "Invalid input '$num'\n" if !$self->validate($num);
65              
66 60         116 $self->{N}++;
67              
68 60         78 $self->{sum} += $num;
69              
70 60 100 66     251 $self->{min} = $num if (!defined $self->{min} or $num < $self->{min});
71 60 50 66     259 $self->{max} = $num if (!defined $self->{max} or $num > $self->{max});
72              
73 60         82 my $delta = $num - $self->{mean};
74              
75 60         118 $self->{mean} += $delta / $self->{N};
76 60         152 $self->{M2} += $delta * ($num - $self->{mean});
77              
78 60 100       165 push( @{ $self->{data} }, $num ) if $self->{keep_data};
  30         76  
79             }
80              
81             sub N {
82 12     12 1 49 return $_[0]->{N};
83             }
84              
85             sub sum {
86 3     3 1 18 return $_[0]->{sum};
87             }
88              
89             sub min {
90 3     3 1 14 return $_[0]->{min};
91             }
92              
93             sub max {
94 3     3 1 21 return $_[0]->{max};
95             }
96              
97             sub mean {
98 3     3 1 10 my ($self,%opt) = @_;
99              
100 3         17 my $mean = $self->{mean};
101              
102 3 100       20 return $opt{formatted} ? $self->_format($mean)
103             : $mean;
104             }
105              
106             sub q1 {
107 2     2 1 694 my ($self,%opt) = @_;
108 2         10 return $self->percentile(0.25, %opt);
109             }
110              
111             sub median {
112 1     1 1 2 my ($self,%opt) = @_;
113 1         4 return $self->percentile(0.5, %opt);
114             }
115              
116             sub q3 {
117 2     2 1 6 my ($self,%opt) = @_;
118 2         7 return $self->percentile(0.75, %opt);
119             }
120              
121             sub variance {
122 4     4 0 9 my ($self,%opt) = @_;
123              
124 4         8 my $N = $self->{N};
125 4         8 my $M2 = $self->{M2};
126              
127 4 50       25 my $variance = $N > 1 ? $M2 / ($N - 1) : undef;
128              
129 4 50       14 return $opt{formatted} ? $self->_format($variance)
130             : $variance;
131             }
132              
133             sub stddev {
134 4     4 1 12 my ($self,%opt) = @_;
135              
136 4         15 my $variance = $self->variance();
137              
138 4 50       32 my $stddev = defined $variance ? sqrt($variance) : undef;
139              
140 4 100       22 return $opt{formatted} ? $self->_format($stddev)
141             : $stddev;
142             }
143              
144             sub stderr {
145 1     1 1 3 my ($self,%opt) = shift;
146              
147 1         2 my $stddev = $self->stddev();
148 1         3 my $N = $self->N();
149              
150 1 50       7 my $stderr = defined $stddev ? $stddev/sqrt($N) : undef;
151              
152 1 50       6 return $opt{formatted} ? $self->_format($stderr)
153             : $stderr;
154             }
155              
156             sub percentile {
157 8     8 1 1051 my ($self, $p, %opt) = @_;
158              
159 8         14 my $data = $self->{data};
160              
161 8 50 33     42 if (!$self->{keep_data} or scalar @{$data} == 0) {
  8         62  
162             }
163              
164 8 50 33     51 if ($p < 0 or $p > 1) {
165 0         0 die "Invalid percentile '$p'\n";
166             }
167              
168 8 100       25 if (!$self->{_is_sorted_}) {
169 3         5 $data = [ sort {$a <=> $b} @{ $data } ];
  57         65  
  3         19  
170 3         10 $self->{_is_sorted_} = 1;
171             }
172              
173 8         598 my $N = $self->N();
174 8         16 my $idx = ($N - 1) * $p;
175              
176 8 100       35 my $percentile =
177             int($idx) == $idx ? $data->[$idx]
178             : ($data->[$idx] + $data->[$idx+1]) / 2;
179              
180 8 50       62 return $opt{formatted} ? _format($percentile)
181             : $percentile;
182             }
183              
184             sub result {
185 1     1 0 7 my $self = shift;
186              
187 1         4 my %result = (
188             N => $self->N(),
189             sum => $self->sum(),
190             mean => $self->mean(),
191             stddev => $self->stddev(),
192             stderr => $self->stderr(),
193             min => $self->min(),
194             max => $self->max(),
195             );
196              
197 1 50       5 if ($self->{keep_data}) {
198 1         6 %result = (%result,
199             (
200             q1 => $self->q1(),
201             median => $self->median(),
202             q3 => $self->q3(),
203             )
204             );
205             }
206              
207 1         10 return %result;
208             }
209              
210             sub _format {
211 2     2   4 my ($self,$value,%opt) = @_;
212              
213 2         5 my $format = $self->{format};
214              
215 2         30 return sprintf( $format, $value );
216             }
217              
218             1;
219              
220             __END__