Holt-Winters forecasting model

(Steven) #1

Hello,
Is this forecasting method available somehow?

0 Likes

(Peter Vanderwaart) #2

I don’t see anything about H-W in MathNet. If fact, I don’t see anything specifically forecasting models at all. I used H-W back in the day, and it worked very well for time series of sales of various food products.

It’s quite easy to program. The big difficulty is keep the indexes straight. The following program follows the lead of Forecasting and Time Series Analysis by Douglas Montgomery and Lynwood Johnson (1976). In that book, it’s just call the Winters method. I think Holt had an original idea and Winters elaborated on it.

There is a class called HoltWinters that does the work. The preliminary program DoHW builds a test series and invokes the class. The programming is quick and dirty; there is no error-checking at all. I tested it only using the one test series but for various degrees of slope and randomness, and it seems to work OK. Feel free to get back to me with questions.

public class Forecasting
{

    public void DoHW()
    {
        // define time series to be modeled
        Random err = new Random();
        double[] ts = new double[36];
        int nPer = 12;
        for (int i = 0; i < ts.Length; i++) ts[i] = 10.0 + 0.2 * i + Math.Sin((i * 2 * Math.PI) / 12) +0.5 * err.NextDouble();


        HoltWinters hw = new HoltWinters();
        hw.SetSeries(ts);
        hw.SetPeriod(nPer);
        hw.SetInitialValues();
        hw.SetSmoothingConstants(.1, .1, .1);
        hw.DoSmoothing();
        var rslt = hw.GetForecast(nPer);
        for (int i = 0; i < ts.Length; i++) Console.WriteLine(ts[i]);
        for (int i = 0; i < rslt.Length; i++) Console.WriteLine(rslt[i]);

    }

}

public class HoltWinters
{
    public double[] x; // time series to model
    public int p; // period, i.e. 4 for quarterly, 12 for monthly
    public double[] s; // seasonal;
    public double[] d; // month-to-month delta
    public double[] c; // constant
    public double rC, rD, rS;
    public HoltWinters() { }

    public void SetSeries(double[] ts) { x = ts; }
    public void SetPeriod(int nPer) { p = nPer; }
    public void SetSmoothingConstants(double r1, double r2, double r3)
    {
        rC = r1;
        rD = r2;
        rS = r3;
    }

    public void SetInitialValues()
    {
        // estimate initial values

        // 1. estimate trend from growth over first cycle
        d = new double[x.Length];
        d[0] = (x[p] - x[0]) / p;

        // 2. estimate seasonals by ratio to trend line. Adjust so seasonals for first cycle sum to p.
        s = new double[x.Length];
        double s_sum = 0.0;
        for (int i = 0; i < p; i++)
        {
            double trend = x[0] + i * d[0];
            s[i] = x[i] / trend;
            s_sum += s[i];
        }
        for (int i = 0; i < p; i++) s[i] = p * s[i] / s_sum;

        // 3. set inital constant value
        c = new double[x.Length];
        c[0] = x[0] / s[0];

    }

    public void DoSmoothing()
    {
        for (int i = 1; i < x.Length; i++)
        {
            // Estimate constant term
            c[i] = rC * x[i - 1] / s[i - 1] + (1.0 - rC) * c[i - 1] + d[i - 1]; ;

            // Estimate trend term
            d[i] = rD * (c[i] - c[i - 1]) + (1 - rD) * d[i - 1];

            // Estimate seasonal term - no change in first cycle
            if (i >= p) s[i] = rS * x[i] / c[i] + (1.0 - rS) * s[i - p];
        }

    }

    public double[] GetForecast(int nPers)
    {
        double[] fcst = new double[nPers];
        int n = x.Length;

        double cons = c[n - 1];
        double delta = d[n - 1];

        for (int i = 0; i < nPers; i++)
        {
            int nSeas = n + i;
            while (nSeas >= n) nSeas -= p;

            fcst[i] = (cons + (i+1) * delta) * s[nSeas];

        }
        return fcst;
    }

}
0 Likes

(Steven) #3

Awesome Peter, thanks! I’ll check it out and come back to you.

Regards,

Steven

0 Likes