File Coverage

blib/lib/Statistics/Descriptive/Sparse.pm
Criterion Covered Total %
statement 62 110 56.3
branch 14 28 50.0
condition 1 3 33.3
subroutine 14 17 82.3
pod 5 5 100.0
total 96 163 58.9


line stmt bran cond sub pod time code
1             package Statistics::Descriptive::Sparse;
2             $Statistics::Descriptive::Sparse::VERSION = '3.0702';
3 9     9   59 use strict;
  9         24  
  9         254  
4 9     9   47 use warnings;
  9         20  
  9         242  
5              
6 9     9   44 use vars qw(%fields);
  9         19  
  9         378  
7 9     9   53 use Carp;
  9         50  
  9         621  
8             require Statistics::Descriptive;
9 9     9   4090 use Statistics::Descriptive::Smoother;
  9         26  
  9         408  
10              
11             sub _make_accessors
12             {
13 27     27   91 my ($pkg, $methods) = @_;
14              
15 9     9   61 no strict 'refs';
  9         22  
  9         1228  
16 27         66 foreach my $method (@$methods)
17             {
18 117         492 *{$pkg."::".$method} =
19 117         183 do {
20 117         197 my $m = $method;
21             sub {
22 799     799   2653 my $self = shift;
23              
24 799 100       1542 if (@_)
25             {
26 445         851 $self->{$m} = shift;
27             }
28 799         1771 return $self->{$m};
29 117         331 };
30             };
31             }
32              
33 27         66 return;
34             }
35              
36             sub _make_private_accessors
37             {
38 18     18   64 my ($pkg, $methods) = @_;
39              
40 9     9   64 no strict 'refs';
  9         20  
  9         7070  
41 18         58 foreach my $method (@$methods)
42             {
43 108         454 *{$pkg."::_".$method} =
44 108         175 do {
45 108         162 my $m = $method;
46             sub {
47 461     461   700 my $self = shift;
48              
49 461 100       880 if (@_)
50             {
51 173         330 $self->{$m} = shift;
52             }
53 461         1248 return $self->{$m};
54 108         320 };
55             };
56             }
57              
58 18         201 return;
59             }
60              
61             ##Define the fields to be used as methods
62             %fields = (
63             count => 0,
64             mean => undef,
65             sum => undef,
66             sumsq => undef,
67             min => undef,
68             max => undef,
69             mindex => undef,
70             maxdex => undef,
71             sample_range => undef,
72             variance => undef,
73             );
74              
75             __PACKAGE__->_make_accessors( [ grep { $_ ne "variance" } keys(%fields) ] );
76             __PACKAGE__->_make_accessors( ["_permitted"] );
77             __PACKAGE__->_make_private_accessors(["variance"]);
78              
79             sub new {
80 39     39 1 76 my $proto = shift;
81 39   33     128 my $class = ref($proto) || $proto;
82 39         273 my $self = {
83             %fields,
84             };
85 39         111 bless ($self, $class);
86 39         153 $self->_permitted(\%fields);
87 39         99 return $self;
88             }
89              
90             sub _is_permitted
91             {
92 0     0   0 my $self = shift;
93 0         0 my $key = shift;
94              
95 0         0 return exists($self->_permitted()->{$key});
96             }
97              
98             sub add_data {
99 0     0 1 0 my $self = shift; ##Myself
100 0         0 my $oldmean;
101 0         0 my ($min,$mindex,$max,$maxdex,$sum,$sumsq,$count);
102 0         0 my $aref;
103              
104 0 0       0 if (ref $_[0] eq 'ARRAY') {
105 0         0 $aref = $_[0];
106             }
107             else {
108 0         0 $aref = \@_;
109             }
110              
111             ##If we were given no data, we do nothing.
112 0 0       0 return 1 if (!@{ $aref });
  0         0  
113              
114             ##Take care of appending to an existing data set
115              
116 0 0       0 if (!defined($min = $self->min()))
117             {
118 0         0 $min = $aref->[$mindex = 0];
119             }
120             else
121             {
122 0         0 $mindex = $self->mindex();
123             }
124              
125 0 0       0 if (!defined($max = $self->max()))
126             {
127 0         0 $max = $aref->[$maxdex = 0];
128             }
129             else
130             {
131 0         0 $maxdex = $self->maxdex();
132             }
133              
134 0         0 $sum = $self->sum();
135 0         0 $sumsq = $self->sumsq();
136 0         0 $count = $self->count();
137              
138             ##Calculate new mean, sumsq, min and max;
139 0         0 foreach ( @{ $aref } ) {
  0         0  
140 0         0 $sum += $_;
141 0         0 $sumsq += $_**2;
142 0         0 $count++;
143 0 0       0 if ($_ >= $max) {
144 0         0 $max = $_;
145 0         0 $maxdex = $count-1;
146             }
147 0 0       0 if ($_ <= $min) {
148 0         0 $min = $_;
149 0         0 $mindex = $count-1;
150             }
151             }
152              
153 0         0 $self->min($min);
154 0         0 $self->mindex($mindex);
155 0         0 $self->max($max);
156 0         0 $self->maxdex($maxdex);
157 0         0 $self->sample_range($max - $min);
158 0         0 $self->sum($sum);
159 0         0 $self->sumsq($sumsq);
160 0         0 $self->mean($sum / $count);
161 0         0 $self->count($count);
162             ##indicator the value is not cached. Variance isn't commonly enough
163             ##used to recompute every single data add.
164 0         0 $self->_variance(undef);
165 0         0 return 1;
166             }
167              
168              
169             sub standard_deviation {
170 21     21 1 325 my $self = shift; ##Myself
171 21 100       36 return undef if (!$self->count());
172 17         36 return sqrt($self->variance());
173             }
174              
175             ##Return variance; if needed, compute and cache it.
176             sub variance {
177 19     19 1 315 my $self = shift; ##Myself
178              
179 19         37 my $count = $self->count();
180              
181 19 100       43 return undef if !$count;
182              
183 18 100       40 return 0 if $count == 1;
184              
185 17 100       33 if (!defined($self->_variance())) {
186 8         17 my $variance = ($self->sumsq()- $count * $self->mean()**2);
187              
188             # Sometimes due to rounding errors we get a number below 0.
189             # This makes sure this is handled as gracefully as possible.
190             #
191             # See:
192             #
193             # https://rt.cpan.org/Public/Bug/Display.html?id=46026
194              
195 8 100       26 $variance = $variance < 0 ? 0 : $variance / ($count - 1);
196              
197 8         22 $self->_variance($variance);
198              
199             # Return now to avoid re-entering this sub
200             # (and therefore save time when many objects are used).
201 8         27 return $variance;
202             }
203              
204 9         20 return $self->_variance();
205             }
206              
207             ##Clear a stat. More efficient than destroying an object and calling
208             ##new.
209             sub clear {
210 0     0 1   my $self = shift; ##Myself
211 0           my $key;
212              
213 0 0         return if (!$self->count());
214 0           while (my($field, $value) = each %fields) { # could use a slice assignment here
215 0           $self->{$field} = $value;
216             }
217             }
218              
219             1;
220              
221             __END__