File Coverage

blib/lib/Statistics/Descriptive/Full.pm
Criterion Covered Total %
statement 395 414 95.4
branch 97 118 82.2
condition 18 23 78.2
subroutine 43 45 95.5
pod 26 26 100.0
total 579 626 92.4


line stmt bran cond sub pod time code
1             package Statistics::Descriptive::Full;
2             $Statistics::Descriptive::Full::VERSION = '3.0800';
3 8     8   45 use strict;
  8         15  
  8         211  
4 8     8   41 use warnings;
  8         20  
  8         185  
5              
6 8     8   34 use Carp;
  8         12  
  8         412  
7 8     8   3406 use POSIX ();
  8         43317  
  8         210  
8 8     8   55 use Statistics::Descriptive::Smoother;
  8         12  
  8         198  
9              
10 8     8   35 use vars qw($a $b %fields);
  8         14  
  8         391  
11              
12 8     8   2844 use parent qw(Statistics::Descriptive::Sparse);
  8         1893  
  8         41  
13              
14 8     8   3930 use List::MoreUtils ();
  8         85575  
  8         200  
15 8     8   61 use List::Util ();
  8         17  
  8         26709  
16              
17             ## no critic (ProhibitExplicitReturnUndef)
18             ##Create a list of fields not to remove when data is updated
19             %fields = (
20             _permitted => undef, ##Place holder for the inherited key hash
21             data => undef, ##Our data
22             samples => undef, ##Number of samples for each value of the data set
23             presorted => undef, ##Flag to indicate the data is already sorted
24             _reserved => undef, ##Place holder for this lookup hash
25             );
26              
27             __PACKAGE__->_make_private_accessors(
28             [
29             qw(data samples frequency geometric_mean harmonic_mean
30             least_squares_fit median mode
31             skewness kurtosis median_absolute_deviation
32             )
33             ]
34             );
35             __PACKAGE__->_make_accessors( [qw(presorted _reserved _trimmed_mean_cache)] );
36              
37             sub _clear_fields
38             {
39 39     39   58 my $self = shift;
40              
41             # Empty array ref for holding data later!
42 39         116 $self->_data( [] );
43 39         120 $self->_samples( [] );
44 39         108 $self->_reserved( \%fields );
45 39         131 $self->presorted(0);
46 39         97 $self->_trimmed_mean_cache( +{} );
47              
48 39         55 return;
49             }
50              
51             ##Have to override the base method to add the data to the object
52             ##The proxy method from above is still valid
53             sub new
54             {
55 39     39 1 11954 my $proto = shift;
56 39   66     185 my $class = ref($proto) || $proto;
57              
58             # Create my self re SUPER
59 39         143 my $self = $class->SUPER::new();
60 39         71 bless( $self, $class ); #Re-anneal the object
61 39         108 $self->_clear_fields();
62 39         74 return $self;
63             }
64              
65             sub _is_reserved
66             {
67 0     0   0 my $self = shift;
68 0         0 my $field = shift;
69              
70 0         0 return exists( $self->_reserved->{$field} );
71             }
72              
73             sub _delete_all_cached_keys
74             {
75 4     4   8 my $self = shift;
76              
77 4         4 my %keys = %{$self};
  4         32  
78              
79             # Remove reserved keys for this class from the deletion list
80 4         8 delete @keys{ keys %{ $self->_reserved } };
  4         8  
81 4         8 delete @keys{ keys %{ $self->_permitted } };
  4         8  
82 4         9 delete $keys{_trimmed_mean_cache};
83              
84             KEYS_LOOP:
85 4         10 foreach my $key ( keys %keys )
86             { # Check each key in the object
87 6         10 delete $self->{$key}; # Delete any out of date cached key
88             }
89 4         9 $self->{_trimmed_mean_cache} = {}; # just reset this one
90 4         7 return;
91             }
92              
93             ##Clear a stat. More efficient than destroying an object and calling
94             ##new.
95             sub clear
96             {
97 0     0 1 0 my $self = shift; ##Myself
98 0         0 my $key;
99              
100 0 0       0 if ( !$self->count() )
101             {
102 0         0 return;
103             }
104              
105 0         0 $self->_delete_all_cached_keys();
106 0         0 $self->SUPER::clear();
107 0         0 $self->_clear_fields();
108             }
109              
110             sub add_data
111             {
112 39     39 1 1049 my $self = shift; ##Myself
113              
114 39         52 my $aref;
115              
116 39 100       98 if ( ref $_[0] eq 'ARRAY' )
117             {
118 3         29 $aref = $_[0];
119             }
120             else
121             {
122 36         60 $aref = \@_;
123             }
124              
125             ##If we were given no data, we do nothing.
126 39 50       57 return 1 if ( !@{$aref} );
  39         82  
127              
128 39         53 my $oldmean;
129 39         54 my ( $min, $max, $sum, $sumsq );
130 39         101 my $count = $self->count;
131              
132             # $count is modified lower down, but we need this flag after that
133 39         56 my $has_existing_data = $count;
134              
135             # Take care of appending to an existing data set
136 39 100       76 if ($has_existing_data)
137             {
138 4         16 $min = $self->min();
139 4         10 $max = $self->max();
140 4         8 $sum = $self->sum();
141 4         9 $sumsq = $self->sumsq();
142             }
143             else
144             {
145 35         54 $min = $aref->[0];
146 35         44 $max = $aref->[0];
147 35         45 $sum = 0;
148 35         51 $sumsq = 0;
149             }
150              
151             # need to allow for already having data
152 39         168 $sum += List::Util::sum(@$aref);
153 39         81 $sumsq += List::Util::sum( map { $_**2 } @$aref );
  477         680  
154 39         110 $max = List::Util::max( $max, @$aref );
155 39         74 $min = List::Util::min( $min, @$aref );
156 39         58 $count += scalar @$aref;
157 39         85 my $mean = $sum / $count;
158              
159 39         110 $self->min($min);
160 39         141 $self->max($max);
161 39         122 $self->sample_range( $max - $min );
162 39         146 $self->sum($sum);
163 39         103 $self->sumsq($sumsq);
164 39         96 $self->mean($mean);
165 39         105 $self->count($count);
166              
167             ##Variance isn't commonly enough
168             ##used to recompute every single data add, so just clear its cache.
169 39         109 $self->_variance(undef);
170              
171 39         153 push @{ $self->_data() }, @{$aref};
  39         79  
  39         156  
172              
173             # no need to clear keys if we are a newly populated object,
174             # and profiling shows it takes a long time when creating
175             # and populating many stats objects
176 39 100       94 if ($has_existing_data)
177             {
178             ##Clear the presorted flag
179 4         10 $self->presorted(0);
180 4         9 $self->_delete_all_cached_keys();
181             }
182              
183 39         88 return 1;
184             }
185              
186             sub add_data_with_samples
187             {
188 1     1 1 11 my ( $self, $aref_values ) = @_;
189              
190 1 50       3 return 1 if ( !@{$aref_values} );
  1         4  
191              
192 1         2 my $aref_data = [ map { keys %$_ } @{$aref_values} ];
  5         11  
  1         3  
193 1         3 my $aref_samples = [ map { values %$_ } @{$aref_values} ];
  5         9  
  1         3  
194              
195 1         51 $self->add_data($aref_data);
196 1         2 push @{ $self->_samples() }, @{$aref_samples};
  1         3  
  1         2  
197              
198 1         3 return 1;
199             }
200              
201             sub get_data
202             {
203 18     18 1 25 my $self = shift;
204 18         19 return @{ $self->_data() };
  18         36  
205             }
206              
207             sub get_data_without_outliers
208             {
209 5     5 1 15 my $self = shift;
210              
211 5 100       12 if ( $self->count() < $Statistics::Descriptive::Min_samples_number )
212             {
213 1         78 carp(
214             "Need at least $Statistics::Descriptive::Min_samples_number samples\n"
215             );
216 1         26 return;
217             }
218              
219 4 100       10 if ( !defined $self->{_outlier_filter} )
220             {
221 1         69 carp("Outliers filter not defined\n");
222 1         25 return;
223             }
224              
225 3         5 my $outlier_candidate_index = $self->_outlier_candidate_index;
226 3         7 my $possible_outlier = ( $self->_data() )->[$outlier_candidate_index];
227 3         8 my $is_outlier = $self->{_outlier_filter}->( $self, $possible_outlier );
228              
229 3 100       12 return $self->get_data unless $is_outlier;
230              
231             # Removing the outlier from the dataset
232             my @good_indexes =
233 2         4 grep { $_ != $outlier_candidate_index } ( 0 .. $self->count() - 1 );
  16         26  
234              
235 2         5 my @data = $self->get_data;
236 2         5 my @filtered_data = @data[@good_indexes];
237 2         7 return @filtered_data;
238             }
239              
240             sub set_outlier_filter
241             {
242 6     6 1 22 my ( $self, $code_ref ) = @_;
243              
244 6 100 100     26 if ( !$code_ref || ref($code_ref) ne "CODE" )
245             {
246 2         269 carp("Need to pass a code reference");
247 2         81 return;
248             }
249              
250 4         10 $self->{_outlier_filter} = $code_ref;
251 4         7 return 1;
252             }
253              
254             sub _outlier_candidate_index
255             {
256 3     3   8 my $self = shift;
257              
258 3         6 my $mean = $self->mean();
259 3         6 my $outlier_candidate_index = 0;
260 3         5 my $max_std_deviation = abs( ( $self->_data() )->[0] - $mean );
261 3         7 foreach my $idx ( 1 .. ( $self->count() - 1 ) )
262             {
263 21         30 my $curr_value = ( $self->_data() )->[$idx];
264 21 100       45 if ( $max_std_deviation < abs( $curr_value - $mean ) )
265             {
266 3         3 $outlier_candidate_index = $idx;
267 3         5 $max_std_deviation = abs( $curr_value - $mean );
268             }
269             }
270 3         7 return $outlier_candidate_index;
271             }
272              
273             sub set_smoother
274             {
275 2     2 1 9 my ( $self, $args ) = @_;
276              
277 2         5 $args->{data} = $self->_data();
278 2         6 $args->{samples} = $self->_samples();
279              
280 2         9 $self->{_smoother} = Statistics::Descriptive::Smoother->instantiate($args);
281             }
282              
283             sub get_smoothed_data
284             {
285 2     2 1 7 my ( $self, $args ) = @_;
286              
287 2 100       6 if ( !defined $self->{_smoother} )
288             {
289 1         150 carp("Smoother object not defined\n");
290 1         36 return;
291             }
292 1         4 $self->{_smoother}->get_smoothed_data();
293             }
294              
295             sub maxdex
296             {
297 4     4 1 253 my $self = shift;
298              
299 4 100       12 return undef if !$self->count;
300 3         4 my $maxdex;
301              
302 3 100       7 if ( $self->presorted )
303             {
304 1         3 $maxdex = $self->count - 1;
305             }
306             else
307             {
308 2         7 my $max = $self->max;
309 2     10   10 $maxdex = List::MoreUtils::first_index { $_ == $max } $self->get_data;
  10         13  
310             }
311              
312 3         7 $self->{maxdex} = $maxdex;
313              
314 3         9 return $maxdex;
315             }
316              
317             sub mindex
318             {
319 4     4 1 231 my $self = shift;
320              
321 4 100       10 return undef if !$self->count;
322              
323             #my $maxdex = $self->{maxdex};
324             #return $maxdex if defined $maxdex;
325 3         5 my $mindex;
326              
327 3 100       6 if ( $self->presorted )
328             {
329 1         2 $mindex = 0;
330             }
331             else
332             {
333 2         5 my $min = $self->min;
334 2     4   10 $mindex = List::MoreUtils::first_index { $_ == $min } $self->get_data;
  4         8  
335             }
336              
337 3         9 $self->{mindex} = $mindex;
338              
339 3         11 return $mindex;
340             }
341              
342             sub sort_data
343             {
344 28     28 1 37 my $self = shift;
345              
346 28 100       55 if ( !$self->presorted() )
347             {
348             ##Sort the data in descending order
349 12         16 $self->_data( [ sort { $a <=> $b } @{ $self->_data() } ] );
  771         856  
  12         25  
350 12         39 $self->presorted(1);
351             ##Fix the maxima and minima indices - no, this is unnecessary now we have methods
352             #$self->mindex(0);
353             #$self->maxdex($#{$self->_data()});
354             }
355              
356 28         53 return 1;
357             }
358              
359             sub percentile
360             {
361 4     4 1 239 my $self = shift;
362 4   100     14 my $percentile = shift || 0;
363             ##Since we're returning a single value there's no real need
364             ##to cache this.
365              
366             ##If the requested percentile is less than the "percentile bin
367             ##size" then return undef. Check description of RFC 2330 in the
368             ##POD below.
369 4         11 my $count = $self->count();
370              
371 4 100 66     21 if ( ( !$count ) || ( $percentile < 100 / $count ) )
372             {
373 2         5 return; # allow for both scalar and list context
374             }
375              
376 2         6 $self->sort_data();
377 2         5 my $num = $count * $percentile / 100;
378 2         8 my $index = &POSIX::ceil($num) - 1;
379 2         6 my $val = $self->_data->[$index];
380             return wantarray
381 2 50       10 ? ( $val, $index )
382             : $val;
383             }
384              
385             sub _calc_new_median
386             {
387 7     7   10 my $self = shift;
388 7         13 my $count = $self->count();
389              
390             ##Even or odd
391 7 100       22 if ( $count % 2 )
392             {
393 4         8 return $self->_data->[ ( $count - 1 ) / 2 ];
394             }
395             else
396             {
397             return (
398             (
399 3         7 $self->_data->[ ($count) / 2 ] +
400             $self->_data->[ ( $count - 2 ) / 2 ]
401             ) / 2
402             );
403             }
404             }
405              
406             sub median
407             {
408 17     17 1 258 my $self = shift;
409              
410 17 100       33 return undef if !$self->count;
411              
412             ##Cached?
413 15 100       32 if ( !defined( $self->_median() ) )
414             {
415 7         20 $self->sort_data();
416 7         18 $self->_median( $self->_calc_new_median() );
417             }
418 15         26 return $self->_median();
419             }
420              
421             sub quantile
422             {
423 18     18 1 5720 my ( $self, $QuantileNumber ) = @_;
424              
425 18 50 33     122 unless ( defined $QuantileNumber and $QuantileNumber =~ m/^0|1|2|3|4$/ )
426             {
427 0         0 carp("Bad quartile type, must be 0, 1, 2, 3 or 4\n");
428 0         0 return;
429             }
430              
431             # check data count after the args are checked - should help debugging
432 18 100       47 return undef if !$self->count;
433              
434 17         39 $self->sort_data();
435              
436 17 100       29 return $self->_data->[0] if ( $QuantileNumber == 0 );
437              
438 14         27 my $count = $self->count();
439              
440 14 100       27 return $self->_data->[ $count - 1 ] if ( $QuantileNumber == 4 );
441              
442 11         30 my $K_quantile = ( ( $QuantileNumber / 4 ) * ( $count - 1 ) + 1 );
443 11         32 my $F_quantile = $K_quantile - POSIX::floor($K_quantile);
444 11         19 $K_quantile = POSIX::floor($K_quantile);
445              
446             # interpolation
447 11         24 my $aK_quantile = $self->_data->[ $K_quantile - 1 ];
448 11 100       33 return $aK_quantile if ( $F_quantile == 0 );
449 7         11 my $aKPlus_quantile = $self->_data->[$K_quantile];
450              
451             # Calcul quantile
452 7         14 my $quantile =
453             $aK_quantile + ( $F_quantile * ( $aKPlus_quantile - $aK_quantile ) );
454              
455 7         44 return $quantile;
456             }
457              
458             sub _real_calc_trimmed_mean
459             {
460 2     2   3 my $self = shift;
461 2         4 my $lower = shift;
462 2         3 my $upper = shift;
463              
464 2         4 my $lower_trim = int( $self->count() * $lower );
465 2         4 my $upper_trim = int( $self->count() * $upper );
466 2         4 my ( $val, $oldmean ) = ( 0, 0 );
467 2         4 my ( $tm_count, $tm_mean, $index ) = ( 0, 0, $lower_trim );
468              
469 2         5 $self->sort_data();
470 2         5 while ( $index <= $self->count() - $upper_trim - 1 )
471             {
472 17         34 $val = $self->_data()->[$index];
473 17         19 $oldmean = $tm_mean;
474 17         17 ++$index;
475 17         20 ++$tm_count;
476 17         37 $tm_mean += ( $val - $oldmean ) / $tm_count;
477             }
478              
479 2         7 return $tm_mean;
480             }
481              
482             sub trimmed_mean
483             {
484 4     4 1 237 my $self = shift;
485 4         7 my ( $lower, $upper );
486              
487             #upper bound is in arg list or is same as lower
488 4 100       10 if ( @_ == 1 )
489             {
490 2         5 ( $lower, $upper ) = ( $_[0], $_[0] );
491             }
492             else
493             {
494 2         5 ( $lower, $upper ) = ( $_[0], $_[1] );
495             }
496              
497             # check data count after the args
498 4 100       11 return undef if !$self->count;
499              
500             ##Cache
501 3         20 my $thistm = join ':', $lower, $upper;
502 3         7 my $cache = $self->_trimmed_mean_cache();
503 3 100       7 if ( !exists( $cache->{$thistm} ) )
504             {
505 2         6 $cache->{$thistm} = $self->_real_calc_trimmed_mean( $lower, $upper );
506             }
507              
508 3         11 return $cache->{$thistm};
509             }
510              
511             sub _test_for_too_small_val
512             {
513 15     15   17 my $self = shift;
514 15         17 my $val = shift;
515              
516 15         33 return ( abs($val) <= $Statistics::Descriptive::Tolerance );
517             }
518              
519             sub _calc_harmonic_mean
520             {
521 5     5   10 my $self = shift;
522              
523 5         6 my $hs = 0;
524              
525 5         6 foreach my $item ( @{ $self->_data() } )
  5         10  
526             {
527             ##Guarantee that there are no divide by zeros
528 11 100       19 if ( $self->_test_for_too_small_val($item) )
529             {
530 1         4 return;
531             }
532              
533 10         17 $hs += 1 / $item;
534             }
535              
536 4 100       10 if ( $self->_test_for_too_small_val($hs) )
537             {
538 3         8 return;
539             }
540              
541 1         4 return $self->count() / $hs;
542             }
543              
544             sub harmonic_mean
545             {
546 5     5 1 241 my $self = shift;
547              
548 5 50       14 if ( !defined( $self->_harmonic_mean() ) )
549             {
550 5         10 $self->_harmonic_mean( scalar( $self->_calc_harmonic_mean() ) );
551             }
552              
553 5         10 return $self->_harmonic_mean();
554             }
555              
556             sub mode
557             {
558 5     5 1 1073 my $self = shift;
559              
560 5 100       14 if ( !defined( $self->_mode() ) )
561             {
562 3         6 my $mode = 0;
563 3         5 my $occurances = 0;
564              
565 3         4 my %count;
566              
567 3         5 foreach my $item ( @{ $self->_data() } )
  3         15  
568             {
569 11         19 my $count = ++$count{$item};
570 11 100       18 if ( $count > $occurances )
571             {
572 4         6 $mode = $item;
573 4         5 $occurances = $count;
574             }
575             }
576              
577             $self->_mode(
578 3 100       15 ( $occurances > 1 )
579             ? { exists => 1, mode => $mode }
580             : { exists => 0, }
581             );
582             }
583              
584 5         13 my $m = $self->_mode;
585              
586 5 100       16 return $m->{'exists'} ? $m->{mode} : undef;
587             }
588              
589             sub geometric_mean
590             {
591 2     2 1 240 my $self = shift;
592              
593 2 100       7 return undef if !$self->count;
594              
595 1 50       4 if ( !defined( $self->_geometric_mean() ) )
596             {
597 1         3 my $gm = 1;
598 1         4 my $exponent = 1 / $self->count();
599              
600 1         3 for my $val ( @{ $self->_data() } )
  1         3  
601             {
602 3 50       8 if ( $val < 0 )
603             {
604 0         0 return undef;
605             }
606 3         8 $gm *= $val**$exponent;
607             }
608              
609 1         4 $self->_geometric_mean($gm);
610             }
611              
612 1         3 return $self->_geometric_mean();
613             }
614              
615             sub skewness
616             {
617 7     7 1 242 my $self = shift;
618              
619 7 50       15 if ( !defined( $self->_skewness() ) )
620             {
621 7         15 my $n = $self->count();
622 7         16 my $sd = $self->standard_deviation();
623              
624 7         11 my $skew;
625              
626             # skip if insufficient records
627 7 100 100     27 if ( $sd && $n > 2 )
628             {
629              
630 5         9 my $mean = $self->mean();
631              
632 5         9 my $sum_pow3;
633 5         10 foreach my $rec ( $self->get_data )
634             {
635 83         149 $sum_pow3 += ( ( $rec - $mean ) / $sd )**3;
636             }
637              
638 5         11 my $correction = $n / ( ( $n - 1 ) * ( $n - 2 ) );
639              
640 5         8 $skew = $correction * $sum_pow3;
641             }
642              
643 7         12 $self->_skewness($skew);
644             }
645              
646 7         15 return $self->_skewness();
647             }
648              
649             sub kurtosis
650             {
651 7     7 1 267 my $self = shift;
652              
653 7 50       17 if ( !defined( $self->_kurtosis() ) )
654             {
655 7         11 my $kurt;
656              
657 7         14 my $n = $self->count();
658 7         15 my $sd = $self->standard_deviation();
659              
660 7 100 100     27 if ( $sd && $n > 3 )
661             {
662              
663 5         11 my $mean = $self->mean();
664              
665 5         8 my $sum_pow4;
666 5         10 foreach my $rec ( $self->get_data )
667             {
668 83         135 $sum_pow4 += ( ( $rec - $mean ) / $sd )**4;
669             }
670              
671 5         15 my $correction1 =
672             ( $n * ( $n + 1 ) ) / ( ( $n - 1 ) * ( $n - 2 ) * ( $n - 3 ) );
673 5         9 my $correction2 =
674             ( 3 * ( $n - 1 )**2 ) / ( ( $n - 2 ) * ( $n - 3 ) );
675              
676 5         10 $kurt = ( $correction1 * $sum_pow4 ) - $correction2;
677             }
678              
679 7         14 $self->_kurtosis($kurt);
680             }
681              
682 7         15 return $self->_kurtosis();
683             }
684              
685             sub frequency_distribution_ref
686             {
687 6     6 1 12 my $self = shift;
688 6         11 my @k = ();
689              
690             # Must have at least two elements
691 6 100       14 if ( $self->count() < 2 )
692             {
693 1         3 return undef;
694             }
695              
696 5 100 66     16 if ( ( !@_ ) && ( defined $self->_frequency() ) )
697             {
698 1         3 return $self->_frequency();
699             }
700              
701 4         6 my %bins;
702 4         7 my $partitions = shift;
703              
704 4 100       11 if ( ref($partitions) eq 'ARRAY' )
705             {
706 2         5 @k = @{$partitions};
  2         5  
707 2 50       5 return undef unless @k; ##Empty array
708 2 50       6 if ( @k > 1 )
709             {
710             ##Check for monotonicity
711 2         3 my $element = $k[0];
712 2         7 for my $next_elem ( @k[ 1 .. $#k ] )
713             {
714 8 50       14 if ( $element > $next_elem )
715             {
716 0         0 carp
717             "Non monotonic array cannot be used as frequency bins!\n";
718 0         0 return undef;
719             }
720 8         12 $element = $next_elem;
721             }
722             }
723 2         4 %bins = map { $_ => 0 } @k;
  10         21  
724             }
725             else
726             {
727 2 50       6 return undef unless $partitions >= 1;
728 2         6 my $interval = $self->sample_range() / $partitions;
729 2         7 foreach my $idx ( 1 .. ( $partitions - 1 ) )
730             {
731 20         31 push @k, ( $self->min() + $idx * $interval );
732             }
733              
734 2         6 $bins{ $self->max() } = 0;
735              
736 2         6 push @k, $self->max();
737             }
738              
739             ELEMENT:
740 4         8 foreach my $element ( @{ $self->_data() } )
  4         9  
741             {
742 129         148 foreach my $limit (@k)
743             {
744 1134 100       1634 if ( $element <= $limit )
745             {
746 129         269 $bins{$limit}++;
747 129         173 next ELEMENT;
748             }
749             }
750             }
751              
752 4         13 return $self->_frequency( \%bins );
753             }
754              
755             sub frequency_distribution
756             {
757 5     5 1 314 my $self = shift;
758              
759 5         13 my $ret = $self->frequency_distribution_ref(@_);
760              
761 5 100       13 if ( !defined($ret) )
762             {
763 1         3 return undef;
764             }
765             else
766             {
767 4         23 return %$ret;
768             }
769             }
770              
771             sub least_squares_fit
772             {
773 3     3 1 236 my $self = shift;
774 3 100       9 return () if $self->count() < 2;
775              
776             ##Sigma sums
777 1         3 my ( $sigmaxy, $sigmax, $sigmaxx, $sigmayy, $sigmay ) =
778             ( 0, 0, 0, 0, $self->sum );
779 1         3 my ( $xvar, $yvar, $err );
780              
781             ##Work variables
782 1         3 my ( $iter, $y, $x, $denom ) = ( 0, 0, 0, 0 );
783 1         2 my $count = $self->count();
784 1         1 my @x;
785              
786             ##Outputs
787 1         2 my ( $m, $q, $r, $rms );
788              
789 1 50       4 if ( !defined $_[1] )
790             {
791 1         3 @x = 1 .. $self->count();
792             }
793             else
794             {
795 0         0 @x = @_;
796 0 0       0 if ( $self->count() != scalar @x )
797             {
798 0         0 carp "Range and domain are of unequal length.";
799 0         0 return ();
800             }
801             }
802 1         3 foreach my $x_val (@x)
803             {
804 4         6 $y = $self->_data->[$iter];
805 4         6 $sigmayy += $y * $y;
806 4         4 $sigmaxx += $x_val * $x_val;
807 4         5 $sigmaxy += $x_val * $y;
808 4         6 $sigmax += $x_val;
809 4         6 ++$iter;
810             }
811 1         3 $denom = $count * $sigmaxx - $sigmax * $sigmax;
812             return ()
813 1 50       4 unless abs($denom) > $Statistics::Descriptive::Tolerance;
814              
815 1         2 $m = ( $count * $sigmaxy - $sigmax * $sigmay ) / $denom;
816 1         2 $q = ( $sigmaxx * $sigmay - $sigmax * $sigmaxy ) / $denom;
817              
818 1         3 $xvar = $sigmaxx - $sigmax * $sigmax / $count;
819 1         3 $yvar = $sigmayy - $sigmay * $sigmay / $count;
820              
821 1         2 $denom = sqrt( $xvar * $yvar );
822 1 50       3 return () unless ( abs($denom) > $Statistics::Descriptive::Tolerance );
823 1         2 $r = ( $sigmaxy - $sigmax * $sigmay / $count ) / $denom;
824              
825 1         2 $iter = 0;
826 1         2 $rms = 0.0;
827 1         2 foreach (@x)
828             {
829             ##Error = Real y - calculated y
830 4         8 $err = $self->_data->[$iter] - ( $m * $_ + $q );
831 4         5 $rms += $err * $err;
832 4         6 ++$iter;
833             }
834              
835 1         2 $rms = sqrt( $rms / $count );
836              
837 1         4 $self->_least_squares_fit( [ $q, $m, $r, $rms ] );
838              
839 1         2 return @{ $self->_least_squares_fit() };
  1         2  
840             }
841              
842             sub median_absolute_deviation
843             {
844 1     1 1 6 my ($self) = @_;
845              
846 1 50       3 if ( !defined( $self->_median_absolute_deviation() ) )
847             {
848 1         4 my $stat = $self->new;
849 1         4 $stat->add_data( map { abs( $_ - $self->median ) } $self->get_data );
  9         13  
850 1         3 $self->_median_absolute_deviation( $stat->median );
851             }
852              
853 1         4 return $self->_median_absolute_deviation();
854             }
855              
856             sub summary
857             {
858 1     1 1 5 my ($self) = @_;
859              
860 1         2 my $FMT = '%.5e';
861              
862             return
863 1         6 sprintf( "Min: $FMT\nMax: $FMT\nMean: $FMT\nMedian: $FMT\n"
864             . "1st quantile: $FMT\n3rd quantile: $FMT\n",
865             $self->min, $self->max, $self->mean, $self->median, $self->quantile(1),
866             $self->quantile(3), );
867              
868             }
869             1;
870              
871             __END__