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

Excel 根据每日值(包括缺失数据)计算加权月平均值,excel,Excel,我有一份多年来产品价格变化的清单,以及当天价格变化的相关日期。然而,这些价格变化很少,这意味着我可能在一年内只有4个数据点。最重要的是,在这些价格变化点之间,价格从上次报告的点保持不变,即使数据中没有列出 如何计算未显示数据的天数的加权月平均值 例如,2015年1月项目3的平均值应为1月31日平均值,即21天的价格为110,7天的价格为94,3天的价格为88。因此,这是104.26 我曾尝试在a中使用建议的公式,但这只是以2015年1月列出的值的平均值为例。答案是97.33,而不是104.26

我有一份多年来产品价格变化的清单,以及当天价格变化的相关日期。然而,这些价格变化很少,这意味着我可能在一年内只有4个数据点。最重要的是,在这些价格变化点之间,价格从上次报告的点保持不变,即使数据中没有列出

如何计算未显示数据的天数的加权月平均值

例如,2015年1月
项目3
的平均值应为1月31日平均值,即21天的价格为110,7天的价格为94,3天的价格为88。因此,这是104.26

我曾尝试在a中使用建议的公式,但这只是以2015年1月列出的值的平均值为例。答案是97.33,而不是104.26

最终输出表应为时间序列表,从每年的1月到12月开始例如


看看这是否适合您。将下面的代码粘贴到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
公共函数CalculateAveragePrice(ByVal strItemNo作为字符串,ByVal dtMonth作为日期,ByVal rngData作为范围)作为变量
Dim lngRow为Long,lngCol为Long,lngItemCol为Long,i为Long,bFoundTop为Boolean,lngYear为Long
Dim dtThisDate为日期,dtComparisonDate为日期,arrDays()为双精度,dtNextDate为日期,长度为1个月
Dim lngBottomRow为长,lngTopRow为长,lngDaysInMonth为长,DBL当前价格为双
'用一些东西初始化数组。
重拨天数(0)
lngMonth=月份(dtMonth)
lngYear=年(月)
与恩格达塔
'查找项目的标题列
对于lngItemCol=1到.Columns.Count
如果.Cells(1,lngItemCol)=strItemNo,则退出
下一个
对于i=1到2
如果i=1,那么
DTComparisonData=DateSerial(年(月),月(月),1)
其他的
dtcomparisonadate=DateAdd(“d”,-1,DateAdd(“m”,1,dtMonth))
lngDaysInMonth=天(dtComparisonDate)
雷迪姆保留天数(lngDaysInMonth)
如果结束
对于lngRow=2到.Rows.Count
dtThisDate=.Cells(lngRow,1)
如果i=1,那么
如果dtThisDatedtComparisonDate而不是bFoundTop,则
lngTopRow=lngRow
bFoundTop=True
如果结束
如果结束
下一个
下一个
如果lngTopRow=0,则lngTopRow=.Rows.Count
如果lngBottomRow=0,则lngBottomRow=2
对于i=1至UBound(arrDays)
对于lngRow=lngTopRow至lngBottomRow步骤-1
dtThisDate=.Cells(lngRow,1)
dblCurrentPrice=.Cells(lngRow,lngItemCol)
如果dtThisDate为0,则CalculateAveragePrice=arrDays(UBound(arrDays))/UBound(arrDays)
以
端函数
。。。然后将公式设置为如下所示

该代码也适用于远远超出给定价格范围的日期。这对你来说可能并不重要,但只是需要注意的事情

也许有一种更优雅的方式,但它对我有效,我相信它也会对你有效


看看它是如何运行的。

数组公式解决方案如下所示:

=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))))
     )
)
=平均值(
如果((行(索引($A:$A,最小($B$3:$B$20)):索引($A:$A,日期(最大($B$3:$B$20)),12,31))>=G3)

*(行(索引($A:$A,MIN($B$3:$B$20)):索引($A:$A,DATE(MAX(YEAR($B$3:$B$20)),12,31)))如果允许使用“helper列”(一个包含每天的列,您可以将其隐藏),这将非常容易。但是一次计算结果的直接公式是相当困难的,因为你没有每个月至少一次和多次。辅助列对你来说是可行的选择吗?顺便说一句,2011年10月你的“理想输出”的平均值是错误的,应该是144(或143.77):)事实上,可以实现一个helper列,尽管理想情况下可以应用直接公式。理想的输出是一个使用以前错误公式的示例:)嗨,Skin,首先非常感谢您的努力!不过,我尝试过添加VBA模块,但当我使用CalculateAveragePrice公式时,我遇到了一个错误。我对VBA不太熟悉,知道是什么导致了这一点吗?好吧,对不起,我计算出了上面的#名称错误!。但是,这可能是日期格式问题吗?@Brian wow,这是不可能的预计。你能把工作表放在某个地方让我拿到吗?当然,如果它不敏感的话。OneDrive?DropBox?@Brian肯定与日期有关,因为我刚刚复制了前几个日期,等等。我得到了一个正确的结果。如果我能得到你的工作表的副本,我们就可以完成它。如果我得到了它,它对我来说直接有效然后是本地化…这当然是个麻烦。@Brian,我刚刚注意到一行代码可能导致了这个问题。你能复制答案中的代码然后再试一次吗?非常感谢Tom!我尝试复制第二个公式并输入为CSE,但是,整个输出都是for(所有输出都是22).你知道我可能做错了什么吗?没问题。我想知道你做的是在输入公式之前突出显示整个范围吗?这会给你同样的结果。你只需要将公式输入第一个单元格(我想是X3),然后将其拖动到同一列中的其他单元格中。我需要在公式中添加一些美元符号,这样您就可以将其拖动到另一列中,因此我将很快整理我的答案,但这不会影响当前问题。嘿,汤姆,这有助于将其完全清除。我添加了美元符号