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