📄 tripleexponentialsmoothingmodel.java
字号:
int bestModelIndex = 0; for ( int m=1; m<5; m++ ) if ( model[m].getMSE() < model[bestModelIndex].getMSE() ) bestModelIndex = m; switch ( bestModelIndex ) { case 1: // Reduce maximums // Can discard models 3 and 4 model[3] = null; model[4] = null; return findBest( dataSet, model[0], model[1], model[2], alphaTolerance, betaTolerance ); case 2: // Can discard models 0 and 4 model[0] = null; model[4] = null; return findBest( dataSet, model[1], model[2], model[3], alphaTolerance, betaTolerance ); case 3: // Reduce minimums // Can discard models 0 and 1 model[0] = null; model[1] = null; return findBest( dataSet, model[2], model[3], model[4], alphaTolerance, betaTolerance ); case 0: case 4: // We're done??? break; } // Release all but the best model constructed so far for ( int m=0; m<5; m++ ) if ( m != bestModelIndex ) model[m] = null; return model[bestModelIndex]; } /** * For the given value of the alpha smoothing constant, returns the best * fit model where the value of the beta (trend) smoothing constant is * between betaMin and betaMax. This method will continually try to * refine the estimate of beta until a tolerance of less than * betaTolerance is achieved. * * <p>Note that the descriptions of the parameters below include a * discussion of valid values. However, since this is a private method and * to help improve performance, we don't provide any validation of these * parameters. Using invalid values may lead to unexpected results. * @param dataSet the data set for which a best fit model is required. * @param alpha the (fixed) value of the alpha smoothing constant to use * for the best fit model. * @param betaMin the minimum value of the beta (trend) smoothing * constant accepted in the resulting best fit model. Must be greater than * (or equal to) 0.0 and less than betaMax. * @param betaMax the maximum value of the beta (trend) smoothing * constant accepted in the resulting best fit model. Must be greater than * betaMin and less than (or equal to) 1.0. * @param betaTolerance the tolerance within which the beta 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 TripleExponentialSmoothingModel findBestBeta( DataSet dataSet, double alpha, double betaMin, double betaMax, double betaTolerance ) { int stepsPerIteration = 10; if ( betaMin < 0.0 ) betaMin = 0.0; if ( betaMax > 1.0 ) betaMax = 1.0; TripleExponentialSmoothingModel bestModel = new TripleExponentialSmoothingModel( alpha, betaMin, 0.0 ); bestModel.init(dataSet); double initialMSE = bestModel.getMSE(); boolean betaImproving = true; double betaStep = (betaMax-betaMin)/stepsPerIteration; double beta = betaMin + betaStep; for ( ; beta<=betaMax || betaImproving; ) { TripleExponentialSmoothingModel model = new TripleExponentialSmoothingModel( alpha, beta, 0.0 ); model.init( dataSet ); if ( model.getMSE() < bestModel.getMSE() ) bestModel = model; else betaImproving = false; beta += betaStep; if ( beta > 1.0 ) betaImproving = false; } // If we're making progress, then try to refine the beta estimate if ( bestModel.getMSE() < initialMSE && betaStep > betaTolerance ) { // Can this be further refined - improving efficiency - by // only searching in the range beta-betaStep/2 to // beta+betaStep/2 ? return findBestBeta( dataSet, bestModel.getAlpha(), bestModel.getBeta()-betaStep, bestModel.getBeta()+betaStep, betaTolerance ); } return bestModel; } /** * Constructs a new triple exponential smoothing forecasting model, using * the given smoothing constants - alpha, beta 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. Values above 0.5 * are uncommon - though they are still valid and are supported by this * implementation. * @param beta the second smoothing constant, beta to use in this model * to smooth the trend. Must be a value in the range 0.0-1.0. Values above * 0.5 are uncommon - though they are still valid and are supported by this * implementation. * @param gamma the third smoothing constant, gamma to use in this model * to smooth the seasonality. Must be a value in the range 0.0-1.0. * @throws IllegalArgumentException if the value of any of the smoothing * constants are invalid - outside the range 0.0-1.0. */ public TripleExponentialSmoothingModel( double alpha, double beta, double gamma ) { if ( alpha < 0.0 || alpha > 1.0 ) throw new IllegalArgumentException("TripleExponentialSmoothingModel: Invalid smoothing constant, " + alpha + " - must be in the range 0.0-1.0."); if ( beta < 0.0 || beta > 1.0 ) throw new IllegalArgumentException("TripleExponentialSmoothingModel: Invalid smoothing constant, beta=" + beta + " - must be in the range 0.0-1.0."); if ( gamma < 0.0 || gamma > 1.0 ) throw new IllegalArgumentException("TripleExponentialSmoothingModel: Invalid smoothing constant, gamma=" + gamma + " - must be in the range 0.0-1.0."); baseValues = new DataSet(); trendValues = new DataSet(); seasonalIndex = new DataSet(); this.alpha = alpha; this.beta = beta; this.gamma = gamma; } /** * Used to initialize the time based model. This method must be called * before any other method in the class. Since the time based model does * not derive any equation for forecasting, this method uses the input * DataSet to calculate forecast values for all values of the independent * time variable within the initial data set. * @param dataSet a data set of observations that can be used to * initialize the forecasting parameters of the forecasting model. */ public void init( DataSet dataSet ) { initTimeVariable( dataSet ); String timeVariable = getTimeVariable(); if ( dataSet.getPeriodsPerYear() <= 1 ) throw new IllegalArgumentException("Data set passed to init in the triple exponential smoothing model does not contain seasonal data. Don't forget to call setPeriodsPerYear on the data set to set this."); periodsPerYear = dataSet.getPeriodsPerYear(); // Calculate initial values for base and trend initBaseAndTrendValues( dataSet ); // Initialize seasonal indices using data for all complete years initSeasonalIndices( dataSet ); Iterator it = dataSet.iterator(); maxObservedTime = Double.NEGATIVE_INFINITY; while ( it.hasNext() ) { DataPoint dp = (DataPoint)it.next(); if ( dp.getIndependentValue(timeVariable) > maxObservedTime ) maxObservedTime = dp.getIndependentValue(timeVariable); } super.init( dataSet ); } /** * Calculates (and caches) the initial base and trend values for the given * DataSet. Note that there are a variety of ways to estimate initial * values for both the base and the trend. The approach here averages the * trend calculated from the first two complete "years" - or cycles - of * data in the DataSet. * @param dataSet the set of data points - observations - to use to * initialize the base and trend values. */ private void initBaseAndTrendValues( DataSet dataSet ) { String timeVariable = getTimeVariable(); double trend = 0.0; Iterator it = dataSet.iterator(); for ( int p=0; p<periodsPerYear; p++ ) { DataPoint dp = (DataPoint)it.next(); trend -= dp.getDependentValue(); } double year2Average = 0.0; for ( int p=0; p<periodsPerYear; p++ ) { DataPoint dp = (DataPoint)it.next(); trend += dp.getDependentValue(); year2Average += dp.getDependentValue(); } trend /= periodsPerYear; trend /= periodsPerYear; year2Average /= periodsPerYear; it = dataSet.iterator(); for ( int p=0; p<periodsPerYear*NUMBER_OF_YEARS; p++ ) { DataPoint obs = (DataPoint)it.next(); double time = obs.getIndependentValue( timeVariable ); DataPoint dp = new Observation( trend ); dp.setIndependentValue( timeVariable, time ); trendValues.add( dp ); // Initialize base values for second year only if ( p >= periodsPerYear ) { // This formula gets a little convoluted partly due to // the fact that p is zero-based, and partly because // of the generalized nature of the formula dp.setDependentValue(year2Average + (p+1-periodsPerYear -(periodsPerYear+1)/2.0)*trend); //dp.setIndependentValue( timeVariable, time ); baseValues.add( dp ); } } } /** * Calculates (and caches) the initial seasonal indices for the given * DataSet. Note that there are a variety of ways to estimate initial * values for the seasonal indices. The approach here averages seasonal * indices calculated for the first two complete "years" - or cycles - in * the DataSet. * @param dataSet the set of data points - observations - to use to * initialize the seasonal indices. */ private void initSeasonalIndices( DataSet dataSet ) { String timeVariable = getTimeVariable(); double yearlyAverage[] = new double[NUMBER_OF_YEARS]; Iterator it = dataSet.iterator(); for ( int year=0; year<NUMBER_OF_YEARS; year++ ) { double sum = 0.0; for ( int p=0; p<periodsPerYear; p++ ) { DataPoint dp = (DataPoint)it.next(); sum += dp.getDependentValue(); } yearlyAverage[year] = sum / (double)periodsPerYear; } it = dataSet.iterator(); double index[] = new double[periodsPerYear]; for ( int year=0; year<NUMBER_OF_YEARS; year++ ) { double sum = 0.0; for ( int p=0; p<periodsPerYear; p++ ) { DataPoint dp = (DataPoint)it.next(); index[p] += (dp.getDependentValue()/yearlyAverage[year]) / NUMBER_OF_YEARS; } } it = dataSet.iterator(); // Skip over first n-1 years for ( int year=0; year<NUMBER_OF_YEARS-1; year++ ) for ( int p=0; p<periodsPerYear; p++ ) it.next(); for ( int p=0; p<periodsPerYear; p++ ) { DataPoint dp = (DataPoint)it.next(); double time = dp.getIndependentValue( timeVariable ); Observation obs = new Observation( index[p] ); obs.setIndependentValue( timeVariable, time ); seasonalIndex.add( obs ); } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -