예제 #1
0
 private ForecastTable getForecastedTable(decimal[] array, int forecasting_number, int forecasting_Type)
 {
     Console.WriteLine(forecasting_Type + "----" + forecasting_number);
     if (forecasting_Type == 0)
     {
         ForecastTable ft = fm.naive(array, forecasting_number, 0);
         return(ft);
     }
     else if (forecasting_Type == 1)
     {
         ForecastTable ft = fm.simpleMovingAverage(array, forecasting_number, 3, 0);
         return(ft);
     }
     else if (forecasting_Type == 2)
     {
         ForecastTable ft = fm.weightedMovingAverage(array, forecasting_number, (Decimal)0.05, (Decimal)0.15, (Decimal)0.8);
         return(ft);
     }
     else if (forecasting_Type == 3)
     {
         ForecastTable ft = fm.adaptiveRateSmoothing(array, forecasting_number, (Decimal)0.2, (Decimal)0.8);
         return(ft);
     }
     else
     {
         ForecastTable ft = fm.exponentialSmoothing(array, forecasting_number, (Decimal)0.8);
         return(ft);
     }
 }
        private void button_ForecastedDataView_Click_1(object sender, EventArgs e)
        {
            int month_min = 0;
            int month_max = 0;

            if (isMonth)
            {
                month_min = comboBox_ForecastedDateRange.SelectedIndex + 1;
                month_max = 25;
            }
            else
            {
                if (comboBox_ForecastedDateRange.SelectedIndex == 0)
                {
                    month_min = 1;
                    month_max = 3;
                }
                else if (comboBox_ForecastedDateRange.SelectedIndex == 1)
                {
                    month_min = 4;
                    month_max = 6;
                }
                else if (comboBox_ForecastedDateRange.SelectedIndex == 2)
                {
                    month_min = 7;
                    month_max = 9;
                }
                else
                {
                    month_min = 10;
                    month_max = 12;
                }
            }
            if (comboBox_ForecastedRegion.SelectedItem != null)
            {
                int    region_id       = (comboBox_ForecastedRegion.SelectedItem as Region).region_id;
                int    forecastingType = comboBox_ForecastingType.SelectedIndex;
                string number          = textBox_ForecastingNumber.Text;

                ForecastTable ft = dataArranger.getForecastTable(region_id, forecasted_yData_type, month_min, month_max, forecastingType, number, this);
                this.grid_ForecastedData.DataSource = ft;


                if (ft != null)
                {
                    this.panel_forecastedGrid.Show();
                    ForecastingMethods fm = new ForecastingMethods();
                    this.textBox_MSE.Text  = fm.MeanSignedError(ft, false, DEFAULT_IGNORE).ToString();
                    this.textBox_MAE.Text  = fm.MeanAbsoluteError(ft, false, DEFAULT_IGNORE).ToString();
                    this.textBox_MPE.Text  = fm.MeanPercentError(ft, false, DEFAULT_IGNORE).ToString();
                    this.textBox_MAPE.Text = fm.MeanAbsolutePercentError(ft, false, DEFAULT_IGNORE).ToString();
                    this.textBox_TS.Text   = fm.TrackingSignal(ft, false, DEFAULT_IGNORE).ToString();
                    this.textBox_MSqE.Text = fm.MeanSquaredError(ft, false, DEFAULT_IGNORE, 0).ToString();
                    this.textBox_CSE.Text  = fm.CumulativeSignedError(ft, false, DEFAULT_IGNORE).ToString();
                    this.textBox_CAE.Text  = fm.CumulativeAbsoluteError(ft, false, DEFAULT_IGNORE).ToString();
                }
            }
        }
        //
        // Adaptive Rate Smoothing
        //
        public ForecastTable adaptiveRateSmoothing(decimal[] values, int Extension, decimal MinGamma, decimal MaxGamma)
        {
            ForecastTable dt = new ForecastTable();

            for (Int32 i = 0; i < (values.Length + Extension); i++)
            {
                //Insert a row for each value in set
                DataRow row = dt.NewRow();
                dt.Rows.Add(row);

                row.BeginEdit();
                //assign its sequence number
                row["Instance"] = i;
                if (i < values.Length)
                {
                    row["Value"] = values[i];
                    if (i == 0)
                    {//initialize first row
                        row["Forecast"] = values[i];
                    }
                    else
                    {//calculate gamma and forecast value
                        DataRow priorRow = dt.Select("Instance=" + (i - 1).ToString())[0];
                        decimal PriorForecast = (Decimal)priorRow["Forecast"];
                        decimal PriorValue = (Decimal)priorRow["Value"];

                        decimal Gamma = Math.Abs(TrackingSignal(dt, false, 3));
                        if (Gamma < MinGamma)
                            Gamma = MinGamma;
                        if (Gamma > MaxGamma)
                            Gamma = MaxGamma;

                        row["Forecast"] = PriorForecast + (Gamma * (PriorValue - PriorForecast));
                    }
                }
                else
                {//extension set, can't use actual values anymore
                    DataRow priorRow = dt.Select("Instance=" + (i - 1).ToString())[0];
                    decimal PriorForecast = (Decimal)priorRow["Forecast"];
                    decimal PriorValue = (Decimal)priorRow["Forecast"];

                    decimal Gamma = Math.Abs(TrackingSignal(dt, false, 3));
                    if (Gamma < MinGamma)
                        Gamma = MinGamma;
                    if (Gamma > MaxGamma)
                        Gamma = MaxGamma;

                    row["Forecast"] = PriorForecast + (Gamma * (PriorValue - PriorForecast));
                }
                row.EndEdit();
            }

            dt.AcceptChanges();

            return dt;
        }
        //CumulativeAbsoluteError = Sum( |E(t)| )
        public decimal CumulativeAbsoluteError(ForecastTable dt, bool Holdout, int IgnoreInitial)
        {
            string Filter = "AbsoluteError Is Not Null AND Instance > " + IgnoreInitial.ToString();
            if (Holdout)
                Filter += " AND Holdout=True";

            if (dt.Select(Filter).Length == 0)
                return 0;
            return (Decimal)dt.Compute("SUM(AbsoluteError)", Filter);
        }
예제 #5
0
        //Tracking signal = MeanSignedError / MeanAbsoluteError
        public decimal TrackingSignal(ForecastTable dt, bool Holdout, int IgnoreInitial)
        {
            decimal MAE = MeanAbsoluteError(dt, Holdout, IgnoreInitial);

            if (MAE == 0)
            {
                return(0);
            }

            return(MeanSignedError(dt, Holdout, IgnoreInitial) / MAE);
        }
예제 #6
0
        internal ForecastTable getForecastTable(int region_id, int forecasted_yData_type, int month_min, int month_max, int forecasting_Type, string number, Main_Form main_Form)
        {
            int tempnumber;

            bool result = Int32.TryParse(number, out tempnumber);

            if (!result)
            {
                MetroFramework.MetroMessageBox.Show(main_Form, "Forecasting number should be an integer value" + "\n" + "Please insert a correct value", "Forecasting number is not an integer", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            else
            {
                if (month_max > 12)
                {
                    DataSet ds    = dr.getGraphDataOnMonth(region_id, month_min);
                    int     count = ds.Tables[0].Rows.Count;
                    Console.WriteLine(count);

                    decimal[] array = new decimal[count];

                    if (forecasted_yData_type == 1)
                    {
                        int index = 0;
                        foreach (DataRow row in ds.Tables[0].Rows)
                        {
                            array[index] = Convert.ToDecimal(row["quantity"].ToString());
                            index++;
                        }

                        int           forecasting_number = Convert.ToInt32(number);
                        ForecastTable ft = this.getForecastedTable(array, forecasting_number, forecasting_Type);//fm.naive(array, 10, 0);
                        return(ft);
                    }
                    else
                    {
                        foreach (DataRow row in ds.Tables[0].Rows)
                        {
                            decimal income = Convert.ToDecimal(row["total_income"].ToString());
                        }

                        ForecastTable ft = fm.naive(array, 10, 0);
                        return(ft);
                    }
                }
                else
                {
                    return(null);
                }
            }
            return(null);
        }
예제 #7
0
        //CumulativeAbsoluteError = Sum( |E(t)| )
        public decimal CumulativeAbsoluteError(ForecastTable dt, bool Holdout, int IgnoreInitial)
        {
            string Filter = "AbsoluteError Is Not Null AND Instance > " + IgnoreInitial.ToString();

            if (Holdout)
            {
                Filter += " AND Holdout=True";
            }

            if (dt.Select(Filter).Length == 0)
            {
                return(0);
            }
            return((Decimal)dt.Compute("SUM(AbsoluteError)", Filter));
        }
예제 #8
0
        //Bayes: use prior actual value as forecast
        public ForecastTable naive(decimal[] values, int Extension, int Holdout)
        {
            ForecastTable dt = new ForecastTable();

            try
            {
                for (Int32 i = 0; i < (values.Length + Extension); i++)
                {
                    //Insert a row for each value in set
                    DataRow row = dt.NewRow();
                    dt.Rows.Add(row);

                    row.BeginEdit();
                    //assign its sequence number
                    row["Instance"] = i;

                    //if i is in the holdout range of values
                    //row["Holdout"] = (i > (values.Length - 1 - Holdout)) && (i < values.Length);

                    if (i < values.Length)
                    { //processing values which actually occurred
                        //Assign the actual value to the DataRow
                        row["Value"] = values[i];
                        if (i == 0)
                        {
                            //first row, value gets itself
                            row["Forecast"] = values[i];
                        }
                        else
                        {
                            //Put the prior row's value into the current row's forecasted value
                            row["Forecast"] = values[i - 1];
                        }
                    }
                    else
                    {//Extension rows
                        row["Forecast"] = values[values.Length - 1];
                    }
                    row.EndEdit();
                }
                dt.AcceptChanges();
                return(dt);
            }
            catch (Exception ex)
            {
            }
            return(null);
        }
예제 #9
0
        //
        //Exponential Smoothing
        //
        //  F(t+1) =    ( Alpha * D(t) ) + (1 - Alpha) * F(t)
        //
        public ForecastTable exponentialSmoothing(decimal[] values, int Extension, decimal Alpha)
        {
            ForecastTable dt = new ForecastTable();

            for (Int32 i = 0; i < (values.Length + Extension); i++)
            {
                //Insert a row for each value in set
                DataRow row = dt.NewRow();
                dt.Rows.Add(row);

                row.BeginEdit();
                //assign its sequence number
                row["Instance"] = i;
                if (i < values.Length)
                {//test set
                    row["Value"] = values[i];
                    if (i == 0)
                    {//initialize first value
                        row["Forecast"] = values[i];
                    }
                    else
                    {//main area of forecasting
                        DataRow priorRow      = dt.Select("Instance=" + (i - 1).ToString())[0];
                        decimal PriorForecast = (Decimal)priorRow["Forecast"];
                        decimal PriorValue    = (Decimal)priorRow["Value"];

                        row["Forecast"] = PriorForecast + (Alpha * (PriorValue - PriorForecast));
                    }
                }
                else
                {//extension has to use prior forecast instead of prior value
                    DataRow priorRow      = dt.Select("Instance=" + (i - 1).ToString())[0];
                    decimal PriorForecast = (Decimal)priorRow["Forecast"];
                    decimal PriorValue    = (Decimal)priorRow["Forecast"];

                    row["Forecast"] = PriorForecast + (Alpha * (PriorValue - PriorForecast));
                }
                row.EndEdit();
            }

            dt.AcceptChanges();

            return(dt);
        }
예제 #10
0
        //MSE = Sum( E(t)^2 ) / n
        public decimal MeanSquaredError(ForecastTable dt, bool Holdout, int IgnoreInitial, int DegreesOfFreedom)
        {
            decimal SquareError = 0;
            string  Filter      = "Error Is Not Null AND Instance > " + IgnoreInitial.ToString();

            if (Holdout)
            {
                Filter += " AND Holdout=True";
            }

            DataRow[] rows = dt.Select(Filter);
            if (rows.Length == 0)
            {
                return(0);
            }

            foreach (DataRow row in rows)
            {
                SquareError = (Decimal)Math.Pow(Double.Parse(row["Error"].ToString()), (Double)2.0);
            }
            return(SquareError / (dt.Rows.Count - DegreesOfFreedom));
        }
        //Tracking signal = MeanSignedError / MeanAbsoluteError
        public decimal TrackingSignal(ForecastTable dt, bool Holdout, int IgnoreInitial)
        {
            decimal MAE = MeanAbsoluteError(dt, Holdout, IgnoreInitial);
            if (MAE == 0)
                return 0;

            return MeanSignedError(dt, Holdout, IgnoreInitial) / MAE;
        }
예제 #12
0
        //
        // Adaptive Rate Smoothing
        //
        public ForecastTable adaptiveRateSmoothing(decimal[] values, int Extension, decimal MinGamma, decimal MaxGamma)
        {
            ForecastTable dt = new ForecastTable();

            for (Int32 i = 0; i < (values.Length + Extension); i++)
            {
                //Insert a row for each value in set
                DataRow row = dt.NewRow();
                dt.Rows.Add(row);

                row.BeginEdit();
                //assign its sequence number
                row["Instance"] = i;
                if (i < values.Length)
                {
                    row["Value"] = values[i];
                    if (i == 0)
                    {//initialize first row
                        row["Forecast"] = values[i];
                    }
                    else
                    {//calculate gamma and forecast value
                        DataRow priorRow      = dt.Select("Instance=" + (i - 1).ToString())[0];
                        decimal PriorForecast = (Decimal)priorRow["Forecast"];
                        decimal PriorValue    = (Decimal)priorRow["Value"];

                        decimal Gamma = Math.Abs(TrackingSignal(dt, false, 3));
                        if (Gamma < MinGamma)
                        {
                            Gamma = MinGamma;
                        }
                        if (Gamma > MaxGamma)
                        {
                            Gamma = MaxGamma;
                        }

                        row["Forecast"] = PriorForecast + (Gamma * (PriorValue - PriorForecast));
                    }
                }
                else
                {//extension set, can't use actual values anymore
                    DataRow priorRow      = dt.Select("Instance=" + (i - 1).ToString())[0];
                    decimal PriorForecast = (Decimal)priorRow["Forecast"];
                    decimal PriorValue    = (Decimal)priorRow["Forecast"];

                    decimal Gamma = Math.Abs(TrackingSignal(dt, false, 3));
                    if (Gamma < MinGamma)
                    {
                        Gamma = MinGamma;
                    }
                    if (Gamma > MaxGamma)
                    {
                        Gamma = MaxGamma;
                    }

                    row["Forecast"] = PriorForecast + (Gamma * (PriorValue - PriorForecast));
                }
                row.EndEdit();
            }

            dt.AcceptChanges();

            return(dt);
        }
        //
        //Exponential Smoothing
        //
        //  F(t+1) =    ( Alpha * D(t) ) + (1 - Alpha) * F(t)
        //
        public ForecastTable exponentialSmoothing(decimal[] values, int Extension, decimal Alpha)
        {
            ForecastTable dt = new ForecastTable();

            for (Int32 i = 0; i < (values.Length + Extension); i++)
            {
                //Insert a row for each value in set
                DataRow row = dt.NewRow();
                dt.Rows.Add(row);

                row.BeginEdit();
                //assign its sequence number
                row["Instance"] = i;
                if (i < values.Length)
                {//test set
                    row["Value"] = values[i];
                    if (i == 0)
                    {//initialize first value
                        row["Forecast"] = values[i];
                    }
                    else
                    {//main area of forecasting
                        DataRow priorRow = dt.Select("Instance=" + (i - 1).ToString())[0];
                        decimal PriorForecast = (Decimal)priorRow["Forecast"];
                        decimal PriorValue = (Decimal)priorRow["Value"];

                        row["Forecast"] = PriorForecast + (Alpha * (PriorValue - PriorForecast));
                    }
                }
                else
                {//extension has to use prior forecast instead of prior value
                    DataRow priorRow = dt.Select("Instance=" + (i - 1).ToString())[0];
                    decimal PriorForecast = (Decimal)priorRow["Forecast"];
                    decimal PriorValue = (Decimal)priorRow["Forecast"];

                    row["Forecast"] = PriorForecast + (Alpha * (PriorValue - PriorForecast));
                }
                row.EndEdit();
            }

            dt.AcceptChanges();

            return dt;
        }
        //MeanPercentError = Sum( PercentError ) / n
        public decimal MeanPercentError(ForecastTable dt, bool Holdout, int IgnoreInitial)
        {
            string Filter = "PercentError Is Not Null AND Instance > " + IgnoreInitial.ToString();
            if (Holdout)
                Filter += " AND Holdout=True";

            if (dt.Select(Filter).Length == 0)
                return 0;
            return (Decimal)dt.Compute("Avg(PercentError)", Filter);
        }
        //MSE = Sum( E(t)^2 ) / n
        public decimal MeanSquaredError(ForecastTable dt, bool Holdout, int IgnoreInitial, int DegreesOfFreedom)
        {
            decimal SquareError = 0;
            string Filter = "Error Is Not Null AND Instance > " + IgnoreInitial.ToString();
            if (Holdout)
                Filter += " AND Holdout=True";

            DataRow[] rows = dt.Select(Filter);
            if (rows.Length == 0)
                return 0;

            foreach (DataRow row in rows)
            {
                SquareError = (Decimal)Math.Pow(Double.Parse(row["Error"].ToString()), (Double)2.0);
            }
            return SquareError / (dt.Rows.Count - DegreesOfFreedom);
        }
예제 #16
0
        //
        //Weighted Moving Average
        //
        //  F(t+1) =  (Weight1 * D(t)) + (Weight2 * D(t-1)) + (Weight3 * D(t-2)) + ... + (WeightN * D(t-n+1))
        //
        public ForecastTable weightedMovingAverage(decimal[] values, int Extension, params decimal[] PeriodWeight)
        {
            //PeriodWeight[].Length is used to determine the number of periods over which to average
            //PeriodWeight[x] is used to apply a weight to the prior period's value

            //Make sure PeriodWeight values add up to 100%
            decimal test = 0;

            foreach (decimal weight in PeriodWeight)
            {
                test += weight;
            }
            if (test != 1)
            {
                MessageBox.Show("Period weights must add up to 1.0", "Invalid parameters", MessageBoxButtons.OK);
                return(null);
            }

            ForecastTable dt = new ForecastTable();

            for (Int32 i = 0; i < values.Length + Extension; i++)
            {
                //Insert a row for each value in set
                DataRow row = dt.NewRow();
                dt.Rows.Add(row);

                row.BeginEdit();
                //assign its sequence number
                row["Instance"] = i;

                if (i < values.Length)
                {//we're in the test set
                    row["Value"] = values[i];
                }

                if (i == 0)
                {//initialize forecast with first row's value
                    row["Forecast"] = values[i];
                }
                else if ((i < values.Length) && (i < PeriodWeight.Length))
                {//processing one of the first rows, before we've advanced enough to properly weight past rows
                    decimal avg = 0;

                    //Get the datarows representing the values within the WMA length
                    DataRow[] rows = dt.Select("Instance>=" + (i - PeriodWeight.Length).ToString() + " AND Instance < " + i.ToString(), "Instance");
                    for (int j = 0; j < rows.Length; j++)
                    {//apply an initial, uniform weight (1 / rows.Length) to the initial rows
                        avg += (Decimal)rows[j]["Value"] * (1 / rows.Length);
                    }
                    row["Forecast"] = avg;
                }
                else if ((i < values.Length) && (i >= PeriodWeight.Length))
                {//Out of initial rows and processing the test set
                    decimal avg = 0;

                    //Get the rows within the weight range just prior to the current row
                    DataRow[] rows = dt.Select("Instance>=" + (i - PeriodWeight.Length).ToString() + " AND Instance < " + i.ToString(), "Instance");
                    for (int j = 0; j <= rows.Length - 1; j++)
                    {//Apply the appropriate period's weight to the value
                        avg += (Decimal)rows[j]["Value"] * PeriodWeight[j];
                    }
                    //Assign the forecasted value to the current row
                    row["Forecast"] = avg;
                }
                else
                {//into the extension
                    decimal avg = 0;

                    DataRow[] rows = dt.Select("Instance>=" + (i - PeriodWeight.Length).ToString() + " AND Instance < " + i.ToString(), "Instance");
                    for (int j = 0; j < rows.Length; j++)
                    {//with no actual values to weight, use the previous rows' forecast instead
                        avg += (Decimal)rows[j]["Forecast"] * PeriodWeight[j];
                    }
                    row["Forecast"] = avg;
                }
                row.EndEdit();
            }

            dt.AcceptChanges();
            return(dt);
        }
        //Bayes: use prior actual value as forecast
        public ForecastTable naive(decimal[] values, int Extension, int Holdout)
        {
            ForecastTable dt = new ForecastTable();

            try
            {
                for (Int32 i = 0; i < (values.Length + Extension); i++)
                {
                    //Insert a row for each value in set
                    DataRow row = dt.NewRow();
                    dt.Rows.Add(row);

                    row.BeginEdit();
                    //assign its sequence number
                    row["Instance"] = i;

                    //if i is in the holdout range of values
                    //row["Holdout"] = (i > (values.Length - 1 - Holdout)) && (i < values.Length);

                    if (i < values.Length)
                    { //processing values which actually occurred
                        //Assign the actual value to the DataRow
                        row["Value"] = values[i];
                        if (i == 0)
                        {
                            //first row, value gets itself
                            row["Forecast"] = values[i];
                        }
                        else
                        {
                            //Put the prior row's value into the current row's forecasted value
                            row["Forecast"] = values[i - 1];
                        }
                    }
                    else
                    {//Extension rows
                        row["Forecast"] = values[values.Length - 1];
                    }
                    row.EndEdit();
                }
                dt.AcceptChanges();
                return dt;
            }
            catch (Exception ex)
            {

            }
            return null;
        }
        //
        //Simple Moving Average
        //
        //            ( Dt + D(t-1) + D(t-2) + ... + D(t-n+1) )
        //  F(t+1) =  -----------------------------------------
        //                              n
        public ForecastTable simpleMovingAverage(decimal[] values, int Extension, int Periods, int Holdout)
        {
            ForecastTable dt = new ForecastTable();

            for (Int32 i = 0; i < values.Length + Extension; i++)
            {
                //Insert a row for each value in set
                DataRow row = dt.NewRow();
                dt.Rows.Add(row);

                row.BeginEdit();
                //assign its sequence number
                row["Instance"] = i;
                if (i < values.Length)
                {//processing values which actually occurred
                    row["Value"] = values[i];
                }

                //Indicate if this is a holdout row
                //row["Holdout"] = (i > (values.Length - Holdout)) && (i < values.Length);

                if (i == 0)
                {//Initialize first row with its own value
                    row["Forecast"] = values[i];
                }
                else if (i <= values.Length - Holdout)
                {//processing values which actually occurred, but not in holdout set
                    decimal avg = 0;
                    DataRow[] rows = dt.Select("Instance>=" + (i - Periods).ToString() + " AND Instance < " + i.ToString(), "Instance");
                    foreach (DataRow priorRow in rows)
                    {
                        avg += (Decimal)priorRow["Value"];
                    }
                    avg /= rows.Length;

                    row["Forecast"] = avg;
                }
                else
                {//must be in the holdout set or the extension
                    decimal avg = 0;

                    //get the Periods-prior rows and calculate an average actual value
                    DataRow[] rows = dt.Select("Instance>=" + (i - Periods).ToString() + " AND Instance < " + i.ToString(), "Instance");
                    foreach (DataRow priorRow in rows)
                    {
                        if ((Int32)priorRow["Instance"] < values.Length)
                        {//in the test or holdout set
                            avg += (Decimal)priorRow["Value"];
                        }
                        else
                        {//extension, use forecast since we don't have an actual value
                            avg += (Decimal)priorRow["Forecast"];
                        }
                    }
                    avg /= rows.Length;

                    //set the forecasted value
                    row["Forecast"] = avg;
                }
                row.EndEdit();
            }

            dt.AcceptChanges();
            return dt;
        }
예제 #19
0
        //
        //Simple Moving Average
        //
        //            ( Dt + D(t-1) + D(t-2) + ... + D(t-n+1) )
        //  F(t+1) =  -----------------------------------------
        //                              n
        public ForecastTable simpleMovingAverage(decimal[] values, int Extension, int Periods, int Holdout)
        {
            ForecastTable dt = new ForecastTable();

            for (Int32 i = 0; i < values.Length + Extension; i++)
            {
                //Insert a row for each value in set
                DataRow row = dt.NewRow();
                dt.Rows.Add(row);

                row.BeginEdit();
                //assign its sequence number
                row["Instance"] = i;
                if (i < values.Length)
                {//processing values which actually occurred
                    row["Value"] = values[i];
                }

                //Indicate if this is a holdout row
                //row["Holdout"] = (i > (values.Length - Holdout)) && (i < values.Length);

                if (i == 0)
                {//Initialize first row with its own value
                    row["Forecast"] = values[i];
                }
                else if (i <= values.Length - Holdout)
                {//processing values which actually occurred, but not in holdout set
                    decimal   avg  = 0;
                    DataRow[] rows = dt.Select("Instance>=" + (i - Periods).ToString() + " AND Instance < " + i.ToString(), "Instance");
                    foreach (DataRow priorRow in rows)
                    {
                        avg += (Decimal)priorRow["Value"];
                    }
                    avg /= rows.Length;

                    row["Forecast"] = avg;
                }
                else
                {//must be in the holdout set or the extension
                    decimal avg = 0;

                    //get the Periods-prior rows and calculate an average actual value
                    DataRow[] rows = dt.Select("Instance>=" + (i - Periods).ToString() + " AND Instance < " + i.ToString(), "Instance");
                    foreach (DataRow priorRow in rows)
                    {
                        if ((Int32)priorRow["Instance"] < values.Length)
                        {//in the test or holdout set
                            avg += (Decimal)priorRow["Value"];
                        }
                        else
                        {//extension, use forecast since we don't have an actual value
                            avg += (Decimal)priorRow["Forecast"];
                        }
                    }
                    avg /= rows.Length;

                    //set the forecasted value
                    row["Forecast"] = avg;
                }
                row.EndEdit();
            }

            dt.AcceptChanges();
            return(dt);
        }
        //
        //Weighted Moving Average
        //            
        //  F(t+1) =  (Weight1 * D(t)) + (Weight2 * D(t-1)) + (Weight3 * D(t-2)) + ... + (WeightN * D(t-n+1))
        //        
        public ForecastTable weightedMovingAverage(decimal[] values, int Extension, params decimal[] PeriodWeight)
        {
            //PeriodWeight[].Length is used to determine the number of periods over which to average
            //PeriodWeight[x] is used to apply a weight to the prior period's value

            //Make sure PeriodWeight values add up to 100%
            decimal test = 0;
            foreach (decimal weight in PeriodWeight)
            {
                test += weight;
            }
            if (test != 1)
            {
                MessageBox.Show("Period weights must add up to 1.0", "Invalid parameters", MessageBoxButtons.OK);
                return null;
            }

            ForecastTable dt = new ForecastTable();

            for (Int32 i = 0; i < values.Length + Extension; i++)
            {
                //Insert a row for each value in set
                DataRow row = dt.NewRow();
                dt.Rows.Add(row);

                row.BeginEdit();
                //assign its sequence number
                row["Instance"] = i;

                if (i < values.Length)
                {//we're in the test set
                    row["Value"] = values[i];
                }

                if (i == 0)
                {//initialize forecast with first row's value
                    row["Forecast"] = values[i];
                }
                else if ((i < values.Length) && (i < PeriodWeight.Length))
                {//processing one of the first rows, before we've advanced enough to properly weight past rows
                    decimal avg = 0;

                    //Get the datarows representing the values within the WMA length
                    DataRow[] rows = dt.Select("Instance>=" + (i - PeriodWeight.Length).ToString() + " AND Instance < " + i.ToString(), "Instance");
                    for (int j = 0; j < rows.Length; j++)
                    {//apply an initial, uniform weight (1 / rows.Length) to the initial rows
                        avg += (Decimal)rows[j]["Value"] * (1 / rows.Length);
                    }
                    row["Forecast"] = avg;
                }
                else if ((i < values.Length) && (i >= PeriodWeight.Length))
                {//Out of initial rows and processing the test set
                    decimal avg = 0;

                    //Get the rows within the weight range just prior to the current row
                    DataRow[] rows = dt.Select("Instance>=" + (i - PeriodWeight.Length).ToString() + " AND Instance < " + i.ToString(), "Instance");
                    for (int j = 0; j <= rows.Length - 1; j++)
                    {//Apply the appropriate period's weight to the value
                        avg += (Decimal)rows[j]["Value"] * PeriodWeight[j];
                    }
                    //Assign the forecasted value to the current row
                    row["Forecast"] = avg;
                }
                else
                {//into the extension
                    decimal avg = 0;

                    DataRow[] rows = dt.Select("Instance>=" + (i - PeriodWeight.Length).ToString() + " AND Instance < " + i.ToString(), "Instance");
                    for (int j = 0; j < rows.Length; j++)
                    {//with no actual values to weight, use the previous rows' forecast instead
                        avg += (Decimal)rows[j]["Forecast"] * PeriodWeight[j];
                    }
                    row["Forecast"] = avg;
                }
                row.EndEdit();
            }

            dt.AcceptChanges();
            return dt;
        }