📄 tripleexponentialsmoothingmodel.java
字号:
/** * 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 time ) throws IllegalArgumentException { double previousTime = time - getTimeInterval(); double previousYear = time - getTimeInterval()*periodsPerYear; // As a starting point, we set the first forecast value to be // the same as the observed value if ( previousTime < getMinimumTimeValue()-TOLERANCE ) return getObservedValue( time ); try { double base = getBase( previousTime ); double trend = getTrend( previousTime ); double si = getSeasonalIndex( previousYear ); double forecast = (base+trend)*si; return forecast; } catch ( IllegalArgumentException idex ) { double base = getBase( maxObservedTime ); double trend = getTrend( maxObservedTime-getTimeInterval() ); double si = getSeasonalIndex( previousYear ); double forecast = (base+(time-maxObservedTime)*trend) * si; return forecast; } } /** * Calculates and returns the base value for the given time period. Except * for the first "year" - where base values are not available - the base * is calculated using a smoothed value of the previous base. See the * class documentation for details on the formulation used. * @param time the time value for which the trend is required. * @return the estimated base value at the given period of time. * @param IllegalArgumentException if the base cannot be determined for * the given time period. */ private double getBase( double time ) throws IllegalArgumentException { // TODO: Optimize this search by having data set sorted by time // Search for previously calculated - and saved - base value String timeVariable = getTimeVariable(); Iterator it = baseValues.iterator(); while ( it.hasNext() ) { DataPoint dp = (DataPoint)it.next(); double dpTimeValue = dp.getIndependentValue( timeVariable ); if ( Math.abs(time-dpTimeValue) < TOLERANCE ) return dp.getDependentValue(); } if ( time < getMinimumTimeValue() +periodsPerYear*getTimeInterval() +TOLERANCE ) throw new IllegalArgumentException( "Attempt to forecast for an invalid time " +time +" - before sufficient observations were made (" +getMinimumTimeValue() +periodsPerYear*getTimeInterval()+")."); // Saved base value not found, so calculate it // (and save it for future reference) double previousTime = time - getTimeInterval(); double previousYear = time - periodsPerYear*getTimeInterval(); double base = alpha*(getObservedValue(time)/getSeasonalIndex(previousYear)) + (1-alpha)*(getBase(previousTime)+getTrend(previousTime)); DataPoint dp = new Observation( base ); dp.setIndependentValue( timeVariable, time ); baseValues.add( dp ); return base; } /** * Calculates and returns the trend for the given time period. Except * for the initial periods - where forecasts are not available - the * trend 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 trend is required. * @return the trend of the data at the given period of time. * @param IllegalArgumentException if the trend cannot be determined for * the given time period. */ private double getTrend( double time ) throws IllegalArgumentException { // TODO: Optimize this search by having data set sorted by time // Search for previously calculated - and saved - trend value String timeVariable = getTimeVariable(); Iterator it = trendValues.iterator(); while ( it.hasNext() ) { DataPoint dp = (DataPoint)it.next(); double dpTimeValue = dp.getIndependentValue( timeVariable ); if ( Math.abs(time-dpTimeValue) < TOLERANCE ) return dp.getDependentValue(); } if ( time < getMinimumTimeValue()+TOLERANCE ) throw new IllegalArgumentException("Attempt to forecast for an invalid time - before the observations began ("+getMinimumTimeValue()+")."); // Saved trend not found, so calculate it // (and save it for future reference) double previousTime = time - getTimeInterval(); double trend = beta*(getBase(time)-getBase(previousTime)) + (1-beta)*getTrend(previousTime); DataPoint dp = new Observation( trend ); dp.setIndependentValue( timeVariable, time ); trendValues.add( dp ); return trend; } /** * Returns the seasonal index for the given time period. */ private double getSeasonalIndex( double time ) throws IllegalArgumentException { // TODO: Optimize this search by having data set sorted by time // Handle initial conditions for seasonal index if ( time < getMinimumTimeValue() +(NUMBER_OF_YEARS-1)*periodsPerYear-TOLERANCE ) return getSeasonalIndex( time + periodsPerYear*getTimeInterval() ); // Search for previously calculated - and saved - seasonal index String timeVariable = getTimeVariable(); Iterator it = seasonalIndex.iterator(); while ( it.hasNext() ) { DataPoint dp = (DataPoint)it.next(); double dpTimeValue = dp.getIndependentValue( timeVariable ); if ( Math.abs(time-dpTimeValue) < TOLERANCE ) return dp.getDependentValue(); } // Saved seasonal index not found, so calculate it // (and save it for future reference) double previousYear = time - getTimeInterval()*periodsPerYear; double index = gamma*(getObservedValue(time)/getForecastValue(time)) + (1-gamma)*getSeasonalIndex(previousYear); DataPoint dp = new Observation( index ); dp.setIndependentValue( timeVariable, time ); seasonalIndex.add( dp ); return index; } /** * 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 triple 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*periodsPerYear; } /** * Since this version of triple 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 ) { // WARNING: THIS STILL NEEDS TO BE VALIDATED // 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 ) + getTrend( 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, alpha, used in this model. * @return the value of the smoothing constant, alpha. * @see #getBeta * @see #getGamma */ public double getAlpha() { return alpha; } /** * Returns the value of the trend smoothing constant, beta, used in this * model. * @return the value of the trend smoothing constant, beta. * @see #getAlpha * @see #getGamma */ public double getBeta() { return beta; } /** * Returns the value of the seasonal smoothing constant, gamma, used in * this model. * @return the value of the seasonal smoothing constant, gamma. * @see #getAlpha * @see #getBeta */ 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 "triple 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 "Triple exponential smoothing model, with smoothing constants of alpha=" + alpha + ", beta=" + beta + ", 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 + -