使用 ARRAYFORMULA 计算 运行 应付账款总额(替代 INDIRECT)
Using ARRAYFORMULA to Calculate Running Total of Payables (Alternative to INDIRECT)
我使用 Google Spreadsheet 来跟踪每个供应商的应付账款。 Spreadsheet 中每个供应商有一个 sheet。简化的 sheet 看起来像这样:
当我收到新发票时,会在 Credit
列中输入金额,当我发放付款时,会在 Debit
列中输入金额。我在 AC Payable
列中跟踪 运行 总数。我通过在 AC Payable
列的每个单元格中使用一个公式来实现这一点(下面的示例来自单元格 E4
):
=IF(
ISNUMBER(INDIRECT(ADDRESS(ROW()-1,COLUMN()))),
INDIRECT(ADDRESS(ROW()-1,COLUMN()))+C4-D4,
C4-D4
)
逻辑很简单。 n
行的 运行 总计计算公式为:
AC Payable(n - 1) + Credit(n) - Debit(n)
此设置工作正常,除了我必须将公式拖到新添加的行中。有没有办法通过使用 ARRAYFORMULA
?
来实现这一点
PS: 我找到了一个解决方案:
= ARRAYFORMULA(
SUMIF(
ROW(C3:C),
"<="&ROW(C3:C),
C3:C)
-
SUMIF(
ROW(D3:D),
"<="&ROW(D3:D),
D3:D
)
)
我觉得这是一个次优的解决方案(最初的 sheet 可以追溯到 2018 年。它有很多行)解决方案,因为在每一行中,它都会计算 Debit
和 Credit
列到当前行,然后从 Credit
列的总和中减去 Debit
列的总和。
我期待一个解决方案,它可以利用上一行中可用的 运行 总数,而不是每行重做整个计算。
最多 1581 行的解决方案:
=ARRAYFORMULA(QUERY(QUERY(MMULT(TRANSPOSE((SEQUENCE(COUNTA(A3:A)*2)<=
SEQUENCE(1, COUNTA(A3:A)*2))*FLATTEN(INDIRECT("C3:D"&COUNTA(A3:A)+ROW(A3)-1)*{1, -1})),
SEQUENCE(COUNTA(A3:A)*2, 1, 1, 0)), "offset 1", ), "skipping 2", ))
技能:
- 速度很快
- 很聪明
- 添加的行越多,速度越慢
- 在 1581 行后死亡
它基于标准 MMULT Running/Cumulative Total/Sum 公式:
=ARRAYFORMULA(MMULT(TRANSPOSE((ROW(B1:B6)
<=TRANSPOSE(ROW(B1:B6)))*B1:B6), SIGN(B1:B6)))
但有一个修改扭曲,因为你总共有 2 列
而不是 ROW(B1:B6)
我们使用实际数据的计数序列乘以 2(因为你有 2 列):
SEQUENCE(COUNTA(A3:A)*2)
而不是 TRANSPOSE(ROW(B1:B6))
我们再次使用:
SEQUENCE(1, COUNTA(A3:A)*2)
这些作品的组合:
=ARRAYFORMULA(TRANSPOSE((SEQUENCE(COUNTA(A3:A)*2)<=SEQUENCE(1, COUNTA(A3:A)*2))))
将生成如下矩阵:
这就是它死于大量行的原因,因为虽然您可能认为如果两列中只有 1500 行,那么公式将仅适用于 1500*2=3000
个虚拟单元格,但实际上MMULT 公式处理 (1500*2)*(1500*2)=9000000
个虚拟单元格。不过,值得注意的是,如果小规模部署,这个 MMULT fx 还是很棒的。
接下来,我们使用
而不是 *B1:B6
*FLATTEN(INDIRECT("C3:D"&COUNTA(A3:A)+ROW(A3)-1)*{1, -1}))
例如。对于 INDIRECT,我们只采用 C3:D 的“有效”范围,在您的示例中 sheet 只是 C3:D5,我们将 C 列乘以 1
,将 D 列乘以 -1
来模拟减法,然后我们将两列展平为一列。 +ROW(A3)-1
部分只是一个偏移量,因为您从第 3
行开始
并且标准 RT fx 的最后一部分 - SIGN(B1:B6)
被替换为一栏全是:
SEQUENCE(COUNTA(A3:A)*2, 1, 1, 0)
然后我们将内部 QUERY 的输出偏移 1,因为我们对减法后的总计感兴趣,最后我们使用 skipping 2
这意味着我们过滤掉每个第二个值 - 同样,我们对总计感兴趣减去 D 列后。
超过1581行的解决方法:
=ARRAYFORMULA(
SUMIF(SEQUENCE(COUNTA(A3:A)), "<="&SEQUENCE(COUNTA(A3:A)), INDIRECT("C3:C"&COUNTA(A3:A)))-
SUMIF(SEQUENCE(COUNTA(A3:A)), "<="&SEQUENCE(COUNTA(A3:A)), INDIRECT("D3:D"&COUNTA(A3:A))))
技能:
- 支持更多行
- 看起来不那么聪明
- 遗憾的是 SUMIF 的第三个参数总是需要一个范围
- 行数越多越慢
- 喂10000行它会生病
- 它可能会杀死你的 sheet 超过 11000 行
这里是Ben Collins' running total formula
的修改
=ARRAYFORMULA(
IF(ISBLANK(A2:A),,
MMULT(TRANSPOSE((ROW(C2:C)<=TRANSPOSE(ROW(C2:C)))*C2:C),SIGN(C2:C))-
MMULT(TRANSPOSE((ROW(D2:D)<=TRANSPOSE(ROW(D2:D)))*D2:D),SIGN(D2:D))))
MMULT 的另一种选择:
=INDEX(QUERY(FLATTEN(QUERY(QUERY(TRANSPOSE(QUERY(QUERY(TRANSPOSE(
(SEQUENCE(COUNTA(A3:A)*2)<=SEQUENCE(1, COUNTA(A3:A)*2))*
FLATTEN(INDIRECT("C3:D"&COUNTA(A3:A)+ROW(A3)-1)*{1, -1})),
"offset 1", ), "skipping 2", )), "select "&QUERY(
"sum(Col"&SEQUENCE(COUNTA(A3:A))&"),",, 9^9)&"' '"),
"offset 1", )), "where Col1 is not null", ))
但同样,LTE (<=
) 1000 万个单元格的限制不会让您在您的案例中使用超过 1581 行或在标准累积和案例中使用 3162 行
(1581 rows * 2 columns) raised on 2nd power < 10 million cells
(1581*2)^2 = 9998244
我使用 Google Spreadsheet 来跟踪每个供应商的应付账款。 Spreadsheet 中每个供应商有一个 sheet。简化的 sheet 看起来像这样:
当我收到新发票时,会在 Credit
列中输入金额,当我发放付款时,会在 Debit
列中输入金额。我在 AC Payable
列中跟踪 运行 总数。我通过在 AC Payable
列的每个单元格中使用一个公式来实现这一点(下面的示例来自单元格 E4
):
=IF(
ISNUMBER(INDIRECT(ADDRESS(ROW()-1,COLUMN()))),
INDIRECT(ADDRESS(ROW()-1,COLUMN()))+C4-D4,
C4-D4
)
逻辑很简单。 n
行的 运行 总计计算公式为:
AC Payable(n - 1) + Credit(n) - Debit(n)
此设置工作正常,除了我必须将公式拖到新添加的行中。有没有办法通过使用 ARRAYFORMULA
?
PS: 我找到了一个解决方案:
= ARRAYFORMULA(
SUMIF(
ROW(C3:C),
"<="&ROW(C3:C),
C3:C)
-
SUMIF(
ROW(D3:D),
"<="&ROW(D3:D),
D3:D
)
)
我觉得这是一个次优的解决方案(最初的 sheet 可以追溯到 2018 年。它有很多行)解决方案,因为在每一行中,它都会计算 Debit
和 Credit
列到当前行,然后从 Credit
列的总和中减去 Debit
列的总和。
我期待一个解决方案,它可以利用上一行中可用的 运行 总数,而不是每行重做整个计算。
最多 1581 行的解决方案:
=ARRAYFORMULA(QUERY(QUERY(MMULT(TRANSPOSE((SEQUENCE(COUNTA(A3:A)*2)<=
SEQUENCE(1, COUNTA(A3:A)*2))*FLATTEN(INDIRECT("C3:D"&COUNTA(A3:A)+ROW(A3)-1)*{1, -1})),
SEQUENCE(COUNTA(A3:A)*2, 1, 1, 0)), "offset 1", ), "skipping 2", ))
技能:
- 速度很快
- 很聪明
- 添加的行越多,速度越慢
- 在 1581 行后死亡
它基于标准 MMULT Running/Cumulative Total/Sum 公式:
=ARRAYFORMULA(MMULT(TRANSPOSE((ROW(B1:B6)
<=TRANSPOSE(ROW(B1:B6)))*B1:B6), SIGN(B1:B6)))
但有一个修改扭曲,因为你总共有 2 列
而不是 ROW(B1:B6)
我们使用实际数据的计数序列乘以 2(因为你有 2 列):
SEQUENCE(COUNTA(A3:A)*2)
而不是 TRANSPOSE(ROW(B1:B6))
我们再次使用:
SEQUENCE(1, COUNTA(A3:A)*2)
这些作品的组合:
=ARRAYFORMULA(TRANSPOSE((SEQUENCE(COUNTA(A3:A)*2)<=SEQUENCE(1, COUNTA(A3:A)*2))))
将生成如下矩阵:
这就是它死于大量行的原因,因为虽然您可能认为如果两列中只有 1500 行,那么公式将仅适用于 1500*2=3000
个虚拟单元格,但实际上MMULT 公式处理 (1500*2)*(1500*2)=9000000
个虚拟单元格。不过,值得注意的是,如果小规模部署,这个 MMULT fx 还是很棒的。
接下来,我们使用
而不是*B1:B6
*FLATTEN(INDIRECT("C3:D"&COUNTA(A3:A)+ROW(A3)-1)*{1, -1}))
例如。对于 INDIRECT,我们只采用 C3:D 的“有效”范围,在您的示例中 sheet 只是 C3:D5,我们将 C 列乘以 1
,将 D 列乘以 -1
来模拟减法,然后我们将两列展平为一列。 +ROW(A3)-1
部分只是一个偏移量,因为您从第 3
并且标准 RT fx 的最后一部分 - SIGN(B1:B6)
被替换为一栏全是:
SEQUENCE(COUNTA(A3:A)*2, 1, 1, 0)
然后我们将内部 QUERY 的输出偏移 1,因为我们对减法后的总计感兴趣,最后我们使用 skipping 2
这意味着我们过滤掉每个第二个值 - 同样,我们对总计感兴趣减去 D 列后。
超过1581行的解决方法:
=ARRAYFORMULA(
SUMIF(SEQUENCE(COUNTA(A3:A)), "<="&SEQUENCE(COUNTA(A3:A)), INDIRECT("C3:C"&COUNTA(A3:A)))-
SUMIF(SEQUENCE(COUNTA(A3:A)), "<="&SEQUENCE(COUNTA(A3:A)), INDIRECT("D3:D"&COUNTA(A3:A))))
技能:
- 支持更多行
- 看起来不那么聪明
- 遗憾的是 SUMIF 的第三个参数总是需要一个范围
- 行数越多越慢
- 喂10000行它会生病
- 它可能会杀死你的 sheet 超过 11000 行
这里是Ben Collins' running total formula
的修改=ARRAYFORMULA(
IF(ISBLANK(A2:A),,
MMULT(TRANSPOSE((ROW(C2:C)<=TRANSPOSE(ROW(C2:C)))*C2:C),SIGN(C2:C))-
MMULT(TRANSPOSE((ROW(D2:D)<=TRANSPOSE(ROW(D2:D)))*D2:D),SIGN(D2:D))))
MMULT 的另一种选择:
=INDEX(QUERY(FLATTEN(QUERY(QUERY(TRANSPOSE(QUERY(QUERY(TRANSPOSE(
(SEQUENCE(COUNTA(A3:A)*2)<=SEQUENCE(1, COUNTA(A3:A)*2))*
FLATTEN(INDIRECT("C3:D"&COUNTA(A3:A)+ROW(A3)-1)*{1, -1})),
"offset 1", ), "skipping 2", )), "select "&QUERY(
"sum(Col"&SEQUENCE(COUNTA(A3:A))&"),",, 9^9)&"' '"),
"offset 1", )), "where Col1 is not null", ))
但同样,LTE (<=
) 1000 万个单元格的限制不会让您在您的案例中使用超过 1581 行或在标准累积和案例中使用 3162 行
(1581 rows * 2 columns) raised on 2nd power < 10 million cells
(1581*2)^2 = 9998244