如何使用 VBA 宏在 Excel 表中查找条件累积和
Posted
技术标签:
【中文标题】如何使用 VBA 宏在 Excel 表中查找条件累积和【英文标题】:How to find conditional cumulative sums in an excel table using VBA macro 【发布时间】:2016-07-05 22:34:27 【问题描述】:假设我有两列。
3.5463 11
4.5592 12
1.6993 111
0.92521 112
1.7331 121
2.1407 122
1.4082 1111
2.0698 1112
2.3973 1121
2.4518 1122
1.1719 1211
1.153 1212
0.67139 1221
0.64744 1222
1.3705 11111
0.9557 11112
0.64868 11121
0.7325 11211
0.58874 11212
0.86673 11221
0.17075 11222
0.64026 12111
0.80229 12112
0.43422 12122
1.0405 12211
0.63376 12212
0.56491 12221
0.34626 12222
0.81631 111111
0.91837 111112
0.70013 111121
0.87384 111122
1.1474 111211
0.47411 111221
0.12249 111222
0.56728 112111
0.88169 112112
0.14509 112121
0.68655 112211
0.36274 112212
1.1652 121111
0.99314 121112
0.42024 121121
0.23937 121122
1.0346 122111
0.64642 122112
0.15632 122121
0.41725 122122
0.40793 122211
在第一列,有一个数字。在第二列中,这些数字中的每一个都是一个关联的 ID。现在,有一些空白行中不包含任何数字。
如果第一个号码的 ID 与第二个号码的 ID 相同,则将其中一个号码定义为另一个号码的“女儿”,并在末尾增加一个数字。例如,ID 11211 和 11212 都是 1121 的女儿,因为 1121 的 ID 在末尾添加了一个额外的数字,即 1 或 2,以形成其女儿的 ID。因此,1121 是 11211 和 11212 的父代。
这是我希望宏执行的操作。它必须输出第三列,其中包含,对于每一行,该行中第一列的编号加上该编号的父编号,以及父编号的父编号等的累积总和。直到它到达 11 或 12。它将首先在第 1 列中简单地输出 11 和 12 中的第三列中的数字。然后,在从 111 开始的循环中,它将累加每一行的总和(该行中的数字加上父级的第三列输出),仅当该行有一个数字和一个 id 时,并且仅当父级存在并且在第 3 列中有输出。 例如,ID 为 11222 的行的第 3 列中的数字应该是该行第 1 列中的数字加上 1122 的数字,再加上112,加上 11。所以,0.17075+2.4518+0.92521+3.5463,或 7.09406。但是,如果您尝试对 ID 111221 执行此操作,您会注意到父级 11122 应为空的行。因此,父节点不存在,111221 的第 3 列将不会输出任何值。
如果有人有时间为我编写此 VBA 宏以换取可接受的解决方案,我将不胜感激。
谢谢
【问题讨论】:
欢迎来到 SO。不幸的是,SO 不是一个免费的、为我编写代码的平台。通常,用户会希望您提出自己的解决方案的一部分,或者您无法解决的问题的非常具体的部分——而不是您希望从头开始为您编码的问题定义。 IMO,您在 excelforums.com 之类的用户可能会这样做的地方有更好的机会。或者,更好的是,自己编写一个脚本,如果您有任何具体问题,请联系我们 - 我们很乐意在这一点上提供帮助。 只是一个提示..尝试SUMIF
函数..
老实说,虽然这对于 VBA 来说看起来不够复杂,但它并不像简单的“SUMIF”那样容易以可接受的方式解决它。使用=SUM(SUMIF(B$2:B4,1*LEFT(B4,ROW(A$2:INDEX(A:A,LEN(B4)))),A$2:A4))
之类的东西确实有效,但是每增加一行数据,计算时间就会大大增加。即使只是这个例子也可能已经冻结了一些人。像=SUM(IF(B$2:B4=TRANSPOSE(1*LEFT(B4,ROW(A$2:INDEX(A:A,LEN(B4))))),A$2:A4))
这样的普通数组也可以做到。 (如果不使用辅助列在没有数组公式的情况下解决它)
@DirkReichel - 我已经为此制定了一个原生数组公式和一个 UDF(等待 OP 显示努力),但 OP 的最终条件仍然缺乏该公式。也就是说:如果链中的任何父级都不存在,则返回一个空字符串(在倒数第二段的末尾进行了描述)。 UDF 并不难得到,但我放弃了数组公式。至于使用数组公式,像这样的ID编号系统,可能有多少条记录?
@Jeeped 我完全错过了......然后根本不需要数组:D
【参考方案1】:
我认为不需要宏,只需要一些公式。首先,我在我的数据列上放置了一个标题,例如“value”和“id”。如果您随后突出显示列标签(即 A 和 B)并按 B(“id”)然后 A(“value”)排序,您将对空白行进行分组。然后,您可以删除这些行。现在你的数据几乎准备好了。当我这样做时,我将 id 列转换为文本,而不是数字值,所以如果我按 id 对表进行排序,模式将是“11、111、1111”等等,而不是“ 11、12、111、112、121。”然后,我添加了列来分隔 id 的单独字符或级别。这是为了帮助父母和孩子。您可以使用 text-to-columns 或 MID 公式,但我所做的是右侧多了 6 个列。对于每个 id 行,每列将具有“1”、“2”或空白(null)值。然后我添加了另一列,称之为“级别”。我在所有 id 拆分列中都使用了类似 COUNTA 的公式。因此,对于 11,我的等级值为 2。111 为 3,11221 为 5,依此类推。这给了我 id 级别(父母、孩子、孙子等)。然后我将最后一列添加到右侧以计算我的值的累积总和。在概念上,我有一个大的嵌套 IF 语句,但在实践中,我需要两个。我的公式说,如果我上面的行有一个较低级别的数字(即,它是某种父级),则将当前行的值添加到上面行的值中。否则,继续上升一行,直到我得到一个父级,并将当前行值添加到该数字。
我对除前 5 行数据之外的所有数据的最终公式是(在第 6 行数据中): =如果(K6
【讨论】:
【参考方案2】:其余答案如下
=if(K6<K7,L6+C7,if(K5<K7,L5+C7,if(K4<K7,L4+C7,if(K3<K7,L3+C7,if(K2<K7,L2+C7,C7)))))
值是 C 列,D 列中的原始 id,id 拆分列是 E 到 J,级别列是 K,我的公式在 L。这个公式可以复制到表中。对于前 4 行,你只需要每行少 1 个 IF 语句。第五行数据可能采用上述公式;这取决于它将如何处理第一行中的列标题。第4行数据的公式可能是:
=if(K4<K5,L4+C5,if(K3<K5,L3+C5,if(K2<K5,L2+C5,if(K1<K5,L1+C5,C5))))
我仍在学习如何格式化这些 cmets,所以我将尝试提供我所拥有的布局示例...
C D E F G H I J K L
1 value id 1 2 3 4 5 6 lvl cumul_sum
2 3.546300 11 1 1 2 3.546300
3 1.699300 111 1 1 1 3 5.245600
4 1.408200 1111 1 1 1 1 4 6.653800
5 1.370500 11111 1 1 1 1 1 5 8.024300
6 0.816310 111111 1 1 1 1 1 1 6 8.840610
7 0.918370 111112 1 1 1 1 1 2 6 8.942670
8 0.955700 11112 1 1 1 1 2 5 7.609500
【讨论】:
【参考方案3】:例如,ID 为 11222 的行的第 3 列的数字应该是该行的第 1 列的数字,加上 1122 的数字,再加上 112 的数字,再加上 11 的数字。所以, 0.17075+2.4518+0.92521+3.5463 或 7.09406。但是,如果您尝试对 ID 111221 执行此操作,您会注意到父 11122 应为空的行。因此,父级不存在,111221的第3列不会输出任何值。
作为 D1 中的原生工作表数组公式¹,
=IF(LEN(B1), SUM(SUMIFS(A$1:INDEX(A:A, MATCH(1E+99, A:A)),
B$1:INDEX(B:B, MATCH(1E+99, A:A)), LEFT(B1, ROW(INDIRECT("2:"&LEN(B1)))))), TEXT(,))
以上内容不补偿缺失的父母(空字符串)。它汇总了它可以找到的所有内容,并使用零来表示失踪的父母。
作为 E1 中的 VBA UDF²,
Function conditionalCumulativeSum(nums As Range, _
ids As Range, sib As Range, _
Optional nullOnBlank As Boolean = True)
Dim i As Integer
'truncate any full column reference to the UsedRange
Set nums = Intersect(nums, nums.Parent.UsedRange)
'match the nums and ids ranges
Set ids = ids.Resize(nums.Rows.Count, nums.Columns.Count)
For i = Len(sib.Value2) To 2 Step -1
If nullOnBlank And IsError(Application.Match(--Left(sib, i), ids, 0)) Then
conditionalCumulativeSum = vbNullString
Exit For
End If
conditionalCumulativeSum = conditionalCumulativeSum + _
Application.SumIfs(nums, ids, Left(sib, i))
Next i
If i = 0 Then conditionalCumulativeSum = vbNullString
End Function
上面默认通过遗传链遇到任何缺失的父节点时返回一个空字符串。这可以通过添加 FALSE 作为可选的第四个参数来关闭,然后 UDF 的行为将与本机公式相同。
Results 来自样本数据
¹ 数组公式需要使用 Ctrl+Shift+Enter↵ 完成。如果输入正确,Excel 会将公式用大括号括起来(例如 和 )。你不用自己输入大括号。一旦正确输入第一个单元格,它们就可以像任何其他公式一样被填充或向下或向右复制。尝试将全列引用减少到更接近代表实际数据范围的范围。数组公式以对数方式消耗计算周期,因此最好将引用范围缩小到最小值。请参阅Guidelines and examples of array formulas 了解更多信息。
² 用户定义函数(又名 UDF)被放入标准模块代码表中。点击Alt+F11,当VBE打开时,立即使用下拉菜单Insert ► Module(Alt+I,M)。将功能代码粘贴到标题为 Book1 - Module1 (Code) 的新模块代码表中。点击 Alt+Q 返回您的工作表。
【讨论】:
以上是关于如何使用 VBA 宏在 Excel 表中查找条件累积和的主要内容,如果未能解决你的问题,请参考以下文章
Excel VBA 一段宏在64位系统中运行会报错,32位系统运行完美,求解决方案?