holt_winters 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: []