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