/** 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;
  }
  /**
   * 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;
  }
예제 #3
0
 public TreeMap<Integer, Double> getForecast(int start, int end) {
   TreeMap<Integer, Double> forecast = new TreeMap<Integer, Double>();
   fcModel.init(this.demandData.getDemandDataSet(reviewPeriod));
   DataSet fcSet = new DataSet();
   for (int i = start; i <= end; i++) {
     DataPoint dp = new Observation(0.0);
     dp.setIndependentValue("Tick", i);
     fcSet.add(dp);
   }
   fcModel.forecast(fcSet);
   Iterator it = fcSet.iterator();
   while (it.hasNext()) {
     DataPoint dp = (DataPoint) it.next();
     forecast.put((int) dp.getIndependentValue("Tick"), dp.getDependentValue());
   }
   // System.out.println("DemandData: " + this.demandData.getDataMap());
   // System.out.println("Forecast: " + forecast);
   return forecast;
 }
  /**
   * 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 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;
  }
  /**
   * Tests the correct output of a DataSet to a TimeSeries by outputting it, then iterating through
   * TimeSeries object checking the correct values were stored/output.
   */
  public void testOutput()
      throws ClassNotFoundException, NoSuchMethodException, InstantiationException,
          IllegalAccessException, InvocationTargetException, InstantiationException {
    // Constants used to determine size of test
    int NUMBER_OF_TIME_PERIODS = 10;
    String TIME_VARIABLE = "t";

    // Set up array for expected results
    double expectedValue[] = new double[NUMBER_OF_TIME_PERIODS];

    // We'll set up periods starting from today
    RegularTimePeriod period = new Day();

    // Create a test DataSet for output
    //  - note that only one independent variable (the time variable)
    //    will be output. This is expected.
    DataSet dataSet = new DataSet();
    dataSet.setTimeVariable(TIME_VARIABLE);
    for (int d = 0; d < NUMBER_OF_TIME_PERIODS; d++) {
      double value = (double) d;
      DataPoint obs = new Observation(value);
      obs.setIndependentValue(TIME_VARIABLE, period.getMiddleMillisecond());
      dataSet.add(obs);

      period = period.next();
      expectedValue[d] = value;
    }

    assertEquals(
        "Checking only one independent variable exists in dataSet",
        1,
        dataSet.getIndependentVariables().length);

    assertEquals(
        "Checking dataSet has correct number of entries", NUMBER_OF_TIME_PERIODS, dataSet.size());

    // Create TimeSeriesOutputter and use it to output dataSet
    TimeSeries timeSeries = new TimeSeries("test");
    TimeSeriesOutputter outputter = new TimeSeriesOutputter(timeSeries, period.getClass());
    outputter.output(dataSet);

    assertEquals(
        "Checking number of items in time series",
        NUMBER_OF_TIME_PERIODS,
        timeSeries.getItemCount());

    // Reset period to start checking from today onwards
    period = new Day();
    for (int d = 0; d < NUMBER_OF_TIME_PERIODS; d++) {
      TimeSeriesDataItem dataItem = timeSeries.getDataItem(d);

      period = dataItem.getPeriod();
      assertNotNull("Checking time period", period);

      long timeValue = period.getMiddleMillisecond();
      assertTrue(
          "Checking time periods match",
          (double) timeValue >= period.getFirstMillisecond()
              && (double) timeValue <= period.getLastMillisecond());

      assertEquals(
          "Checking values for period " + dataItem.getPeriod() + " match",
          expectedValue[d],
          dataItem.getValue().doubleValue(),
          TOLERANCE);

      period = period.next();
    }
  }