Расчет и обновление прогнозных данных взвешенного скользящего среднего с использованием набора записей ADO - PullRequest
0 голосов
/ 03 февраля 2019

В Access 2016 я пытаюсь вычислить взвешенные прогнозы скользящего среднего и обновляю свою таблицу прогнозов с результатами.

Мой код просматривает набор записей без каких-либо проблем, но моя текущая логика не будет вычислять взвешенные средние,На самом деле, он просто возвращает фактические продажи за каждый период.Я отлаживал это некоторое время и не могу обернуться вокруг него.

Я использую класс ADO для операций CRUD с сервером SQL в качестве бэкэнда через ODBC.В моем коде ниже я удалил обработку ошибок, чтобы сжать мой код.Ниже:

Sub WMAForecast( _
                    lngCompanyID As Long, _
                    lngItemID As Long, _
                    dtmStartDate As Date, _
                    dtmEndDate As Date, _
                    intPeriods As Integer)                    

    ' Object related declarations ->
    Dim objRs As ADODB.Recordset
    Dim objDb1 As clADO
    Dim objDb2 As clADO
    Dim objEh As clError
    Dim strSQL1 As String: strSQL1 = vbNullString
    Dim strSQL2 As String: strSQL2 = vbNullString

    '// Generics variables ->
    Dim lngRecords As Long: lngRecords = 0
    Dim lngDetailsCount: lngDetailsCount = 0
    Dim lngDetailRecords: lngDetailRecords = 0
    Dim dblReturn As Double: dblReturn = 0
    Dim dblTempSum As Double: dblTempSum = 0
    Dim dblWeightSum As Double: dblWeightSum = 0

    '// Loop counters ->
    Dim i As Long: i = 0
    Dim j As Long: j = 0
    Dim k As Long: k = 0

    '// Calculate the sum of weights ->
    dblWeightSum = intPeriods * (intPeriods + 1) / 2

    '// Declare an array to store the weights ->
    Dim arrWeights As Variant
    ReDim arrWeights(1 To intPeriods)

    '// Construct SQL ->
    strSQL1 = "SELECT Sum(ItemDemandHistory.DemandUnits) AS Issues, PlanningCalendar.WeekEndDate, ItemDemandHistory.ItemID " & _
    "FROM PlanningCalendar INNER JOIN ItemDemandHistory ON PlanningCalendar.WeekEndDate = ItemDemandHistory.WeekEndDate " & _
    "GROUP BY PlanningCalendar.WeekEndDate, ItemDemandHistory.ItemID, PlanningCalendar.CompanyID " & _
    "HAVING PlanningCalendar.WeekEndDate>=? " & _
    "AND PlanningCalendar.WeekEndDate<=? " & _
    "AND ItemDemandHistory.ItemID=? " & _
    "AND PlanningCalendar.CompanyID=?"

    '// Validate parameters ->
    If Not fIsNullOrEmpty(strSQL1) And _
        Not fIsNullOrEmpty(lngCompanyID) And _
        Not fIsNullOrEmpty(lngItemID) And _
        Not fIsNullOrEmpty(dtmStartDate) And _
        Not fIsNullOrEmpty(dtmStartDate) And _
        Not fIsNullOrEmpty(intPeriods) Then

        '// Initialize database ->
        Set objDb1 = New clADO
        With objDb1
            .Initialize DatabaseType:=DBTypeEnum.TypeODBC
            .CursorLocation = adUseClient: .CommandType = adCmdText: .CursorType = adOpenStatic

            '// Retrieve recordset ->
            Set objRs = .ExecuteQuery(strSQL1, dtmStartDate, dtmEndDate, lngItemID, lngCompanyID)
            With objRs
                If Not (.EOF And .BOF) Then
                    If .RecordCount > 0 Then

                        '// Collect the number of records ->
                        lngRecords = .RecordCount

                        '// Construct and array to store the cummulative values ->
                        Dim arrCumulative As Variant
                        ReDim arrCumulative(1 To lngRecords) As Double

                        '// Construct and array to store the cummulative values ->
                        Dim arrWMA As Variant
                        ReDim arrWMA(1 To lngRecords) As Double

                        '// Move cursor to first position ->
                        .MoveFirst

                        '// Traverse through the recordset ->
                        For i = 1 To lngRecords

                            '// Set counter defaults ->
                            dblTempSum = 0
                            k = 0

                            '// Check if first record and assign first value to cummulative array ->
                            If i = 1 Then
                                arrCumulative(i) = .Fields(0)
                            Else
                                arrCumulative(i) = .Fields(0) + arrCumulative(i - 1)
                            End If

                            '// At points <= period N, calculate a simple average ->
                            '// Example using 3 Periods: If N=3, MA(1) = first series point, MA(2) = Average(first two points), MA(3) = Average(first three points)...etc ->
                            If i <= intPeriods Then
                                arrWMA(i) = arrCumulative(i) / i
                            Else
                                '// When i > intPeriods, the moving average calculation kicks in ->
                                For j = i - intPeriods + 1 To i
                                    k = k + 1
                                    dblTempSum = dblTempSum + .Fields(0) * k
                                Next j
                                arrWMA(i) = dblTempSum / dblWeightSum

                                '// Initialize database ->
                                Set objDb2 = New clADO
                                With objDb2
                                    .Initialize DatabaseType:=DBTypeEnum.TypeODBC: .CommandType = adCmdText

                                    '// Construct SQL ->
                                    strSQL2 = "UPDATE ItemDemandForecast " & _
                                    "SET ForecastUnits=? " & _
                                    "WHERE CompanyID=? " & _
                                    "AND ItemID=? " & _
                                    "AND WeekEndDate=?"

                                    '// Execute SQL ->
                                    lngDetailRecords = .ExecuteNonQuery(strSQL2, CDbl(arrWMA(i)), lngCompanyID, lngItemID, objRs.Fields(1))

                                    '// Increment record count ->
                                    lngDetailsCount = lngDetailsCount + lngDetailRecords
                                End With
                            End If
                        .MoveNext
                        Next
                    End If
                End If
            End With
        End With
    End If

    '// Cleanup ->
    Erase arrCumulative
    Erase arrWMA
    Erase arrWeights
    If Not objRs Is Nothing Then Set objRs = Nothing
    If Not objDb1 Is Nothing Then Set objDb1 = Nothing
    If Not objDb2 Is Nothing Then Set objDb2 = Nothing
    If Not objEh Is Nothing Then Set objEh = Nothing

End Function

Ниже приведены мои данные с ожидаемыми результатами:

CompanyID   ItemID  Planning_Period Period_Ending   Demand_Units    Forecast_Units
1           10      1               2016-01-10      814             814
1           10      2               2016-01-17      1386            1386
1           10      3               2016-01-24      571             1100
1           10      4               2016-01-31      827             883.17
1           10      5               2016-02-07      1217            834.83
1           10      6               2016-02-14      1143            979.33
1           10      7               2016-02-21      1249            1115.00
1           10      8               2016-02-28      1303            1208.33
1           10      9               2016-03-06      1283            1258.33
1           10      10              2016-03-13      1379            1284.00
1           10      11              2016-03-20      990             1334.33
1           10      12              2016-03-27      1241            1168.50

Чтобы подвести итог моих целей:

  1. Рассчитать прогноз взвешенного скользящего среднего дляэлемент / период на основе предыдущей истории продаж.
  2. Используя рассчитанный прогноз, обновите прогноз для каждого соответствующего элемента / периода.

1 Ответ

0 голосов
/ 03 февраля 2019

Простой ответ заключается в том, что код не вычисляет скользящее среднее, поскольку код суммирования не ссылается на значения из предыдущих строк.


Сначала просмотрите этот код:

dblWeightSum = intPeriods * (intPeriods + 1) / 2

Это просто сумма целых чисел от 1 до intPeriods, например 1 + 2 + 3 + ... + intPeriods.

Теперь перейдите к коду

dblTempSum = 0
k = 0
...

For j = i - intPeriods + 1 To i
    k = k + 1
    dblTempSum = dblTempSum + .Fields(0) * k
Next j
arrWMA(i) = dblTempSum / dblWeightSum

Первое замечание, что нет предыдущихзначения, добавленные к сумме. Другими словами, она вообще не включает предыдущее значение.Нет ссылок на предыдущие значения.Следовательно, это не может быть скользящим средним для нескольких строк.

Далее, учтите, что общее число итераций цикла - просто intPeriods.k эффективно начинается с 1, затем считается от 1 до intPeriods.Каждые итерации цикла умножают одно и то же текущее значение .Fields(0) на текущее значение k.В целом цикл выдает сумму, переписанную следующим образом:

dblTempSum = .Fields(0) * (1 + 2 + 3 + ... + intPeriods)

Это выглядит знакомо?Это должно произойти, поскольку он содержит ту же сумму, что и в dblWeightSum, как объяснено ранее.

Следовательно, последняя строка кода из приведенного выше фрагмента в итоге уменьшается, как показано ниже ...

arrWMA(i) == dblTempSum / dblWeightSum
          == .Fields(0) * (1 + 2 + 3 + ... + intPeriods) / (1 + 2 + 3 + ... + intPeriods)
          == .Fields(0)

Значение arrWMA(i) - это то, что обновляет поле ForecastUnits в следующем коде.Таким образом, поле, предназначенное для скользящей средней, вместо этого заканчивается исходным одиночным значением из той же строки ... как вы уже видели.


Извините, но я не могу опубликоватьправильный взвешенный код скользящего среднего прямо сейчас.Однако ключ заключается в том, чтобы заменить текущий цикл из приведенного выше фрагмента суммой текущего взвешенного значения минус предыдущая взвешенная сумма.Чтобы сделать это правильно, я думаю, что вам нужен как минимум еще один массив для хранения взвешенной суммы И вы должны вычесть значения из существующей суммы, которые выходят за пределы размера скользящего периода (intPeriod).Для точных шагов просмотрите проверенный алгоритм.

...