0

Holt-Winters 在这里介绍:

http://en.wikipedia.org/wiki/Holt-Winters

此处讨论了它的季节性阻尼版本(向下滚动页面):

http://otexts.com/fpp/7/5/

简而言之,它基本上着眼于 3 件事:

  • 长期趋势
  • 短期趋势
  • 季节性趋势

它也不会将它们平均在一起,因为您真正需要的是加权平均,其中季节性和短期比长期趋势更重要,自然地,财务数据趋势。

给定 $anYear1 和 $anYear2,我如何应用 Holt-Winters 季节性阻尼方法来预测 $anYear2 结束后的 2 个月?假设 $anYear1 是一个包含 12 个数字的数组。假设 $anYear2 是一个范围为 0 到 12 个数字的数组。

所以,我可以用随机数据填充它,如下所示:

<?php

$anYear1 = array();
$anYear2 = array();
$nStop = 10; // so we need 11 and 12 of the year
for ($i = 1; $i <= 12; $i++) {
  $anYear1[$i] = rand(200,500);
  if ($i <= $nStop) {
    // give it a natural lift like real financial data
    $anYear2[$i] = rand(400,700); 
  }
}
$nSeasonRange = 4; // 4 months in a business quarter

Therefore, I want to create a function like so:

function forecastHoltWinters($anYear1, $anYear2, $nSeasonRange = 4) {
  ///////////////////
  // DO MAGIC HERE //
  ///////////////////

  // an array with 2 numbers, indicating 2 months forward from end of $anYear2
  return $anForecast;
}

$anForecast = forecastHoltWinters($anYear1, $anYear2, $nSeasonRange);
echo "YEAR 1\n";
print_r($anYear1);
echo "\n\nYEAR 2\n"
print_r($anYear2);
echo "\n\nTWO MONTHS FORECAST\n";
print_r($anForecast);

注意:我在这里找到了一个 Github 示例,但它没有显示如何进行投影。它也在这里讨论。

4

1 回答 1

1

我找到了一种方法来调整Ian Barber 的功能来做我需要的事情。

<?php

error_reporting(E_ALL);
ini_set('display_errors','On');

$anYear1 = array();
$anYear2 = array();
$nStop = 10;
for($i = 1; $i <= 12; $i++) {
    $anYear1[$i] = rand(100,400);
    if ($i <= $nStop) {
        $anYear2[$i+12] = rand(200,600);
    }
}

print_r($anYear1);
print_r($anYear2);
$anData = array_merge($anYear1,$anYear2);
print_r(forecastHoltWinters($anData));

function forecastHoltWinters($anData, $nForecast = 2, $nSeasonLength = 4, $nAlpha = 0.2, $nBeta = 0.01, $nGamma = 0.01, $nDevGamma = 0.1) {

    // Calculate an initial trend level
    $nTrend1 = 0;
    for($i = 0; $i < $nSeasonLength; $i++) {
      $nTrend1 += $anData[$i];
    }
    $nTrend1 /= $nSeasonLength;

    $nTrend2 = 0;
    for($i = $nSeasonLength; $i < 2*$nSeasonLength; $i++) {
      $nTrend2 += $anData[$i];
    }
    $nTrend2 /= $nSeasonLength;

    $nInitialTrend = ($nTrend2 - $nTrend1) / $nSeasonLength;

    // Take the first value as the initial level
    $nInitialLevel = $anData[0];

    // Build index
    $anIndex = array();
    foreach($anData as $nKey => $nVal) {
      $anIndex[$nKey] = $nVal / ($nInitialLevel + ($nKey + 1) * $nInitialTrend);
    }

    // Build season buffer
    $anSeason = array_fill(0, count($anData), 0);
    for($i = 0; $i < $nSeasonLength; $i++) {
      $anSeason[$i] = ($anIndex[$i] + $anIndex[$i+$nSeasonLength]) / 2;
    }

    // Normalise season
    $nSeasonFactor = $nSeasonLength / array_sum($anSeason);
    foreach($anSeason as $nKey => $nVal) {
      $anSeason[$nKey] *= $nSeasonFactor;
    }

    $anHoltWinters = array();
    $anDeviations = array();
    $nAlphaLevel = $nInitialLevel;
    $nBetaTrend = $nInitialTrend;
    foreach($anData as $nKey => $nVal) {
      $nTempLevel = $nAlphaLevel;
      $nTempTrend = $nBetaTrend;

      $nAlphaLevel = $nAlpha * $nVal / $anSeason[$nKey] + (1.0 - $nAlpha) * ($nTempLevel + $nTempTrend);
      $nBetaTrend = $nBeta * ($nAlphaLevel - $nTempLevel) + ( 1.0 - $nBeta ) * $nTempTrend;

      $anSeason[$nKey + $nSeasonLength] = $nGamma * $nVal / $nAlphaLevel + (1.0 - $nGamma) * $anSeason[$nKey];

      $anHoltWinters[$nKey] = ($nAlphaLevel + $nBetaTrend * ($nKey + 1)) * $anSeason[$nKey];
      $anDeviations[$nKey] = $nDevGamma * abs($nVal - $anHoltWinters[$nKey]) + (1-$nDevGamma) 
                  * (isset($anDeviations[$nKey - $nSeasonLength]) ? $anDeviations[$nKey - $nSeasonLength] : 0);
    }

    $anForecast = array();
    $nLast = end($anData);
    for($i = 1; $i <= $nForecast; $i++) {
       $nComputed = round($nAlphaLevel + $nBetaTrend * $anSeason[$nKey + $i]);
       if ($nComputed < 0) { // wildly off due to outliers
         $nComputed = $nLast;
       }
       $anForecast[] = $nComputed;
    }

    return $anForecast;
}
于 2012-10-31T23:11:18.977 回答