Estimating Holt-Winter Smoothing Coefficients in Java

I am working on a system written in Java that can make forecasts using historical data. The algorithm used is the Java port, this implementation of Holt-Winters (multiplicative seasonality).

I have several time series that we would like to analyze, and we need different smoothing factors for these time series. At the moment, the algorithm works very well, the only problem is how to determine the most reasonable values โ€‹โ€‹of the smoothing coefficients (alpha, beta, gamma).

I know that I need some kind of non-linear optimization, but I'm not a mathematician at all, so I lost a little among all these theories and concepts.

EDIT

I have many different time series for analysis, I would like to know if there is a standard / good enough method (the library will be better) for calculating the smoothing parameters that I have to give to the Holt-Zithers algorithm.

+4
source share
2 answers

Have you watched JMulTi ?

This SO question can be very important to you.

Anyone looking at Holt Winters should definitely check out Prof. Hyndman website . He is an expert in the field and also the creator of the forecast() library in R.

You said you want to better understand this technique. The good news is that Hyndman is writing a tutorial that is available to us for free. A specific chapter on Holt Winters: http://otexts.com/fpp/7/5/

I know that you wanted it in Java, but if at all R is an option, you should try it. (Some people recommend writing from R and reading it in your Java program.)

UPDATE:

If these are the initial HW parameters you are worried about, I can only think of the ets package, which implements a maximum likelihood search to get the parameters. If you have not found any Java implementation, it is best to use JRI (rJava) and call ets or HoltWinters from within that.

Hope this helps.

+3
source

You can use the Nelder Stream Optimizer implemented by Apache (SimplexOptimizer)

 double[] dataPoints = { 141, 53, 78, 137, 182, 161, 177, 164, 70, 67, 129, 187, 161, 136, 167, 57, 61, 159, 158, 152, 169, 181, 65, 60, 146, 186, 180, 181, 167, 70, 62, 170, 193, 167, 176, 149, 69, 68, 168, 181, 200, 179, 181, 83, 72, 157, 188, 193, 173, 184, 61, 59, 158, 158, 143, 208, 172, 82, 86, 158, 194, 193, 159 }; NelderMeadOptimizer.Parameters op = NelderMeadOptimizer.optimize(dataPoints, 7); op.getAlpha(); op.getBeta(); op.getGamma(); 

Now you need NelderMeadOptimizer:

 import org.apache.commons.math3.analysis.MultivariateFunction; import org.apache.commons.math3.optim.InitialGuess; import org.apache.commons.math3.optim.MaxEval; import org.apache.commons.math3.optim.MaxIter; import org.apache.commons.math3.optim.PointValuePair; import org.apache.commons.math3.optim.nonlinear.scalar.GoalType; import org.apache.commons.math3.optim.nonlinear.scalar.MultivariateFunctionMappingAdapter; import org.apache.commons.math3.optim.nonlinear.scalar.ObjectiveFunction; import org.apache.commons.math3.optim.nonlinear.scalar.noderiv.NelderMeadSimplex; import org.apache.commons.math3.optim.nonlinear.scalar.noderiv.SimplexOptimizer; import java.util.List; public class NelderMeadOptimizer { // configuration private static final double minValueForOptimizedParameters = 0.001; private static final double maxValueForOptimizedParameters = 0.99; private static final double simplexRelativeThreshold = 0.0001; private static final double simplexAbsoluteThreshold = 0.0001; private static final double DEFAULT_LEVEL_SMOOTHING = 0.01; private static final double DEFAULT_TREND_SMOOTHING = 0.01; private static final double DEFAULT_SEASONAL_SMOOTHING = 0.01; private static final int MAX_ALLOWED_NUMBER_OF_ITERATION = 1000; private static final int MAX_ALLOWED_NUMBER_OF_EVALUATION = 1000; /** * * @param dataPoints the observed data points * @param seasonLength the amount of data points per season * @return the optimized parameters */ public static Parameters optimize(double[] dataPoints, int seasonLength) { MultivariateFunctionMappingAdapter costFunc = getCostFunction(dataPoints, seasonLength); double[] initialGuess = getInitialGuess(dataPoints, seasonLength); double[] optimizedValues = optimize(initialGuess, costFunc); double alpha = optimizedValues[0]; double beta = optimizedValues[1]; double gamma = optimizedValues[2]; return new Parameters(alpha, beta, gamma); } /** * Optimizes parameters using the Nelder-Mead Method * @param initialGuess initial guess / state required for Nelder-Mead-Method * @param costFunction which defines that the Mean Squared Error has to be minimized * @return the optimized values */ private static double[] optimize(double[] initialGuess, MultivariateFunctionMappingAdapter costFunction) { double[] result; SimplexOptimizer optimizer = new SimplexOptimizer(simplexRelativeThreshold, simplexAbsoluteThreshold); PointValuePair unBoundedResult = optimizer.optimize( GoalType.MINIMIZE, new MaxIter(MAX_ALLOWED_NUMBER_OF_ITERATION), new MaxEval(MAX_ALLOWED_NUMBER_OF_EVALUATION), new InitialGuess(initialGuess), new ObjectiveFunction(costFunction), new NelderMeadSimplex(initialGuess.length)); result = costFunction.unboundedToBounded(unBoundedResult.getPoint()); return result; } /** * Defines that the Mean Squared Error has to be minimized * in order to get optimized / good parameters for alpha, betta and gamma. * It also defines the minimum and maximum values for the parameters to optimize. * @param dataPoints the data points * @param seasonLength the amount of data points per season * @return a cost function {@link MultivariateFunctionMappingAdapter} which * defines that the Mean Squared Error has to be minimized * in order to get optimized / good parameters for alpha, betta and gamma */ private static MultivariateFunctionMappingAdapter getCostFunction(final double[] dataPoints, final int seasonLength) { MultivariateFunction multivariateFunction = new MultivariateFunction() { @Override public double value(double[] point) { double alpha = point[0]; double beta = point[1]; double gamma = point[2]; if (beta >= alpha) { return Double.POSITIVE_INFINITY; } List<Double> predictedValues = TripleExponentialSmoothing.getSmoothedDataPointsWithPredictions(dataPoints, seasonLength, alpha, beta, gamma, 1); predictedValues.remove(predictedValues.size()-1); double meanSquaredError = getMeanSquaredError(dataPoints, predictedValues); return meanSquaredError; } }; double[][] minMax = getMinMaxValues(); return new MultivariateFunctionMappingAdapter(multivariateFunction, minMax[0], minMax[1]); } /** * Generates an initial guess/state required for Nelder-Mead-Method. * @param dataPoints the data points * @param seasonLength the amount of data points per season * @return array containing initial guess/state required for Nelder-Mead-Method */ public static double[] getInitialGuess(double[] dataPoints, int seasonLength){ double[] initialGuess = new double[3]; initialGuess[0] = DEFAULT_LEVEL_SMOOTHING; initialGuess[1] = DEFAULT_TREND_SMOOTHING; initialGuess[2] = DEFAULT_SEASONAL_SMOOTHING; return initialGuess; } /** * Get minimum and maximum values for the parameters alpha (level coefficient), * beta (trend coefficient) and gamma (seasonality coefficient) * @return array containing all minimum and maximum values for the parameters alpha, beta and gamma */ private static double[][] getMinMaxValues() { double[] min = new double[3]; double[] max = new double[3]; min[0] = minValueForOptimizedParameters; min[1] = minValueForOptimizedParameters; min[2] = minValueForOptimizedParameters; max[0] = maxValueForOptimizedParameters; max[1] = maxValueForOptimizedParameters; max[2] = maxValueForOptimizedParameters; return new double[][]{min, max}; } /** * Compares observed data points from the past and predicted data points * in order to calculate the Mean Squared Error (MSE) * @param observedData the observed data points from the past * @param predictedData the predicted data points * @return the Mean Squared Error (MSE) */ public static double getMeanSquaredError(double[] observedData, List<Double> predictedData){ double sum = 0; for(int i=0; i<observedData.length; i++){ double error = observedData[i] - predictedData.get(i); double sumSquaredError = error * error; // SSE sum += sumSquaredError; } return sum / observedData.length; } /** * Holds the parameters alpha (level coefficient), beta (trend coefficient) * and gamma (seasonality coefficient) for Triple Exponential Smoothing. */ public static class Parameters { public final double alpha; public final double beta; public final double gamma; public Parameters(double alpha, double beta, double gamma) { this.alpha = alpha; this.beta = beta; this.gamma = gamma; } public double getAlpha() { return alpha; } public double getBeta() { return beta; } public double getGamma() { return gamma; } }; } 
+1
source

Source: https://habr.com/ru/post/1435963/


All Articles