简体   繁体   English

根据每日值(包括缺失数据)计算加权每月平均值

[英]Calculating weighted monthly average from daily values, including missing data

I have a list of price changes for a product over many years, with associated dates of changes in the price down to the day. 我有一个产品多年价格变化的清单,以及价格变化的相关日期,直到一天。 However, these prices change infrequently, meaning I may have only 4 data points in one year. 但是,这些价格很少变化,这意味着我一年可能只有4个数据点。 What is most important is that in between these price change points, prices remain constant from the last reported point, even though they are not listed in the data. 最重要的是,在这些价格变化点之间,即使未在数据中列出,价格也从上次报告的点开始保持恒定。

How do I find the weighted monthly average, accounting for the days in which data is not shown? 我如何找到未显示数据的天数的加权月平均值?

Excel数据

The average for Item3 in January 2015, for example, should end up a weighted average of 21 days of prices at 110, 7 days of prices at 94 and 3 days of prices at 88, averaged over the 31 days of January. 例如,2015年1月, Item3的平均值应以一月31天的平均加权平均价格为21天(110天),7天(94天)和3天(88天)的加权平均值。 This is therefore 104.26 因此,这是104.26

I've tried using the suggested formula in a previously asked question , but what that does is simply take the average of values listed in January 2015, for example. 我曾尝试在一个先前提出的问题中使用建议的公式,但是这样做只是以2015年1月列出的平均值为例。 That gives the answer of 97.33 instead of what should be 104.26 给出的答案是97.33,而不是104.26。

The final output table should be a time series table, starting with January to December for every year. 最终输出表应该是一个时间序列表,每年从一月到十二月开始。 For example : 例如

Ideal output 理想输出

See if this works for you. 看看这是否适合您。 Paste the below code into a new module in VBA ... 将以下代码粘贴到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

... and then setup your formula to look like the below ... ...然后将您的公式设置为如下所示...

公式设定

The code will also work for dates well outside the range of given prices. 该代码还将在日期超出给定价格范围内使用。 That may not be important for you but just something to note. 那对您来说可能并不重要,只是需要注意的一点。

There's probably a more elegant way but it works for me and I trust it will work for you too. 也许有一种更优雅的方法,但是它对我有用,我相信它也对您有用。

See how it goes. 看看进展如何。

An array formula solution would look like this: 数组公式解决方案如下所示:

=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))))
     )
)

It's basically the same as using a helper column to generate a date for each day across the whole range, except that it's generated inside the formula by the ROW(INDEX...)) statements. 它与使用helper列在整个范围内每天生成日期基本相同,除了它是由ROW(INDEX ...)语句在公式内部生成的。

Must be entered using Ctrl Shift Enter 必须使用Ctrl Shift 输入

在此处输入图片说明

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM