Вычисление средневзвешенного месячного значения по дневным значениям, включая отсутствующие данные - PullRequest
2 голосов
/ 09 апреля 2019

У меня есть список изменений цены на продукт за многие годы с соответствующими датами изменения цены вниз по дням.Однако эти цены меняются нечасто, то есть у меня может быть только 4 точки данных за один год. Что наиболее важно, так это то, что между этими точками изменения цены цены остаются постоянными с момента последней сообщенной точки, даже если они не перечислены в данных.

Как найти взвешенныйсреднемесячные, с учетом дней, в которые данные не отображаются

Excel data

Например, среднее значение для Item3 в январе 2015 года должно составить средневзвешенное значение для 21-дневных цен на 110, 7-дневных цен на 94 и 3.дней цены на 88, в среднем за 31 дней января.Таким образом, это 104,26

Я пытался использовать предложенную формулу в ранее заданном вопросе , но для этого достаточно просто взять среднее значение, указанное в январе 2015 года, например.Это дает ответ 97,33 вместо того, что должно быть 104,26

Окончательная выходная таблица должна быть таблицей временных рядов, начиная с января по декабрь для каждого года. Например :

Идеальный вывод

Ответы [ 2 ]

2 голосов
/ 09 апреля 2019

Посмотрите, работает ли это для вас. Вставьте приведенный ниже код в новый модуль в VBA ...

Public Function CalculateAveragePrice(ByVal strItemNo As String, ByVal dtMonth As Date, ByVal rngData As Range) As Variant
    Dim lngRow As Long, lngCol As Long, lngItemCol As Long, i As Long, bFoundTop As Boolean, lngYear As Long
    Dim dtThisDate As Date, dtComparisonDate As Date, arrDays() As Double, dtNextDate As Date, lngMonth As Long
    Dim lngBottomRow As Long, lngTopRow As Long, lngDaysInMonth As Long, dblCurrentPrice As Double

    ' Initialise the array with something.
    ReDim arrDays(0)

    lngMonth = Month(dtMonth)
    lngYear = Year(dtMonth)

    With rngData
        ' Find the header column for the item
        For lngItemCol = 1 To .Columns.Count
            If .Cells(1, lngItemCol) = strItemNo Then Exit For
        Next

        For i = 1 To 2
            If i = 1 Then
                dtComparisonDate = DateSerial(Year(dtMonth), Month(dtMonth), 1)
            Else
                dtComparisonDate = DateAdd("d", -1, DateAdd("m", 1, dtMonth))
                lngDaysInMonth = Day(dtComparisonDate)
                ReDim Preserve arrDays(lngDaysInMonth)
            End If

            For lngRow = 2 To .Rows.Count
                dtThisDate = .Cells(lngRow, 1)

                If i = 1 Then
                    If dtThisDate < dtComparisonDate Then lngBottomRow = lngRow
                Else
                    If dtThisDate > dtComparisonDate And Not bFoundTop Then
                        lngTopRow = lngRow
                        bFoundTop = True
                    End If
                End If
            Next
        Next

        If lngTopRow = 0 Then lngTopRow = .Rows.Count
        If lngBottomRow = 0 Then lngBottomRow = 2

        For i = 1 To UBound(arrDays)
            For lngRow = lngTopRow To lngBottomRow Step -1
                dtThisDate = .Cells(lngRow, 1)
                dblCurrentPrice = .Cells(lngRow, lngItemCol)

                If dtThisDate <= DateSerial(lngYear, lngMonth, i) Then
                    arrDays(i) = dblCurrentPrice + arrDays(i - 1)
                    Exit For
                End If
            Next
        Next

        If UBound(arrDays) > 0 Then CalculateAveragePrice = arrDays(UBound(arrDays)) / UBound(arrDays)
    End With
End Function

... а затем настройте формулу так, как показано ниже ...

Formula Setup

Код также будет работать для дат, выходящих за пределы диапазона указанных цен. Это может быть не важно для вас, но просто кое-что отметить.

Возможно, есть более элегантный способ, но он работает для меня, и я верю, что он будет работать и для вас.

Посмотрите, как это происходит.

1 голос
/ 09 апреля 2019

Решение формулы массива будет выглядеть так:

=AVERAGE(
      IF((ROW(INDEX($A:$A,MIN($B$3:$B$20)):INDEX($A:$A,DATE(MAX(YEAR($B$3:$B$20)),12,31)))>=$G3)
     *(ROW(INDEX($A:$A,MIN($B$3:$B$20)):INDEX($A:$A,DATE(MAX(YEAR($B$3:$B$20)),12,31)))<=EOMONTH($G3,0)),
      INDEX(C$3:C$20,N(IF({1},MATCH(ROW(INDEX($A:$A,MIN($B$3:$B$20)):INDEX($A:$A,DATE(MAX(YEAR($B$3:$B$20)),12,31))),$B$3:$B$20))))
     )
)

Это в основном то же самое, что использование вспомогательного столбца для генерации даты для каждого дня во всем диапазоне, за исключением того, что он генерируется внутри формулы операторами ROW (INDEX ...)).

Необходимо ввести с помощью Ctrl Shift Введите

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...