File Coverage

blib/lib/Medical/Growth/NHANES_2000/Base.pm
Criterion Covered Total %
statement 71 71 100.0
branch 16 16 100.0
condition 6 6 100.0
subroutine 17 17 100.0
pod 5 5 100.0
total 115 115 100.0


line stmt bran cond sub pod time code
1             #!/usr/bin/env perl
2              
3 18     18   9255 use 5.010;
  18         50  
  18         636  
4 18     18   110 use strict;
  18         24  
  18         498  
5 18     18   78 use warnings;
  18         32  
  18         831  
6              
7             package Medical::Growth::NHANES_2000::Base;
8              
9             our ($VERSION) = '1.00';
10              
11 18     18   84 use Scalar::Util ();
  18         22  
  18         289  
12 18     18   75 use Exporter;
  18         21  
  18         707  
13 18     18   586 use Moo::Lax; # Vanilla Moo considered harmful
  18         11797  
  18         75  
14              
15 18     18   18790 use Statistics::Standard_Normal qw(z_to_pct pct_to_z);
  18         13860  
  18         1327  
16 18     18   8262 use namespace::clean;
  18         173051  
  18         118  
17              
18             extends 'Medical::Growth::Base';
19              
20             # Ugly hack here to accommodate the fact that MooX::ClassAttribute
21             # doesn't handle inheriting/overriding class attrs.
22             # If you're writing your own, and can afford to use Moose,
23             # MooseX::ClassAttribute is cleaner.
24             sub _declare_params_LMS {
25 33     33   2052 my $class = shift;
26 18     18   4287 no strict 'refs';
  18         36  
  18         9564  
27 33         264 *{ $class . '::_params_LMS' } = sub {
28 32539     32539   29101 state $lms_values = shift->_build_params_LMS;
29 32539         38744 $lms_values;
30             }
31 33         132 }
32              
33             sub _build_params_LMS {
34 17     17   30 my $class = shift;
35 17         290 my (@data);
36              
37 17         28 foreach my $r ( @{ $class->read_data } ) {
  17         127  
38 1751         13184 push @data,
39             {
40             index => $r->[0],
41             L => $r->[1],
42             M => $r->[2],
43             S => $r->[3]
44             };
45             }
46 17         428 @data = sort { $a->{index} <=> $b->{index} } @data;
  1735         1969  
47 17         55 \@data;
48             }
49              
50             sub lookup_LMS {
51 32539     32539 1 33923 my ( $self, $index ) = @_;
52 32539 100       68246 return unless defined $index;
53 32538         52934 my $list = $self->_params_LMS;
54 32538         31485 my $i = 0;
55              
56 32538 100 100     200127 return if $index < $list->[0]->{index} or $index > $list->[-1]->{index};
57              
58 32532         2203538 $i++ while $list->[$i]->{index} < $index;
59 32532 100 100     114848 $i-- if $i and $index != $list->[$i]->{index};
60              
61             # Exact match, just return current values
62 32532 100       61256 return @{ $list->[$i] }{qw/L M S/} if $index == $list->[$i]->{index};
  32531         103889  
63              
64             # Between two indices; return interpolated values
65 1         2 my ( $lo_i, $lo_l, $lo_m, $lo_s ) = @{ $list->[$i] }{qw/index L M S/};
  1         2  
66 1         3 my ( $hi_i, $hi_l, $hi_m, $hi_s ) = @{ $list->[ $i + 1 ] }{qw/index L M S/};
  1         3  
67 1         23 my $frac = ( $index - $lo_i ) / ( $hi_i - $lo_i );
68             return (
69 1         9 $lo_l + $frac * ( $hi_l - $lo_l ),
70             $lo_m + $frac * ( $hi_m - $lo_m ),
71             $lo_s + $frac * ( $hi_s - $lo_s )
72             );
73             }
74              
75             sub z_for_value {
76 16267     16267 1 17623 my ( $self, $value, $index ) = @_;
77 16267         25420 my ( $l, $m, $s ) = $self->lookup_LMS($index);
78              
79 16267 100       29293 return unless $m; # Off end of range
80              
81 16265 100       24387 if ($l) {
82 16264         119427 return ( ( $value / $m )**$l - 1 ) / ( $l * $s );
83             }
84             else {
85 1         15 return log( $value / $m ) / $s;
86             }
87             }
88              
89             sub pct_for_value {
90 16264     16264 1 6250686 my ( $self, $value, $index ) = @_;
91 16264         28867 return z_to_pct( $self->z_for_value( $value, $index ) );
92             }
93              
94             sub value_for_z {
95 16267     16267 1 1298283 my ( $self, $z_score, $index ) = @_;
96 16267         27891 my ( $l, $m, $s ) = $self->lookup_LMS($index);
97              
98 16267 100       29910 return unless $m; # Off end of range
99              
100 16265 100       23590 if ($l) {
101 16263         103686 return $m * ( 1 + $l * $s * $z_score )**( 1 / $l );
102             }
103             else {
104 2         29 return $m * exp( $s * $z_score );
105             }
106             }
107              
108             sub value_for_pct {
109 16264     16264 1 7443070 my ( $self, $pct, $index ) = @_;
110 16264         41695 $self->value_for_z( pct_to_z($pct), $index );
111             }
112              
113             1;
114              
115             __END__