📄 doubleexponentialsmoothingmodel.java
字号:
* @param gammaMin the minimum value of the gamma (trend) smoothing * constant accepted in the resulting best fit model. Must be greater than * (or equal to) 0.0 and less than gammaMax. * @param gammaMax the maximum value of the gamma (trend) smoothing * constant accepted in the resulting best fit model. Must be greater than * gammaMin and less than (or equal to) 1.0. * @param gammaTolerance the tolerance within which the gamma value is * required. Must be considerably less than 1.0. However, note that the * smaller this value the longer it will take to diverge on a best fit * model. */ private static DoubleExponentialSmoothingModel findBestGamma( DataSet dataSet, double alpha, double gammaMin, double gammaMax, double gammaTolerance ) { int stepsPerIteration = 10; if ( gammaMin < 0.0 ) gammaMin = 0.0; if ( gammaMax > 1.0 ) gammaMax = 1.0; DoubleExponentialSmoothingModel bestModel = new DoubleExponentialSmoothingModel( alpha, gammaMin ); bestModel.init(dataSet); double initialMSE = bestModel.getMSE(); boolean gammaImproving = true; double gammaStep = (gammaMax-gammaMin)/stepsPerIteration; double gamma = gammaMin + gammaStep; for ( ; gamma<=gammaMax || gammaImproving; ) { DoubleExponentialSmoothingModel model = new DoubleExponentialSmoothingModel( alpha, gamma ); model.init( dataSet ); if ( model.getMSE() < bestModel.getMSE() ) bestModel = model; else gammaImproving = false; gamma += gammaStep; if ( gamma > 1.0 ) gammaImproving = false; } // If we're making progress, then try to refine the gamma estimate if ( bestModel.getMSE() < initialMSE && gammaStep > gammaTolerance ) { // Can this be further refined - improving efficiency - by // only searching in the range gamma-gammaStep/2 to // gamma+gammaStep/2 ? return findBestGamma( dataSet, bestModel.getAlpha(), bestModel.getGamma()-gammaStep, bestModel.getGamma()+gammaStep, gammaTolerance ); } return bestModel; } /** * Constructs a new double exponential smoothing forecasting model, using * the given smoothing constants - alpha and gamma. For a valid model to * be constructed, you should call init and pass in a data set containing * a series of data points with the time variable initialized to identify * the independent variable. * @param alpha the smoothing constant to use for this exponential * smoothing model. Must be a value in the range 0.0-1.0. * @param gamma the second smoothing constant, gamma to use in this model * to smooth the trend. Must be a value in the range 0.0-1.0. * @throws IllegalArgumentException if the value of either smoothing * constant is invalid - outside the range 0.0-1.0. */ public DoubleExponentialSmoothingModel( double alpha, double gamma ) { if ( alpha < 0.0 || alpha > 1.0 ) throw new IllegalArgumentException("DoubleExponentialSmoothingModel: Invalid smoothing constant, " + alpha + " - must be in the range 0.0-1.0."); if ( gamma < 0.0 || gamma > 1.0 ) throw new IllegalArgumentException("DoubleExponentialSmoothingModel: Invalid smoothing constant, gamma=" + gamma + " - must be in the range 0.0-1.0."); slopeValues = new DataSet(); this.alpha = alpha; this.gamma = gamma; } /** * Returns the forecast value of the dependent variable for the given * value of the (independent) time variable using a single exponential * smoothing model. See the class documentation for details on the * formulation used. * @param t the value of the time variable for which a forecast * value is required. * @return the forecast value of the dependent variable at time, t. * @throws IllegalArgumentException if there is insufficient historical * data - observations passed to init - to generate a forecast for the * given time value. */ protected double forecast( double t ) throws IllegalArgumentException { double previousTime = t - getTimeInterval(); // As a starting point, we set the first forecast value to be // the same as the observed value if ( previousTime < getMinimumTimeValue()+TOLERANCE ) return getObservedValue( t ); try { double b = getSlope( previousTime ); double forecast = alpha*getObservedValue(t) + (1.0-alpha)*(getForecastValue(previousTime)+b); return forecast; } catch ( IllegalArgumentException iaex ) { double maxTimeValue = getMaximumTimeValue(); double b = getSlope( maxTimeValue-getTimeInterval() ); double forecast = getForecastValue(maxTimeValue) + (t-maxTimeValue)*b; return forecast; } } /** * Calculates and returns the slope for the given time period. Except * for the initial periods - where forecasts are not available - the * slope is calculated using forecast values, and not observed values. * See the class documentation for details on the formulation used. * @param time the time value for which the slope is required. * @return the slope of the data at the given period of time. * @param IllegalArgumentException if the slope cannot be determined for * the given time period. */ private double getSlope( double time ) throws IllegalArgumentException { // TODO: Optimize this search by having data set sorted by time // Search for previously calculated - and saved - slope value String timeVariable = getTimeVariable(); Iterator it = slopeValues.iterator(); while ( it.hasNext() ) { DataPoint dp = (DataPoint)it.next(); double dpTimeValue = dp.getIndependentValue( timeVariable ); if ( Math.abs(time-dpTimeValue) < TOLERANCE ) return dp.getDependentValue(); } // Saved slope not found, so calculate it // (and save it for future reference) double previousTime = time - getTimeInterval(); double slope = 0.0; // Initial condition for first periods if ( previousTime < getMinimumTimeValue()+TOLERANCE ) slope = getObservedValue(time)-getObservedValue(previousTime); else slope = gamma*(forecast(time)-forecast(previousTime)) + (1-gamma)*getSlope(previousTime); DataPoint dp = new Observation( slope ); dp.setIndependentValue( timeVariable, time ); slopeValues.add( dp ); return slope; } /** * Returns the current number of periods used in this model. This is also * the minimum number of periods required in order to produce a valid * forecast. Strictly speaking, for double exponential smoothing only two * previous periods are needed - though such a model would be of relatively * little use. At least ten to fifteen prior observations would be * preferred. * @return the minimum number of periods used in this model. */ protected int getNumberOfPeriods() { return 2; } /** * Since this version of double exponential smoothing uses the current * observation to calculate a smoothed value, we must override the * calculation of the accuracy indicators. * @param dataSet the DataSet to use to evaluate this model, and to * calculate the accuracy indicators against. */ protected void calculateAccuracyIndicators( DataSet dataSet ) { // Note that the model has been initialized initialized = true; // Reset various helper summations double sumErr = 0.0; double sumAbsErr = 0.0; double sumAbsPercentErr = 0.0; double sumErrSquared = 0.0; String timeVariable = getTimeVariable(); double timeDiff = getTimeInterval(); // Calculate the Sum of the Absolute Errors Iterator it = dataSet.iterator(); while ( it.hasNext() ) { // Get next data point DataPoint dp = (DataPoint)it.next(); double x = dp.getDependentValue(); double time = dp.getIndependentValue( timeVariable ); double previousTime = time - timeDiff; // Get next forecast value, using one-period-ahead forecast double forecastValue = getForecastValue( previousTime ) + getSlope( previousTime ); // Calculate error in forecast, and update sums appropriately double error = forecastValue - x; sumErr += error; sumAbsErr += Math.abs( error ); sumAbsPercentErr += Math.abs( error / x ); sumErrSquared += error*error; } // Initialize the accuracy indicators int n = dataSet.size(); accuracyIndicators.setBias( sumErr / n ); accuracyIndicators.setMAD( sumAbsErr / n ); accuracyIndicators.setMAPE( sumAbsPercentErr / n ); accuracyIndicators.setMSE( sumErrSquared / n ); accuracyIndicators.setSAE( sumAbsErr ); } /** * Returns the value of the smoothing constant, alpa, used in this model. * @return the value of the smoothing constant, alpha. * @see #getGamma */ public double getAlpha() { return alpha; } /** * Returns the value of the trend smoothing constant, gamma, used in this * model. * @return the value of the trend smoothing constant, gamma. * @see #getAlpha */ public double getGamma() { return gamma; } /** * Returns a one or two word name of this type of forecasting model. Keep * this short. A longer description should be implemented in the toString * method. * @return a string representation of the type of forecasting model * implemented. */ public String getForecastType() { return "double exponential smoothing"; } /** * This should be overridden to provide a textual description of the * current forecasting model including, where possible, any derived * parameters used. * @return a string representation of the current forecast model, and its * parameters. */ public String toString() { return "Double exponential smoothing model, with smoothing constants of alpha=" + alpha + ", gamma=" + gamma + ", and using an independent variable of " + getIndependentVariable(); }}// Local Variables:// tab-width: 4// End:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -