File Coverage

blib/lib/Math/HoltWinters.pm
Criterion Covered Total %
statement 9 80 11.2
branch 0 36 0.0
condition n/a
subroutine 3 11 27.2
pod 0 4 0.0
total 12 131 9.1


line stmt bran cond sub pod time code
1              
2             package Math::HoltWinters;
3              
4             # Time series smoothing and forecasting with Holt-Winters exponential smoothing
5              
6 1     1   26735 use 5.010001;
  1         4  
  1         37  
7 1     1   4 use strict;
  1         2  
  1         29  
8 1     1   4 use warnings;
  1         5  
  1         728  
9              
10             require Exporter;
11              
12             our @ISA = qw(Exporter);
13              
14             our $VERSION = '0.03';
15              
16             # ============================================================
17              
18             sub single {
19 0     0 0   my ( $alpha, $x0 ) = @_; # initial point, optional
20              
21             return sub {
22 0     0     my ( $x ) = @_; # $x is optional (forecast if undef)
23              
24 0 0         unless( defined $x0 ) { # initialize $x0 if not done yet
25 0           $x0 = $x;
26 0           return $x0;
27             }
28              
29 0 0         if( defined $x ) { # only update for new data, no-op for forecast
30 0           $x0 = $alpha*$x + (1-$alpha)*$x0
31             }
32              
33 0           return $x0;
34             }
35 0           }
36              
37             sub double {
38 0     0 0   my ( $alpha, $beta, $x0, $t0 ) = @_; # initial point and trend, both optional
39              
40             return sub {
41 0     0     my ( $x ) = @_;
42              
43             # first point: $x0 not defined
44 0 0         unless( defined $x0 ) {
45 0           $x0 = $x;
46 0           return $x0;
47             }
48              
49             # second point: $x0 defined, $t0 not defined
50 0 0         unless( defined $t0 ) {
51 0           $t0 = $x - $x0;
52 0           $x0 = $alpha*$x + (1-$alpha)*$x0; # sgl exponential smoothing!
53 0           return $x0;
54             }
55              
56             # other points: $0, $t0, $x all defined
57 0 0         if( defined $x ) {
58 0           my $old = $x0;
59 0           $x0 = $alpha*$x + (1-$alpha)*($old + $t0);
60 0           $t0 = $beta*($x0-$old) + (1-$beta)*$t0;
61 0           return $x0;
62             }
63              
64             # forecast: $x not defined
65 0 0         if( !defined $x ) {
66 0           $x0 += $t0;
67 0           return $x0;
68             }
69             }
70 0           }
71              
72             sub triple_add {
73 0     0 0   my $alpha = shift;
74 0           my $beta = shift;
75 0           my $gamma = shift;
76 0           my $season = pop; # seasonality information is alwas the LAST argument
77              
78 0           my ( $x0, $t0, @p );
79 0 0         $x0 = shift if @_;
80 0 0         $t0 = shift if @_;
81 0 0         @p = ref $season ? @$season : (0) x $season;
82              
83             # if( ref $season ) {
84             # @p = @$season; # perform deep copy
85             # } else {
86             # @p = (0) x $season;
87             # }
88              
89             return sub {
90 0     0     my ( $x ) = @_;
91              
92 0           my $p0 = shift @p;
93              
94 0 0         if( !defined $x0 ) { # First point
    0          
    0          
95 0           $x0 = $x;
96              
97             } elsif( !defined $t0 ) { # Second point
98 0           $t0 = $x - $x0;
99 0           $x0 = $alpha*$x + (1-$alpha)*$x0;
100              
101             } elsif( defined $x ) { # Smoothing
102 0           my $old = $x0;
103              
104 0           $x0 = $alpha*($x - $p0) + (1-$alpha)*($old + $t0);
105 0           $t0 = $beta*($x0 - $old) + (1-$beta)*$t0;
106 0           $p0 = $gamma*($x - $x0) + (1-$gamma)*$p0;
107              
108             } else { # Forecasting
109 0           $x0 += $t0;
110             }
111 0           push @p, $p0;
112              
113 0           return $x0 + $p0;
114             }
115 0           }
116              
117             sub triple_mul {
118 0     0 0   my $alpha = shift;
119 0           my $beta = shift;
120 0           my $gamma = shift;
121 0           my $season = pop; # seasonality information is alwas the LAST argument
122              
123 0           my ( $x0, $t0, @p );
124 0 0         $x0 = shift if @_;
125 0 0         $t0 = shift if @_;
126 0 0         @p = ref $season ? @$season : (1) x $season;
127              
128              
129             return sub {
130 0     0     my ( $x ) = @_;
131              
132 0           my $p0 = shift @p;
133              
134 0 0         if( !defined $x0 ) { # First point
    0          
    0          
135 0           $x0 = $x;
136              
137             } elsif( !defined $t0 ) { # Second point
138 0           $t0 = $x - $x0;
139 0           $x0 = $alpha*$x + (1-$alpha)*$x0;
140              
141             } elsif( defined $x ) { # Smoothing
142 0           my $old = $x0;
143              
144             # >>> What happens if either $p0 or $x0 is every zero? <<<
145 0           $x0 = $alpha*$x/$p0 + (1-$alpha)*($old + $t0);
146 0           $t0 = $beta*($x0 - $old) + (1-$beta)*$t0;
147 0           $p0 = $gamma*$x/$x0 + (1-$gamma)*$p0;
148              
149             } else { # Forecasting
150 0           $x0 += $t0;
151             }
152 0           push @p, $p0;
153              
154 0           return $x0*$p0;
155             }
156 0           }
157              
158             1;
159              
160             __END__