在数组上使用 OR 逻辑作为 Sumproduct 中的参数
Using OR logic on an array as argument in Sumproduct
我有一个相当大的数据集,我需要将多个条目组合成一个值。我的数据集包含两个数据集的组合数据,每个数据集都使用自己的 ID 和密钥。
我想像这样使用 Sumproduct()
函数:
=SUMPRODUCT(--('Raw data'!C:C=Landgebruik!A2);--('Raw data'!O:O={20;21;22;23;40});'Raw data'!S:S)
Landgebruik!A2
持有第一个数据集的 ID,我需要将第二个数据集聚合到该 ID。
'Raw data'!O:O
包含来自第二个数据集的 ID。在上述情况下,当第二个 ID 的值为以下任何值时,我需要对面积(在 'Raw data'!S:S
中)求和:{20;21;22;23;40}
。 (或逻辑)该列仅包含整数值。
是否有任何其他方法可以解决此问题,然后为数组中的所有值复制 --('Raw data'!O:O=20)
?
编辑:
我暂时采用了解决方法,即:=SUMPRODUCT(--('Raw data'!C:C=Landgebruik!A2);--('Raw data'!O:O=20)+('Raw data'!O:O=20)+('Raw data'!O:O=21)+('Raw data'!O:O=22)+('Raw data'!O:O=23)+('Raw data'!O:O=40);'Raw data'!S:S)
。但是我觉得应该有更优雅的方式来做到这一点。
这可行:
={SUMPRODUCT(--('Raw data'!C:C=Landgebruik!A2);--IFERROR(MATCH('Raw data'!O:O;{20;21;22;23;40};0)>0;0);'Raw data'!S:S)}
这需要作为数组公式输入。
您可以按照评论中的说明将其拆分为两个 SUMIFS。如果所有值都是整数,则将 'Raw data'!O:O 与 20、21、22 和 23 进行比较与测试它是否 >=20 和 <=23 相同。值40要单独做。
=SUMIFS('Raw Data'!S:S,'Raw Data'!C:C,Landgebruik!A2,'Raw Data'!O:O,">="&20,'Raw Data'!O:O,"<="&23)
+SUMIFS('Raw Data'!S:S,'Raw Data'!C:C,Landgebruik!A2,'Raw Data'!O:O,40)
在我的语言环境中
或
=SUMIFS('Raw Data'!S:S;'Raw Data'!C:C;Landgebruik!A2;'Raw Data'!O:O;">="&20;'Raw Data'!O:O;"<="&23)
+SUMIFS('Raw Data'!S:S;'Raw Data'!C:C;Landgebruik!A2;'Raw Data'!O:O;40)
在您的语言环境中。
这仅适用于多个条件是连续整数的情况。
速度方面的考虑
SUMIFS 被认为比 sumproduct 快五倍左右,因此可能是大型数据集的首选 as demonstrated here
您可能会争辩说,来自@BrakNicku 的 SUM 中(有效地)五个 SUMIFS 的更一般性建议应该与一个 SUMPRODUCT 一样快,但 SUM(SUMIFS) 可能仍会获胜,因为像 SUMIFS 这样的公式处理完整-列引用比数组公式更有效。
虽然之前已经做过数百次了,嘿也许微软改变了公式之类的。
我偏向于 Jerry 和 Me 建议的方法,因为它们非常简单和简洁,但是你付出了沉重的性能代价。
Tom 的公式在我看来很丑陋,但到目前为止最快,比我最初的示例快大约 4 倍。我们能够将 {} 与 Tom 的公式相结合,但要让它发挥作用,我们必须用 sum 函数包装 sumifs 函数。这大大减慢了公式的速度,但使它更漂亮。
z32a7ul 也有一个很好的解决方案。我真的很喜欢使用 -- 并且学习了如何使用 |s 来搜索文本并且只搜索该文本。乍一看,我认为它不适用于 2323 这样的数字,但它确实有效。
模拟示例如下:
A1:A5000 充满了 LandgeBruik,
B1:B5000 填满了 40 的
C1:5000 填满了 1。
结果:
=SUMPRODUCT((A1:A5000="LandgeBruik")*(B1:B5000={20,21,22,23,40})*C1:C5000)
19.186031 秒过去了 |
59,818,073 个刻度
{=SUM(IF(A1:A5000="Landgebruik",1,0)*IF(B1:B5000={20,21,22,23,40},1,0)*C1:C5000)}
26.124411 秒过去了 |
81,450,506 个刻度
{=SUM((A1:A5000=""Landgebruik"")*(B1:B5000={20,21,22,23,40})*C1:C5000)}
21.111835 秒过去了 |
65,822,330 个刻度
"=SUMIFS(C1:C5000,B1:B5000,"">=20"",B1:B5000,""<=23"",A1:A5000,""=Landgebruik"")+SUMIFS(C1:C5000,B1:B5000,""=40"",A1:A5000,""=Landgebruik"")"
6.732804 秒过去了 |
20,991,490 个刻度
"=SUM(SUMIFS(C1:C5000,A1:A5000,"Landgebruik",B1:B5000,{21,22,23,24,40}))"
16.954528 秒过去了 |
52,860,709 个刻度
"=SUMPRODUCT(--(A1:A5000=""Landgebruik""),--NOT(ISERROR(FIND(""|""&B1:B5000&""|"",""|20|21|22|23|40|""))),C1:C5000)"
11.822379 秒过去了 |
36,859,729 个刻度
您可以对当前的公式稍作改动;将 ;
更改为 *
(在该特定情况下也不需要 --
):
=SUMPRODUCT(('Raw data'!C:C=Landgebruik!A2)*('Raw data'!O:O={20;21;22;23;40})*'Raw data'!S:S)
这应该有效。
当您向 SUMPRODUCT
提供单独的参数时,每个参数的大小必须相同。但是当你像这样将它们相乘时,它会强制计算并且数组会扩展。
例如,如果您采用两个数组,5x1 和 1x5,您将得到一个 5x5 的结果数组:
在询问 OP 澄清之后,我想回答这个问题,因为英语不是我的主要语言,我想我误解了一些东西。
所以,我做了什么来模拟情况,用 2 sheets 制作了一个新工作簿。
一个 sheet 被命名为 Landgebruik
并在 A2
中得到一个值,我这样做了:
第二个 sheet 名为 Raw data
。我隐藏了一些列以仅使用 C、O 和 S 列。在 S 列中,我只输入等于 1 的值。在 O 列中,我随机输入等于 {20,21,22,23,40}
的值,在 C 列中,我随机输入值 A 或 B。它看起来像这样(请注意我隐藏了一些列):
问题想对 S 列中的值求和,但前提是 O 列等于 20 或 21 或 22 或 23 o 40 且 C 列等于 Landgebruik!A2
(在我的测试中,值在有字母 A
)
我们可以使用数组公式对S列的数据进行过滤,过滤后对符合要求的值求和。在我的测试中,正确的结果应该是 8,因为 S 列中只有 8 个值符合 C 和 O 列的要求。在图像中,右边的行以黄色突出显示。
OP 已经这样做了,但想知道是否有 shorter/elegant 公式。
我找到的最短公式是这样的:
=SUM(IF($O:$O={20;21;22;23;40};IF($C:$C=Landgebruik!$A;$S:$S)))
This is an array formula, so it must be inserted pressing
CTRL+SHIFT+ENTER or it won't work!
工作原理:
首先 IF
获取 S 列中的所有值,并忽略 O 列中的所有等效值不是 20 或 21 或 22 或 23 或 40 的所有值。第二个 IF
获取该新数组,并忽略C 列中等效的所有值不等于 Landgebruik!$A
。最终数组由函数 SUM
总结
我已尽力解释清楚。希望您能根据自己的需要进行调整。
您可以为此使用文本搜索:
--NOT(ISERROR(FIND('Raw data'!O:O,"2021222340")))
但是你要注意不要在较长的ID中错误地找到较短的ID,例如如果你想在 ID 中搜索 { 123, 456, 789 } 那么 12 不被认为是在 ID 中。所以像上面这样的简单文本搜索是行不通的。您需要一个分隔符来分隔 ID 字符串。通常我为此目的使用竖线字符,因为我不记得它出现在 Excel 文件的原始文本中的任何情况,并且因为它使公式 human-readable:
--NOT(ISERROR(FIND("|"&'Raw data'!O:O&"|","|20|21|22|23|40|")))
示例:
'Raw data'!O:O 是 20 => |21|位于 |20|21|22|23|40|
'Raw data'!O:O 是 2 => |2|未在 |20|21|22|23|40|
中找到
(如果您的 ID 可能包含竖线字符,那么您可以使用 CHR(1),这是一个早已被遗忘的 SOH 的 ASCII 代码,表示 header 的开始;当然,它的可读性较差。)
整个公式:
=SUMPRODUCT(--('Raw data'!C:C=Landgebruik!A2),--NOT(ISERROR(FIND("|"&'Raw data'!O:O&"|","|20|21|22|23|40|"))),'Raw data'!S:S)
(抱歉,我的 Excel 使用 , 而不是 ;)
如果对性能(计算速度)感兴趣,不怕矩阵计算,可以用MMULT:
=SUMPRODUCT(--('Raw data'!C:C=Landgebruik!A2),MMULT(--('Raw data'!O:O={20,21,22,23,24}),TRANSPOSE({1,1,1,1,1})),'Raw data'!S:S)
解释:
首先创建一个1048576×5的矩阵,如果'Raw data'!O:O的第i行中的ID是第i行第j列的值为1与枚举 {20,21,22,23,24} 中的第 j 个值相同,否则为 0。
其次,将其乘以 1 的向量(5 个 1,因为 {20,21,22,23,24} 包含五个元素),这意味着您接受所有五个值。
第三,从上面你得到一个向量,如果 ID 在可接受的值中,第 i 个元素是 1,否则是 0,你把这个向量放在你的 SUMPRODUCT 中的其他向量旁边。
(抱歉,我的 Excel 使用了 ',' 而不是 ';'。如果你想缩短公式,你可以写 {1;1;1;1;1} 而不是 TRANSPOSE( {1,1,1,1,1})。但你必须找出你的 Excel 使用什么而不是 ';' 来分隔行,很可能是 '.'。)
注意:如果您引用实际包含值的 运行ge 而不是整列,则可能会提高计算速度,例如'Raw data'!C1:C123 而不是 'Raw data'!C:C.
如果您在已包含的最后一行上方使用 Shift+Space Ctrl++ 插入新行,那么公式中的引用将自动更新。或者,您可以使用带有特殊公式的名称,通过确定最后一个非空单元格来增加引用的范围。
更新
我做了一些测量来比较这些方法的效率。我使用了 10000 行的 运行dom 数据,每个公式都重新计算了 1000 次。您可以在第二列中看到经过的时间。
我注释掉了其他公式,而我 运行 这个 VBA 代码来测量时间:
Public Sub MeasureCalculationTime()
Dim datStart As Date: datStart = Now
Dim i As Long: For i = 1 To 1000
Application.Calculate
Next i
Dim datFinish As Date: datFinish = Now
Dim dblSeconds As Double: dblSeconds = (datFinish - datStart) * 24 * 60 * 60
Debug.Print "Calculation finished at " & datFinish; " took " & dblSeconds & " seconds"
End Sub
在这种情况下,MMULT 不是最快的。
但是,我想指出它是最灵活的,因为
您可以将它与开关一起使用:您引用单元格 运行ge 而不是 {1,1,1,1,1},您将能够包括/ 非常快速地排除选择中的 ID。就像您输入 A1:A5 {20,21,22,23,24} 并在其旁边输入 B1:B5 {1,1,1,1,1} 一样。如果要排除21,则将B2重写为0,如果要包含,则将其写回1。
您可能会使用更复杂的条件,其中您必须比较多个级别。喜欢:
=SUMPRODUCT(MMULT(--(CarId=CarOwner),--(CarOwner=ListOfJobs),--(ListOfJobs=JobsByDepartment),--(DepartmentIncludedInSelection=1)),FuelConsumption)
注意:上面一行只是伪代码,MMULT只有两个参数。
我有一个相当大的数据集,我需要将多个条目组合成一个值。我的数据集包含两个数据集的组合数据,每个数据集都使用自己的 ID 和密钥。
我想像这样使用 Sumproduct()
函数:
=SUMPRODUCT(--('Raw data'!C:C=Landgebruik!A2);--('Raw data'!O:O={20;21;22;23;40});'Raw data'!S:S)
Landgebruik!A2
持有第一个数据集的 ID,我需要将第二个数据集聚合到该 ID。
'Raw data'!O:O
包含来自第二个数据集的 ID。在上述情况下,当第二个 ID 的值为以下任何值时,我需要对面积(在 'Raw data'!S:S
中)求和:{20;21;22;23;40}
。 (或逻辑)该列仅包含整数值。
是否有任何其他方法可以解决此问题,然后为数组中的所有值复制 --('Raw data'!O:O=20)
?
编辑:
我暂时采用了解决方法,即:=SUMPRODUCT(--('Raw data'!C:C=Landgebruik!A2);--('Raw data'!O:O=20)+('Raw data'!O:O=20)+('Raw data'!O:O=21)+('Raw data'!O:O=22)+('Raw data'!O:O=23)+('Raw data'!O:O=40);'Raw data'!S:S)
。但是我觉得应该有更优雅的方式来做到这一点。
这可行:
={SUMPRODUCT(--('Raw data'!C:C=Landgebruik!A2);--IFERROR(MATCH('Raw data'!O:O;{20;21;22;23;40};0)>0;0);'Raw data'!S:S)}
这需要作为数组公式输入。
您可以按照评论中的说明将其拆分为两个 SUMIFS。如果所有值都是整数,则将 'Raw data'!O:O 与 20、21、22 和 23 进行比较与测试它是否 >=20 和 <=23 相同。值40要单独做。
=SUMIFS('Raw Data'!S:S,'Raw Data'!C:C,Landgebruik!A2,'Raw Data'!O:O,">="&20,'Raw Data'!O:O,"<="&23)
+SUMIFS('Raw Data'!S:S,'Raw Data'!C:C,Landgebruik!A2,'Raw Data'!O:O,40)
在我的语言环境中
或
=SUMIFS('Raw Data'!S:S;'Raw Data'!C:C;Landgebruik!A2;'Raw Data'!O:O;">="&20;'Raw Data'!O:O;"<="&23)
+SUMIFS('Raw Data'!S:S;'Raw Data'!C:C;Landgebruik!A2;'Raw Data'!O:O;40)
在您的语言环境中。
这仅适用于多个条件是连续整数的情况。
速度方面的考虑
SUMIFS 被认为比 sumproduct 快五倍左右,因此可能是大型数据集的首选 as demonstrated here
您可能会争辩说,来自@BrakNicku 的 SUM 中(有效地)五个 SUMIFS 的更一般性建议应该与一个 SUMPRODUCT 一样快,但 SUM(SUMIFS) 可能仍会获胜,因为像 SUMIFS 这样的公式处理完整-列引用比数组公式更有效。
虽然之前已经做过数百次了,嘿也许微软改变了公式之类的。
我偏向于 Jerry 和 Me 建议的方法,因为它们非常简单和简洁,但是你付出了沉重的性能代价。
Tom 的公式在我看来很丑陋,但到目前为止最快,比我最初的示例快大约 4 倍。我们能够将 {} 与 Tom 的公式相结合,但要让它发挥作用,我们必须用 sum 函数包装 sumifs 函数。这大大减慢了公式的速度,但使它更漂亮。
z32a7ul 也有一个很好的解决方案。我真的很喜欢使用 -- 并且学习了如何使用 |s 来搜索文本并且只搜索该文本。乍一看,我认为它不适用于 2323 这样的数字,但它确实有效。
模拟示例如下:
A1:A5000 充满了 LandgeBruik,
B1:B5000 填满了 40 的
C1:5000 填满了 1。
结果:
=SUMPRODUCT((A1:A5000="LandgeBruik")*(B1:B5000={20,21,22,23,40})*C1:C5000)
19.186031 秒过去了 | 59,818,073 个刻度
{=SUM(IF(A1:A5000="Landgebruik",1,0)*IF(B1:B5000={20,21,22,23,40},1,0)*C1:C5000)}
26.124411 秒过去了 | 81,450,506 个刻度
{=SUM((A1:A5000=""Landgebruik"")*(B1:B5000={20,21,22,23,40})*C1:C5000)}
21.111835 秒过去了 | 65,822,330 个刻度
"=SUMIFS(C1:C5000,B1:B5000,"">=20"",B1:B5000,""<=23"",A1:A5000,""=Landgebruik"")+SUMIFS(C1:C5000,B1:B5000,""=40"",A1:A5000,""=Landgebruik"")"
6.732804 秒过去了 | 20,991,490 个刻度
"=SUM(SUMIFS(C1:C5000,A1:A5000,"Landgebruik",B1:B5000,{21,22,23,24,40}))"
16.954528 秒过去了 | 52,860,709 个刻度
"=SUMPRODUCT(--(A1:A5000=""Landgebruik""),--NOT(ISERROR(FIND(""|""&B1:B5000&""|"",""|20|21|22|23|40|""))),C1:C5000)"
11.822379 秒过去了 | 36,859,729 个刻度
您可以对当前的公式稍作改动;将 ;
更改为 *
(在该特定情况下也不需要 --
):
=SUMPRODUCT(('Raw data'!C:C=Landgebruik!A2)*('Raw data'!O:O={20;21;22;23;40})*'Raw data'!S:S)
这应该有效。
当您向 SUMPRODUCT
提供单独的参数时,每个参数的大小必须相同。但是当你像这样将它们相乘时,它会强制计算并且数组会扩展。
例如,如果您采用两个数组,5x1 和 1x5,您将得到一个 5x5 的结果数组:
在询问 OP 澄清之后,我想回答这个问题,因为英语不是我的主要语言,我想我误解了一些东西。
所以,我做了什么来模拟情况,用 2 sheets 制作了一个新工作簿。
一个 sheet 被命名为 Landgebruik
并在 A2
中得到一个值,我这样做了:
第二个 sheet 名为 Raw data
。我隐藏了一些列以仅使用 C、O 和 S 列。在 S 列中,我只输入等于 1 的值。在 O 列中,我随机输入等于 {20,21,22,23,40}
的值,在 C 列中,我随机输入值 A 或 B。它看起来像这样(请注意我隐藏了一些列):
问题想对 S 列中的值求和,但前提是 O 列等于 20 或 21 或 22 或 23 o 40 且 C 列等于 Landgebruik!A2
(在我的测试中,值在有字母 A
)
我们可以使用数组公式对S列的数据进行过滤,过滤后对符合要求的值求和。在我的测试中,正确的结果应该是 8,因为 S 列中只有 8 个值符合 C 和 O 列的要求。在图像中,右边的行以黄色突出显示。
OP 已经这样做了,但想知道是否有 shorter/elegant 公式。
我找到的最短公式是这样的:
=SUM(IF($O:$O={20;21;22;23;40};IF($C:$C=Landgebruik!$A;$S:$S)))
This is an array formula, so it must be inserted pressing CTRL+SHIFT+ENTER or it won't work!
工作原理:
首先 IF
获取 S 列中的所有值,并忽略 O 列中的所有等效值不是 20 或 21 或 22 或 23 或 40 的所有值。第二个 IF
获取该新数组,并忽略C 列中等效的所有值不等于 Landgebruik!$A
。最终数组由函数 SUM
我已尽力解释清楚。希望您能根据自己的需要进行调整。
您可以为此使用文本搜索:
--NOT(ISERROR(FIND('Raw data'!O:O,"2021222340")))
但是你要注意不要在较长的ID中错误地找到较短的ID,例如如果你想在 ID 中搜索 { 123, 456, 789 } 那么 12 不被认为是在 ID 中。所以像上面这样的简单文本搜索是行不通的。您需要一个分隔符来分隔 ID 字符串。通常我为此目的使用竖线字符,因为我不记得它出现在 Excel 文件的原始文本中的任何情况,并且因为它使公式 human-readable:
--NOT(ISERROR(FIND("|"&'Raw data'!O:O&"|","|20|21|22|23|40|")))
示例:
'Raw data'!O:O 是 20 => |21|位于 |20|21|22|23|40|
'Raw data'!O:O 是 2 => |2|未在 |20|21|22|23|40|
中找到(如果您的 ID 可能包含竖线字符,那么您可以使用 CHR(1),这是一个早已被遗忘的 SOH 的 ASCII 代码,表示 header 的开始;当然,它的可读性较差。)
整个公式:
=SUMPRODUCT(--('Raw data'!C:C=Landgebruik!A2),--NOT(ISERROR(FIND("|"&'Raw data'!O:O&"|","|20|21|22|23|40|"))),'Raw data'!S:S)
(抱歉,我的 Excel 使用 , 而不是 ;)
如果对性能(计算速度)感兴趣,不怕矩阵计算,可以用MMULT:
=SUMPRODUCT(--('Raw data'!C:C=Landgebruik!A2),MMULT(--('Raw data'!O:O={20,21,22,23,24}),TRANSPOSE({1,1,1,1,1})),'Raw data'!S:S)
解释:
首先创建一个1048576×5的矩阵,如果'Raw data'!O:O的第i行中的ID是第i行第j列的值为1与枚举 {20,21,22,23,24} 中的第 j 个值相同,否则为 0。
其次,将其乘以 1 的向量(5 个 1,因为 {20,21,22,23,24} 包含五个元素),这意味着您接受所有五个值。
第三,从上面你得到一个向量,如果 ID 在可接受的值中,第 i 个元素是 1,否则是 0,你把这个向量放在你的 SUMPRODUCT 中的其他向量旁边。
(抱歉,我的 Excel 使用了 ',' 而不是 ';'。如果你想缩短公式,你可以写 {1;1;1;1;1} 而不是 TRANSPOSE( {1,1,1,1,1})。但你必须找出你的 Excel 使用什么而不是 ';' 来分隔行,很可能是 '.'。)
注意:如果您引用实际包含值的 运行ge 而不是整列,则可能会提高计算速度,例如'Raw data'!C1:C123 而不是 'Raw data'!C:C.
如果您在已包含的最后一行上方使用 Shift+Space Ctrl++ 插入新行,那么公式中的引用将自动更新。或者,您可以使用带有特殊公式的名称,通过确定最后一个非空单元格来增加引用的范围。
更新
我做了一些测量来比较这些方法的效率。我使用了 10000 行的 运行dom 数据,每个公式都重新计算了 1000 次。您可以在第二列中看到经过的时间。
我注释掉了其他公式,而我 运行 这个 VBA 代码来测量时间:
Public Sub MeasureCalculationTime()
Dim datStart As Date: datStart = Now
Dim i As Long: For i = 1 To 1000
Application.Calculate
Next i
Dim datFinish As Date: datFinish = Now
Dim dblSeconds As Double: dblSeconds = (datFinish - datStart) * 24 * 60 * 60
Debug.Print "Calculation finished at " & datFinish; " took " & dblSeconds & " seconds"
End Sub
在这种情况下,MMULT 不是最快的。
但是,我想指出它是最灵活的,因为
您可以将它与开关一起使用:您引用单元格 运行ge 而不是 {1,1,1,1,1},您将能够包括/ 非常快速地排除选择中的 ID。就像您输入 A1:A5 {20,21,22,23,24} 并在其旁边输入 B1:B5 {1,1,1,1,1} 一样。如果要排除21,则将B2重写为0,如果要包含,则将其写回1。
您可能会使用更复杂的条件,其中您必须比较多个级别。喜欢:
=SUMPRODUCT(MMULT(--(CarId=CarOwner),--(CarOwner=ListOfJobs),--(ListOfJobs=JobsByDepartment),--(DepartmentIncludedInSelection=1)),FuelConsumption)
注意:上面一行只是伪代码,MMULT只有两个参数。