holt_winters 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NWE3MWEzMWQ1MDFiOTRkYmMzMDM5ZWMzMjZiYTZlODMyYjI5NDdlNw==
5
+ data.tar.gz: !binary |-
6
+ ZmU3OWMxNWIwNTI4OWFmZTc5MzllZTBlODU3YTI4NzMwNzA0NWI3MQ==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ NzRlNjA0MmE4MjEzZjI0ZDg1ZTUyYTk0OTEzNTllODJmYzBlODY2NTE1OTlk
10
+ ZTgxY2JmNjQ5MjYzMWIyMmJkZDg3ZWE1MzkwOWE1NGM0M2E2MmNmZWMxZTk0
11
+ MmM2NWRjOWZhNGQyOGFjYmYxNjMxYTM5NjM0YWRjMGI4ZGI3MmY=
12
+ data.tar.gz: !binary |-
13
+ MjE2MzYwNGQxZjI4MTMxNDk4NDg0NTExMjg2ZDVmZTYyNmEzOGVjZTRmODcz
14
+ YmQzZmZmYjJjNWI2NzA0NmM2NDFkYTFiZjMyNmM5OTg5YzQ3YzY3MDJjNGVm
15
+ YWE0N2IxZjNjODc4ZjQzNmY1ZGExYjg0MzFkNzYxNDM1NjE4ZDI=
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ # -*- mode: ruby; -*-
2
+ source :rubygems
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ (The MIT-License)
2
+
3
+ Copyright (c) 2011 Brandon Keene
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,76 @@
1
+ # Holt-Winters Triple Exponential Smoothing Algorithm
2
+
3
+ A Ruby port of Nishant Chandra's
4
+ [Java implementation](http://n-chandra.blogspot.com/2011/04/holt-winters-triple-exponential.html)
5
+ of the Holt-Winters smoothing algorithm.
6
+
7
+ ![Algorithm](http://www.itl.nist.gov/div898/handbook/pmc/section4/eqns/ts26.gif)
8
+
9
+ The equations are intended to give more weight to recent observations and less weights to observations further in the past.
10
+ These weights are geometrically decreasing by a constant ratio.
11
+
12
+ # Usage
13
+
14
+ ## forecast()
15
+
16
+ It calculates the initial values and returns the forecast for __m__ periods.
17
+
18
+ # y Time series array
19
+ # alpha Level smoothing coefficient
20
+ # beta Trend smoothing coefficient (increasing beta tightens fit)
21
+ # gamma Seasonal smoothing coefficient
22
+ # period A complete season's data consists of L periods. And we need
23
+ # to estimate the trend factor from one period to the next. To
24
+ # accomplish this, it is advisable to use two complete seasons;
25
+ # that is, 2L periods.
26
+ # m Extrapolated future data points
27
+ # - 4 quarterly
28
+ # - 7 weekly
29
+ # - 12 monthly
30
+ def forecast(y, alpha, beta, gamma, period, m)
31
+ # ...
32
+ end
33
+
34
+
35
+ ## Example
36
+
37
+ This will generate a several variations of beta for a simple line:
38
+
39
+ require 'holt_winters'
40
+
41
+ x = (0..128).to_a
42
+ puts x.join(',')
43
+ puts HoltWinters.forecast(x, 0.5, 0, 0, 12, 2).join(',')
44
+ puts HoltWinters.forecast(x, 0.5, 0.25, 0, 12, 2).join(',')
45
+ puts HoltWinters.forecast(x, 0.5, 0.5, 0, 12, 2).join(',')
46
+ puts HoltWinters.forecast(x, 0.5, 0.75, 0, 12, 2).join(',')
47
+ puts HoltWinters.forecast(x, 0.5, 1.0, 0, 12, 2).join(',')
48
+
49
+ Try plotting the different lines to see how beta affects the forecast:
50
+
51
+ ![Chart](http://www.itl.nist.gov/div898/handbook/pmc/section4/gifs/tseries6.gif)
52
+
53
+ # License
54
+
55
+ (The MIT-License)
56
+
57
+ Copyright (c) 2011 Brandon Keene
58
+
59
+ Permission is hereby granted, free of charge, to any person obtaining
60
+ a copy of this software and associated documentation files (the
61
+ "Software"), to deal in the Software without restriction, including
62
+ without limitation the rights to use, copy, modify, merge, publish,
63
+ distribute, sublicense, and/or sell copies of the Software, and to
64
+ permit persons to whom the Software is furnished to do so, subject to
65
+ the following conditions:
66
+
67
+ The above copyright notice and this permission notice shall be
68
+ included in all copies or substantial portions of the Software.
69
+
70
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
71
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
72
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
73
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
74
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
75
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
76
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,244 @@
1
+ package com.fr.tsa;
2
+
3
+ /**
4
+ * Copyright 2011 Nishant Chandra
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+
19
+ /**
20
+ * Given a time series, say a complete monthly data for 12 months, the Holt-Winters smoothing and forecasting
21
+ * technique is built on the following formulae (multiplicative version):
22
+ *
23
+ * St[i] = alpha * y[i] / It[i - period] + (1.0 - alpha) * (St[i - 1] + Bt[i - 1])
24
+ * Bt[i] = gamma * (St[i] - St[i - 1]) + (1 - gamma) * Bt[i - 1]
25
+ * It[i] = beta * y[i] / St[i] + (1.0 - beta) * It[i - period]
26
+ * Ft[i + m] = (St[i] + (m * Bt[i])) * It[i - period + m]
27
+ *
28
+ * Note: Many authors suggest calculating initial values of St, Bt and It in a variety of ways, but
29
+ * some of them are incorrect e.g. determination of It parameter using regression. I have used
30
+ * the NIST recommended methods.
31
+ *
32
+ * For more details, see:
33
+ * http://adorio-research.org/wordpress/?p=1230
34
+ * http://www.itl.nist.gov/div898/handbook/pmc/section4/pmc435.htm
35
+ *
36
+ * @author Nishant Chandra
37
+ *
38
+ */
39
+ public class HoltWintersTripleExponentialImpl {
40
+
41
+ /**
42
+ * This method is the entry point. It calculates the initial values and returns the forecast
43
+ * for the m periods.
44
+ *
45
+ * @param y - Time series data.
46
+ * @param alpha - Exponential smoothing coefficients for level, trend, seasonal components.
47
+ * @param beta - Exponential smoothing coefficients for level, trend, seasonal components.
48
+ * @param gamma - Exponential smoothing coefficients for level, trend, seasonal components.
49
+ * @param perdiod - A complete season's data consists of L periods. And we need to estimate
50
+ * the trend factor from one period to the next. To accomplish this, it is advisable to use
51
+ * two complete seasons; that is, 2L periods.
52
+ * @param m - Extrapolated future data points.
53
+ * @param debug - Print debug values. Useful for testing.
54
+ *
55
+ * 4 quarterly
56
+ * 7 weekly.
57
+ * 12 monthly
58
+ */
59
+ public static double[] forecast(int[] y, double alpha, double beta,
60
+ double gamma, int period, int m, boolean debug) {
61
+
62
+ if (y == null) {
63
+ return null;
64
+ }
65
+
66
+ int seasons = y.length / period;
67
+ double a0 = calculateInitialLevel(y, period);
68
+ double b0 = calculateInitialTrend(y, period);
69
+ double[] initialSeasonalIndices = calculateSeasonalIndices(y, period, seasons);
70
+
71
+ if (debug) {
72
+ System.out.println(String.format(
73
+ "Total observations: %d, Seasons %d, Periods %d", y.length,
74
+ seasons, period));
75
+ System.out.println("Initial level value a0: " + a0);
76
+ System.out.println("Initial trend value b0: " + b0);
77
+ printArray("Seasonal Indices: ", initialSeasonalIndices);
78
+ }
79
+
80
+ double[] forecast = calculateHoltWinters(y, a0, b0, alpha, beta, gamma,
81
+ initialSeasonalIndices, period, m, debug);
82
+
83
+ if (debug) {
84
+ printArray("Forecast", forecast);
85
+ }
86
+
87
+ return forecast;
88
+ }
89
+
90
+ /**
91
+ * This method realizes the Holt-Winters equations.
92
+ *
93
+ * @param y
94
+ * @param a0
95
+ * @param b0
96
+ * @param alpha
97
+ * @param beta
98
+ * @param gamma
99
+ * @param initialSeasonalIndices
100
+ * @param period
101
+ * @param m
102
+ * @param debug
103
+ * @return - Forecast for m periods.
104
+ */
105
+ private static double[] calculateHoltWinters(int[] y, double a0, double b0, double alpha,
106
+ double beta, double gamma, double[] initialSeasonalIndices, int period, int m, boolean debug) {
107
+
108
+ double[] St = new double[y.length];
109
+ double[] Bt = new double[y.length];
110
+ double[] It = new double[y.length];
111
+ double[] Ft = new double[y.length + m];
112
+
113
+ //Initialize base values
114
+ St[1] = a0;
115
+ Bt[1] = b0;
116
+
117
+ for (int i = 0; i < period; i++) {
118
+ It[i] = initialSeasonalIndices[i];
119
+ }
120
+
121
+ Ft[m] = (St[0] + (m * Bt[0])) * It[0];//This is actually 0 since Bt[0] = 0
122
+ Ft[m + 1] = (St[1] + (m * Bt[1])) * It[1];//Forecast starts from period + 2
123
+
124
+ //Start calculations
125
+ for (int i = 2; i < y.length; i++) {
126
+
127
+ //Calculate overall smoothing
128
+ if((i - period) >= 0) {
129
+ St[i] = alpha * y[i] / It[i - period] + (1.0 - alpha) * (St[i - 1] + Bt[i - 1]);
130
+ } else {
131
+ St[i] = alpha * y[i] + (1.0 - alpha) * (St[i - 1] + Bt[i - 1]);
132
+ }
133
+
134
+ //Calculate trend smoothing
135
+ Bt[i] = gamma * (St[i] - St[i - 1]) + (1 - gamma) * Bt[i - 1];
136
+
137
+ //Calculate seasonal smoothing
138
+ if((i - period) >= 0) {
139
+ It[i] = beta * y[i] / St[i] + (1.0 - beta) * It[i - period];
140
+ }
141
+
142
+ //Calculate forecast
143
+ if( ((i + m) >= period) ){
144
+ Ft[i + m] = (St[i] + (m * Bt[i])) * It[i - period + m];
145
+ }
146
+
147
+ if(debug){
148
+ System.out.println(String.format(
149
+ "i = %d, y = %d, S = %f, Bt = %f, It = %f, F = %f", i,
150
+ y[i], St[i], Bt[i], It[i], Ft[i]));
151
+ }
152
+ }
153
+
154
+ return Ft;
155
+ }
156
+
157
+ /**
158
+ * See: http://robjhyndman.com/researchtips/hw-initialization/
159
+ * 1st period's average can be taken. But y[0] works better.
160
+ *
161
+ * @return - Initial Level value i.e. St[1]
162
+ */
163
+ private static double calculateInitialLevel(int[] y, int period) {
164
+
165
+ /**
166
+ double sum = 0;
167
+
168
+ for (int i = 0; i < period; i++) {
169
+ sum += y[i];
170
+ }
171
+
172
+ return sum / period;
173
+ **/
174
+ return y[0];
175
+ }
176
+
177
+ /**
178
+ * See: http://www.itl.nist.gov/div898/handbook/pmc/section4/pmc435.htm
179
+ *
180
+ * @return - Initial trend - Bt[1]
181
+ */
182
+ private static double calculateInitialTrend(int[] y, int period){
183
+
184
+ double sum = 0;
185
+
186
+ for (int i = 0; i < period; i++) {
187
+ sum += (y[period + i] - y[i]);
188
+ }
189
+
190
+ return sum / (period * period);
191
+ }
192
+
193
+ /**
194
+ * See: http://www.itl.nist.gov/div898/handbook/pmc/section4/pmc435.htm
195
+ *
196
+ * @return - Seasonal Indices.
197
+ */
198
+ private static double[] calculateSeasonalIndices(int[] y, int period, int seasons){
199
+
200
+ double[] seasonalAverage = new double[seasons];
201
+ double[] seasonalIndices = new double[period];
202
+
203
+ double[] averagedObservations = new double[y.length];
204
+
205
+ for (int i = 0; i < seasons; i++) {
206
+ for (int j = 0; j < period; j++) {
207
+ seasonalAverage[i] += y[(i * period) + j];
208
+ }
209
+ seasonalAverage[i] /= period;
210
+ }
211
+
212
+ for (int i = 0; i < seasons; i++) {
213
+ for (int j = 0; j < period; j++) {
214
+ averagedObservations[(i * period) + j] = y[(i * period) + j] / seasonalAverage[i];
215
+ }
216
+ }
217
+
218
+ for (int i = 0; i < period; i++) {
219
+ for (int j = 0; j < seasons; j++) {
220
+ seasonalIndices[i] += averagedObservations[(j * period) + i];
221
+ }
222
+ seasonalIndices[i] /= seasons;
223
+ }
224
+
225
+ return seasonalIndices;
226
+ }
227
+
228
+ /**
229
+ * Utility method to pring array values.
230
+ *
231
+ * @param description
232
+ * @param data
233
+ */
234
+ private static void printArray(String description, double[] data){
235
+
236
+ System.out.println(String.format("******************* %s *********************", description));
237
+
238
+ for (int i = 0; i < data.length; i++) {
239
+ System.out.println(data[i]);
240
+ }
241
+
242
+ System.out.println(String.format("*****************************************************************", description));
243
+ }
244
+ }
@@ -0,0 +1,148 @@
1
+ #include <string.h>
2
+ #include <stdlib.h>
3
+ #include <stdio.h>
4
+
5
+ /*
6
+ * Based on th R implementation
7
+ *
8
+ * a: level component
9
+ * b: trend component
10
+ * s: seasonal component
11
+ *
12
+ * Additive:
13
+ *
14
+ * Yhat[t+h] = a[t] + h * b[t] + s[t + 1 + (h - 1) mod p],
15
+ * a[t] = α (Y[t] - s[t-p]) + (1-α) (a[t-1] + b[t-1])
16
+ * b[t] = β (a[t] - a[t-1]) + (1-β) b[t-1]
17
+ * s[t] = γ (Y[t] - a[t]) + (1-γ) s[t-p]
18
+ *
19
+ * Multiplicative:
20
+ *
21
+ * Yhat[t+h] = (a[t] + h * b[t]) * s[t + 1 + (h - 1) mod p],
22
+ * a[t] = α (Y[t] / s[t-p]) + (1-α) (a[t-1] + b[t-1])
23
+ * b[t] = β (a[t] - a[t-1]) + (1-β) b[t-1]
24
+ * s[t] = γ (Y[t] / a[t]) + (1-γ) s[t-p]
25
+ */
26
+ void HoltWinters (
27
+ double *x,
28
+ int *xl, // Time t + h
29
+ double *alpha, // alpha parameter of Holt-Winters Filter.
30
+ double *beta, // beta parameter of Holt-Winters Filter. If set to 0, the function will do exponential smoothing.
31
+ double *gamma, // gamma parameter used for the seasonal component. If set to 0, an non-seasonal model is fitted.
32
+ int *start_time, // Time t
33
+ int *seasonal,
34
+ int *period,
35
+ double *a, // Start value for level (a[0]).
36
+ double *b, // Start value for trend (b[0]).
37
+ double *s, // Vector of start values for the seasonal component (s_1[0] ... s_p[0])
38
+
39
+ /* return values */
40
+ double *SSE, // The final sum of squared errors achieved in optimizing
41
+ double *level, // Estimated values for the level component (size xl - t + 1)
42
+ double *trend, // Estimated values for the trend component (size xl - t + 1)
43
+ double *season // Estimated values for the seasonal component (size xl - t + 1)
44
+ )
45
+
46
+ {
47
+ double res = 0, xhat = 0, stmp = 0;
48
+ int i, i0, s0;
49
+
50
+ /* copy start values to the beginning of the vectors */
51
+ level[0] = *a;
52
+ if (*beta > 0) trend[0] = *b;
53
+ if (*gamma > 0) memcpy(season, s, *period * sizeof(double));
54
+
55
+ for (i = *start_time - 1; i < *xl; i++) {
56
+ /* indices for period i */
57
+ i0 = i - *start_time + 2;
58
+ s0 = i0 + *period - 1;
59
+
60
+ /* forecast *for* period i */
61
+ xhat = level[i0 - 1] + (*beta > 0 ? trend[i0 - 1] : 0);
62
+ stmp = *gamma > 0 ? season[s0 - *period] : (*seasonal != 1);
63
+ if (*seasonal == 1)
64
+ xhat += stmp;
65
+ else
66
+ xhat *= stmp;
67
+
68
+ /* Sum of Squared Errors */
69
+ res = x[i] - xhat;
70
+ *SSE += res * res;
71
+
72
+ /* estimate of level *in* period i */
73
+ if (*seasonal == 1)
74
+ level[i0] = *alpha * (x[i] - stmp)
75
+ + (1 - *alpha) * (level[i0 - 1] + trend[i0 - 1]);
76
+ else
77
+ level[i0] = *alpha * (x[i] / stmp)
78
+ + (1 - *alpha) * (level[i0 - 1] + trend[i0 - 1]);
79
+
80
+ /* estimate of trend *in* period i */
81
+ if (*beta > 0)
82
+ trend[i0] = *beta * (level[i0] - level[i0 - 1])
83
+ + (1 - *beta) * trend[i0 - 1];
84
+
85
+ /* estimate of seasonal component *in* period i */
86
+ if (*gamma > 0) {
87
+ if (*seasonal == 1)
88
+ season[s0] = *gamma * (x[i] - level[i0])
89
+ + (1 - *gamma) * stmp;
90
+ else
91
+ season[s0] = *gamma * (x[i] / level[i0])
92
+ + (1 - *gamma) * stmp;
93
+ }
94
+ }
95
+ }
96
+
97
+ int main() {
98
+ // US population in millions
99
+ double series[] = {3.93, 5.31, 7.24, 9.64, 12.90, 17.10, 23.20, 31.40, 39.80, 50.20, 62.90, 76.00, 92.00, 105.70, 122.80, 131.70, 151.30, 179.30, 203.20};
100
+
101
+ int forecast = 19;
102
+ double alpha = 0.9999208;
103
+ double beta = 0;
104
+ double gamma = 0;
105
+ int start_time = 2;
106
+ int seasonal = 0;
107
+ int period = 0;
108
+ double a0 = series[0];
109
+ double b0 = 0;
110
+ double s[] = {};
111
+
112
+ double errors;
113
+ int nb_computations = forecast - start_time - 1;
114
+ double *estimated_level = malloc(nb_computations * sizeof(double));
115
+ double *estimated_trend = malloc(nb_computations * sizeof(double));
116
+ double *estimated_season = malloc(nb_computations * sizeof(double));
117
+
118
+ HoltWinters(
119
+ series,
120
+ &forecast,
121
+ &alpha,
122
+ &beta,
123
+ &gamma,
124
+ &start_time,
125
+ &seasonal,
126
+ &period,
127
+ &a0,
128
+ &b0,
129
+ s,
130
+ &errors,
131
+ estimated_level,
132
+ estimated_trend,
133
+ estimated_season
134
+ );
135
+
136
+ int i = 0;
137
+ int first_year = 1800;
138
+ printf("Estimated:\n");
139
+ for (i = 0; i < nb_computations; i++) {
140
+ printf("\tyear = %d, level: %f, trend: %f\n", first_year + i * 10, estimated_level[i], estimated_trend[i]);
141
+ }
142
+
143
+ free(estimated_level);
144
+ free(estimated_trend);
145
+ free(estimated_season);
146
+
147
+ return 0;
148
+ }
@@ -0,0 +1,93 @@
1
+ /* R : A Computer Language for Statistical Data Analysis
2
+ *
3
+ * Copyright (C) 2003-7 The R Development Core Team
4
+ *
5
+ * This program is free software; you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation; either version 2 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program; if not, a copy is available at
17
+ * http://www.r-project.org/Licenses/.
18
+ */
19
+
20
+ #include <R.h>
21
+ #include "ts.h"
22
+ #include <stdlib.h>
23
+
24
+ void HoltWinters (
25
+ double *x,
26
+ int *xl,
27
+ double *alpha,
28
+ double *beta,
29
+ double *gamma,
30
+ int *start_time,
31
+ int *seasonal,
32
+ int *period,
33
+ double *a,
34
+ double *b,
35
+ double *s,
36
+
37
+ /* return values */
38
+ double *SSE,
39
+ double *level,
40
+ double *trend,
41
+ double *season
42
+ )
43
+
44
+ {
45
+ double res = 0, xhat = 0, stmp = 0;
46
+ int i, i0, s0;
47
+
48
+ /* copy start values to the beginning of the vectors */
49
+ level[0] = *a;
50
+ if (*beta > 0) trend[0] = *b;
51
+ if (*gamma > 0) memcpy(season, s, *period * sizeof(double));
52
+
53
+ for (i = *start_time - 1; i < *xl; i++) {
54
+ /* indices for period i */
55
+ i0 = i - *start_time + 2;
56
+ s0 = i0 + *period - 1;
57
+
58
+ /* forecast *for* period i */
59
+ xhat = level[i0 - 1] + (*beta > 0 ? trend[i0 - 1] : 0);
60
+ stmp = *gamma > 0 ? season[s0 - *period] : (*seasonal != 1);
61
+ if (*seasonal == 1)
62
+ xhat += stmp;
63
+ else
64
+ xhat *= stmp;
65
+
66
+ /* Sum of Squared Errors */
67
+ res = x[i] - xhat;
68
+ *SSE += res * res;
69
+
70
+ /* estimate of level *in* period i */
71
+ if (*seasonal == 1)
72
+ level[i0] = *alpha * (x[i] - stmp)
73
+ + (1 - *alpha) * (level[i0 - 1] + trend[i0 - 1]);
74
+ else
75
+ level[i0] = *alpha * (x[i] / stmp)
76
+ + (1 - *alpha) * (level[i0 - 1] + trend[i0 - 1]);
77
+
78
+ /* estimate of trend *in* period i */
79
+ if (*beta > 0)
80
+ trend[i0] = *beta * (level[i0] - level[i0 - 1])
81
+ + (1 - *beta) * trend[i0 - 1];
82
+
83
+ /* estimate of seasonal component *in* period i */
84
+ if (*gamma > 0) {
85
+ if (*seasonal == 1)
86
+ season[s0] = *gamma * (x[i] - level[i0])
87
+ + (1 - *gamma) * stmp;
88
+ else
89
+ season[s0] = *gamma * (x[i] / level[i0])
90
+ + (1 - *gamma) * stmp;
91
+ }
92
+ }
93
+ }
@@ -0,0 +1,22 @@
1
+ # -*- mode: ruby; encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "holt_winters/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "holt_winters"
7
+ s.version = HoltWinters::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Brandon Keene"]
10
+ s.email = ["bkeene@gmail.com"]
11
+ s.homepage = ""
12
+ s.summary = %q{Holt-Winters Triple Exponential Smoothing}
13
+
14
+ s.rubyforge_project = "holt_winters"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_development_dependency "rspec", "~> 2.6.0"
22
+ end
@@ -0,0 +1,135 @@
1
+ # Given a time series, say a complete monthly data for 12 months, the Holt-Winters smoothing and forecasting
2
+ # technique is built on the following formulae (multiplicative version):
3
+ #
4
+ # St[i] = alpha * y[i] / It[i - period] + (1.0 - alpha) * (St[i - 1] + Bt[i - 1])
5
+ # Bt[i] = gamma * (St[i] - St[i - 1]) + (1 - gamma) * Bt[i - 1]
6
+ # It[i] = beta * y[i] / St[i] + (1.0 - beta) * It[i - period]
7
+ # Ft[i + m] = (St[i] + (m * Bt[i])) * It[i - period + m]
8
+ #
9
+ # Note: Many authors suggest calculating initial values of St, Bt and It in a variety of ways, but
10
+ # some of them are incorrect e.g. determination of It parameter using regression. I have used
11
+ # the NIST recommended methods.
12
+ #
13
+ # For more details, see:
14
+ # http://adorio-research.org/wordpress/?p=1230
15
+ # http://www.itl.nist.gov/div898/handbook/pmc/section4/pmc435.htm
16
+ #
17
+ module HoltWinters
18
+ class << self
19
+ # Calculate initial values and return the forecast for m periods.
20
+ #
21
+ # y Time series array
22
+ # alpha Level smoothing coefficient
23
+ # beta Trend smoothing coefficient (increasing beta tightens fit)
24
+ # gamma Seasonal smoothing coefficient
25
+ # period A complete season's data consists of L periods. And we need
26
+ # to estimate the trend factor from one period to the next. To
27
+ # accomplish this, it is advisable to use two complete seasons;
28
+ # that is, 2L periods.
29
+ # m Extrapolated future data points
30
+ # - 4 quarterly
31
+ # - 7 weekly
32
+ # - 12 monthly
33
+ #
34
+ def forecast(y, alpha, beta, gamma, period, m)
35
+ return nil if y.empty?
36
+
37
+ seasons = y.size / period
38
+ a0 = initial_level(y, period)
39
+ b0 = initial_trend(y, period)
40
+
41
+ seasonal = seasonal_indicies(y, period, seasons)
42
+
43
+ holt_winters(y, a0, b0, alpha, beta, gamma, seasonal, period, m);
44
+ end
45
+
46
+ def holt_winters(y, a0, b0, alpha, beta, gamma, seasonal, period, m)
47
+ st = Array.new(y.length, 0.0)
48
+ bt = Array.new(y.length, 0.0)
49
+ it = Array.new(y.length, 0.0)
50
+ ft = Array.new(y.length + m, 0.0)
51
+
52
+ st[1] = a0
53
+ bt[1] = b0
54
+
55
+ (0..period - 1).each do |i|
56
+ it[i] = seasonal[i]
57
+ end
58
+
59
+ ft[m] = (st[0] + (m * bt[0])) * it[0] # This is actually 0 since bt[0] = 0
60
+ ft[m + 1] = (st[1] + (m * bt[1])) * it[1] # Forecast starts from period + 2
61
+
62
+ (2..(y.size - 1)).each do |i|
63
+ # Calculate overall smoothing
64
+ if (i - period) >= 0
65
+ st[i] = alpha * y[i] / it[i - period] + (1.0 - alpha) * (st[i - 1] + bt[i - 1])
66
+ else
67
+ st[i] = alpha * y[i] + (1.0 - alpha) * (st[i - 1] + bt[i - 1])
68
+ end
69
+
70
+ # Calculate trend smoothing
71
+ bt[i] = gamma * (st[i] - st[i - 1]) + (1 - gamma) * bt[i - 1]
72
+
73
+ # Calculate seasonal smoothing
74
+ if (i - period) >= 0
75
+ it[i] = beta * y[i] / st[i] + (1.0 - beta) * it[i - period]
76
+ end
77
+
78
+ # Calculate forecast
79
+ if (i + m) >= period
80
+ ft[i + m] = (st[i] + (m * bt[i])) * it[i - period + m]
81
+ end
82
+ end
83
+
84
+ ft
85
+ end
86
+
87
+ # See: http://robjhyndman.com/researchtips/hw-initialization/
88
+ # 1st period's average can be taken. But y[0] works better.
89
+ def initial_level(y, period)
90
+ y.first
91
+ end
92
+
93
+
94
+ # See: http://www.itl.nist.gov/div898/handbook/pmc/section4/pmc435.htm
95
+ def initial_trend(y, period)
96
+ sum = 0
97
+
98
+ (0..period - 1).each do |i|
99
+ sum += (y[period + i] - y[i])
100
+ end
101
+
102
+ sum / (period * period)
103
+ end
104
+
105
+
106
+ # See: http://www.itl.nist.gov/div898/handbook/pmc/section4/pmc435.htm
107
+ def seasonal_indicies(y, period, seasons)
108
+ seasonal_average = Array.new(seasons, 0.0)
109
+ seasonal_indices = Array.new(period, 0.0)
110
+ averaged_observations = Array.new(y.size, 0.0)
111
+
112
+ (0..seasons - 1).each do |i|
113
+ (0..period - 1).each do |j|
114
+ seasonal_average[i] += y[(i * period) + j]
115
+ end
116
+ seasonal_average[i] /= period
117
+ end
118
+
119
+ (0..seasons - 1).each do |i|
120
+ (0..period - 1).each do |j|
121
+ averaged_observations[(i * period) + j] = y[(i * period) + j] / seasonal_average[i]
122
+ end
123
+ end
124
+
125
+ (0..period - 1).each do |i|
126
+ (0..seasons - 1).each do |j|
127
+ seasonal_indices[i] += averaged_observations[(j * period) + i]
128
+ end
129
+ seasonal_indices[i] /= seasons
130
+ end
131
+
132
+ seasonal_indices
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,3 @@
1
+ module HoltWinters
2
+ VERSION = "0.0.0"
3
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: holt_winters
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Brandon Keene
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-10-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 2.6.0
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 2.6.0
27
+ description:
28
+ email:
29
+ - bkeene@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - Gemfile
35
+ - LICENSE
36
+ - README.md
37
+ - ext/holtWinters.java
38
+ - ext/holt_winters.c
39
+ - ext/holt_winters_R_language.c
40
+ - holt_winters.gemspec
41
+ - lib/holt_winters.rb
42
+ - lib/holt_winters/version.rb
43
+ homepage: ''
44
+ licenses: []
45
+ metadata: {}
46
+ post_install_message:
47
+ rdoc_options: []
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ! '>='
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ requirements: []
61
+ rubyforge_project: holt_winters
62
+ rubygems_version: 2.0.7
63
+ signing_key:
64
+ specification_version: 4
65
+ summary: Holt-Winters Triple Exponential Smoothing
66
+ test_files: []