带有溢出动态数组的 Excel365 Sumif

带有溢出动态数组的 Excel365 Sumif

我在 Excel365 中跨行和列对动态数组数据求和时出现错误。

我有一个初始输入(不在表格中):

    A      B       C       D       E       F       G       H       I
1   data  value1  value2  value3  value4  value5  value6  value7  value8
2   a         83      39      84      56      81      16      83      66
3   b         74      74      42       0      98      14      86      18
4   c         53      50      71      79      72       1      57      54
5   d          4      67       7      57      62       7       6      73
6   b         99      67      47      65      73      92      73      59
7   c         30       0      84      30      49      89      70      48
8   d         95      42      73       1      80       9      52      63
9   b         49      45      78      68      89      54      28       5
10  b         36      68      99      37      72      49      90      66
11  d         15      80      34      47      89      96      87      83
12  a         73       4      17      59      71      86       4      93
13  b         78      21      15      94      92      33      17      50
14  a          9      35      57      47      30      12      62      89
15  a          7      66      77      51      69      75      92      49
16  a         19      89      43       0      67      81      62      90
17  a        100      63      17      83      52      26      48       4

这些特定值并没有什么特别之处。在我的生产电子表格中,它是给定接口上的 MAC 地址数量,本质上是任意数据,对于此示例,我使用 RANDARRAY 函数生成这些数据,然后将其复制粘贴为值。

然后我将其转换为一系列溢出数组。

B20: "=B1:I1"
A21: "=A2:A17"
B21: "=B2:I17"

结果如下(我使用“ASCII 艺术”来显示结果...我认为它们被称为“溢出动态数组”?)

    A       B       C       D       E       F       G       H       I
.         --------------------------------------------------------------
20 data  |value1  value2  value3  value4  value5  value6  value7  value8|
.         --------------------------------------------------------------
.   -     --------------------------------------------------------------
21| a |  |    83      39      84      56      81      16      83      66|
22| b |  |    74      74      42       0      98      14      86      18|
23| c |  |    53      50      71      79      72       1      57      54|
24| d |  |     4      67       7      57      62       7       6      73|
25| b |  |    99      67      47      65      73      92      73      59|
26| c |  |    30       0      84      30      49      89      70      48|
27| d |  |    95      42      73       1      80       9      52      63|
28| b |  |    49      45      78      68      89      54      28       5|
29| b |  |    36      68      99      37      72      49      90      66|
30| d |  |    15      80      34      47      89      96      87      83|
31| a |  |    73       4      17      59      71      86       4      93|
32| b |  |    78      21      15      94      92      33      17      50|
33| a |  |     9      35      57      47      30      12      62      89|
34| a |  |     7      66      77      51      69      75      92      49|
35| a |  |    19      89      43       0      67      81      62      90|
36| a |  |   100      63      17      83      52      26      48       4|
.   -     --------------------------------------------------------------

在我的生产电子表格中,我在多张工作表中进行了一些数据处理,但我重新创建了这个示例,即使使用这些简单的表达式,也遇到了同样的问题

所以我的问题-我应用了新功能:

B40: "=B20#"
A41: "=UNIQUE(A21#)"
B41: "=SUMIFS(B21:B36,A21#,A41#)"

哪个工作正常:

<code>
.   A       B       C       D       E       F       G       H       I
.         --------------------------------------------------------------
40 data  |value1  value2  value3  value4  value5  value6  value7  value8|
.         --------------------------------------------------------------
.   -     ------
41| a |  |   291|
42| b |  |   336|
43| c |  |    83|
44| d |  |   114|
.   -     ------

但是如果我将 B41 更改为"=SUMIFS(B21#,A21#,A41#)"或,"=SUMIFS(B21#,A21#,A41#,B20#,A40#)"我会在所有溢出数组单元格表上得到 #VALUE! 的结果(尽管溢出数组大小符合预期)

我最终寻找的结果是有效的 2-D sumifs 在单元格 B41 中产生单个公式,并使用溢出数组填充所有行/列 {a} - {d} 和值 1-8:

    A       B       C       D       E       F       G       H       I
.         --------------------------------------------------------------
40 data  |value1  value2  value3  value4  value5  value6  value7  value8|
.         --------------------------------------------------------------
.   -     --------------------------------------------------------------
41| a |  |   291     296     295     296     370     296     351     391|
42| b |  |   336     275     281     264     424     242     294     198|
43| c |  |    83      50     155     109     121      90     127     102|
44| d |  |   114     189     114     105     231     112     145     219|
.   -     --------------------------------------------------------------

我意识到我可以简单地执行少量 sumif 并使我的样本数据集变得更容易,但我的生产数据集每张表和多张表有超过 2600 万个数据点,处理起来需要几个小时。我发现动态溢出数组更省时,即使没有其他原因,也只是因为更少的 RAM,即更少的交换文件使用量,以及可能更好的 L2 缓存性能。如果有一个不需要 2 小时才能关闭的工作簿就好了。

答案1

解决方案:单个数组公式中的多个 SUMIF

这相当于 2D SUMIF 给出数组答案

=MMULT(--(TRANSPOSE(A21#)=A41#),B21#)

怎么运行的:(这个解释比我希望的要长得多!)

MMULT 执行矩阵乘法,是 Excel 中为数不多的可以在单个数组公式中更改数组长度(和宽度)的方法之一。你想要将大型 2D 表缩小为较小的 2D 表这一事实已经暗示我 MMULTI 可能是可行的方法。

理解矩阵乘法的具体工作原理超出了这篇文章的范围,但你可以阅读更多内容这里。了解尺寸如何变化很重要。如果C = AB,矩阵A为墨西哥(行 x 列),则 B 必须有n行匹配 An列。如果 B 是恩智浦,则 C 的输出维度为mxn.nxp=mxp

如果 A 是水平/行数组1 xnB 是垂直/列阵列nx 1,AB 的答案将是1 x 1在维度上,即单个标量值。这本质上与 Excel 中的点积或 SUMPRODUCT(a1 * b1 + a2 * b2 + ... + an * bn)相同,唯一的区别是 SUMPRODUCT 要求数组要么都是水平的,要么都是垂直的。如果您现在在 2D 中堆叠 B,即恩智浦,那么 MMULT(A, B) 实际上是在做SUMPRODUCTs 一个接一个地输出尺寸1 经验值

从 SUMIF 到 MMULT

任何 SUMIF 都可以表示为 SUMPRODUCT。该公式=SUMIF(A1:A10,"yes",B1:B10)相当于=SUMPRODUCT(--(A1:A10="yes"), B1:B10)--只是将布尔值 TRUE/FALSE 转换为数值 1/0。--(A1:A10="yes")变为 0 和 1 的掩码数组,选择将 B 中的哪些条目相加。同样,SUMIF/SUMPRODUCT 可以用 MMULT 完成,只要注意数组方向即可=MMULT(--(TRANSPOSE(A1:A10)="yes"), B1:B10)

不幸的是,SUMIF 和 SUMPRODUCT 都不能扩展到二维,=SUMIF(A1:A10,"yes",B1:F10)不是工作,无法提供 B11:F11 中的选择性总和数组。MMULT 来救援:

=MMULT(--(TRANSPOSE(A1:A10)="yes"), B1:F10)

就尺寸而言1 x 10 . 10 x 5 = 1 x 5. B1:F10 的每一列都通过相同的掩码数组进行矩阵乘法/“和积”。

最后,在令人费解的数学运算的最后一步,想象一下您想要总结上面的“是”结果和下面的“否”结果。您可以分成两行来做:

=MMULT(--(TRANSPOSE(A1:A10)="yes"), B1:F10)
=MMULT(--(TRANSPOSE(A1:A10)="no"), B1:F10)

但为什么不呢

=MMULT(--(TRANSPOSE(A1:A10)={"yes"; "no"}), B1:F10)

或者与你的具体情况相比

=MMULT(--(TRANSPOSE(A1:A10)=UNIQUE(A1:A10)), B1:F10)

就尺寸而言2 x 10.10 x 5 = 2 x 5

要了解 Excel 如何处理不同大小的矩阵比较,单独测试可能会有所帮助=TRANSPOSE(A1:A10)={"yes"; "no"}。去掉“;no”。测试。替换为“,no”并测试。注意“;”是行分隔符,“,”是列分隔符(至少对于我的本地化设置而言)。

从 SUMIFS 到 MMULT

实现具有多个条件的数组输出 SUMIFS

=MMULT(TRANSPOSE(BITAND((A1:A10={"yes","no"}),(G1:G10="active"))),B1:F10)

关于 SUMIF 与 SUMPRODUCT/MMULT 效率的最后说明

如图所示,这些函数可以为 1D 情况提供等效答案。值得注意的是,SUMIF 可能更高效(使用更少的内存并且没有乘法指令)。如果条件为 FALSE,SUMIF 不执行任何操作,而 SUMPRODUCT 仍会与零相乘并将结果添加到总数中。现在比较多个 SUMIF(每个都在自己的单元格中)与 MMULT 并不那么容易。虽然 MMULTI 需要墨西哥内存来存储掩码和陣容额外的乘法,每个 SUMIF 将重复与条件比较mxnx (p-1)更多单元格读取和(慢速文本?)比较。速度比较会很好,但我更看好 MMULT。

答案2

抽象的

我没有办法改进@Mobus 的回答,我甚至不会尝试。

但是,我认为这个额外的努力会引起其他偶然发现这个帖子的人的兴趣。不确定是否值得了解我的实际生产数据,但不管怎样,页面空间很便宜。 如果你只想看我的最终结果,请向下滚动,包括截图。

介绍

我正在尝试使用运行 IOS XE 17.6 的 Catalyst 3850 和 9300 交换机为我的组织开发 IPv6 第一跳安全功能。IOS XE 使用所谓的“设备跟踪”,但关键是交换机必须跟踪少于 1000 个 IP,否则交换机会变得不稳定(没有记录任何地方除了 Cisco TAC 案例 634280103)。因此,如果我假设 IPv6 链路范围 (FE80::)、IPv6 全局范围 (2000::) 和 IPv4 地址都使用相同 MAC 地址的设备跟踪条目,那么使用设备跟踪功能我最多只能跟踪 333 个 MAC 地址。

在我的组织中,这会限制我什么?

假设:要么

  • “面向客户端的接口,但没有网络模块上的接口”,或
  • “客户端 VLAN 但不是管理 VLAN。”

方法

绘制图表。统计上有意义的时间段内所有交换机上的所有接口和 VLAN(所有安全功能至少都是基于接口的,有些具有基于 VLAN 的替代方法)。这意味着我需要一个 3D 图表,设备接口 x 时间戳,z 轴上有 #MAC。但我需要数据,也需要 Excel 的绘图功能。(是的,我知道 Matlab 会更好,但我已经自学了足够多的东西才能走到这一步)。由于我想看看各种选项在哪里中断,我需要看到高端极值,这样我还可以绘制最大阈值平面并查看超过我的最大阈值的数据点。

我编写了一些 Python 脚本来集成一些不同的管理服务器,以帮助完成此任务。这生成了两个 CSV 文件:一个记录每个交换机每个 VLAN 的 MAC 地址数量,另一个记录每个交换机每个接口的 MAC 地址 - 两者均以 15 分钟为间隔,持续一个月。任何为零的条目都会被转储,但这仍然导致接口 CSV 文件中有 2600 万行(VLAN 较小 - 只有 800 万行)。

回到 Python,我将其转换为两个带有标题行(VLAN 实际上相同)的新 CSV 文件:device_interface、device、model、interface、time1、time2、time3、time4、time5.....

这样就产生了大约 31600 条接口线和大约 13000 条 VLAN 线,均带有大约 2600 个时间戳。太棒了!我现在可以在 Excel 中打开它们了。

两个有趣的点:(1)我记录了收集整个电池数据的开始时间的时间戳,以及收集单个开关数据的时间戳;(2)一些数据收集电池不完整,但最终收集了足够的数据,从统计上使第二个问题消失(“用零替换,丢失的数据无关紧要”)。这使得 switch_interfaceXtime(n) 交集看起来像:x&excel-formatted-timestamp&MACs(x =“C”表示干净,“D”表示脏)。示例:C&44612.59375&27

好的,太棒了,包括数据中的所有空白,我现在已经在两个工作表中加载了大约 1 亿个数据点。这就是我遇到问题的地方,试图处理这么多数据。

我认为我原来的解决方案 (抱歉,我不再有它了) 会起作用,但是在它消耗了 60GB 的虚拟内存后我停止了该计算,并且我计算了完成时间并发现它将花费 3 年时间。我也感到沮丧,因为我所做的任何事情都需要很长时间才能应用 (通常需要几分钟)。回到绘图板,这产生了@Mobus 出色回答的问题。事实证明我原来的解决方案存在很多问题,请参阅我在@Mobus 回答下的评论,但很大一部分我使用 C/D 来应用条件格式并且我使用了函数OFFSET,它们都是易失性的,我认为这使我的解决方案达到 O(n^2) 或 1 亿 X 1 亿次计算,甚至可能更大 (O(n^3)?O(n^4)?)。

不幸的是,@Mobus 的解决方案在我正在处理的规模下不起作用。它在 13,000 行 VLAN 工作表上工作得很好,但 Excel 甚至懒得尝试 31,000 行接口工作表(一些对话框显示资源不足,无法完成操作)。

我尝试重新执行一些 mmult 函数和 sumproducts 等,看看是否可以得到溢出的结果,然后将所有内容塞入输出范围的左上角单元格中,但遇到了一些问题:

  • 我的第一次尝试基本上是对你建议的重新讨论(直到后来我才意识到这一点),并且由于资源耗尽,它立即拒绝计算。
  • 我的第二次尝试是看看我是否可以使用点积或叉积。我想看看我是否可以成功处理单个单元格,然后扩展到溢出数组。我思考我可能已经完成了,但尝试它消耗了太多内存,导致系统冻结。有一次我看到它在我的 16GB 机器上占用了 60GB,所以它至少有那么高。

在研究 sumproduct 公式时,我了解了 excel 的数组乘法机制(@Mobus mmult 解决方案中使用了该机制,但直到我做了这项工作,我才明白其基本原理)。这终于奏效了,尽管我的解决方案只计算了一个单元格,但我需要将其复制/粘贴到所有数百万个单元格中。复制/粘贴很容易,至少我只需要做一次(对吧……???)。

讨论

每次复制/粘贴到整个工作表都要花 5 分钟或 20 分钟,即使只复制一行。我没有试图找出差异背后的“原因”,尽管我认为这与页面交换有关。

计算(复制后)花了大半天的时间,根据各种观察,我估计每张工作表需要 9-10 个小时,我的 4x CPU 内核在两个超线程管道上的负载均为 90%,以 90% 的最大频率运行,几乎全部在 Linux 所谓的“用户空间”中运行。刚打开时,最终产品为 18.4GB。

我正在进行绘图方面的后续活动;我认为它不一定与引发这个话题的问题相关。

现在打开包含所有 9x 工作表的文件需要 45 分钟,保存和关闭每个文件现在需要大约 10-15 分钟;这比以前花费的几个小时要少得多。使用我的第一种方法:打开、保存或关闭至少需要 2 小时,只有 4x 工作表;我甚至从来没有尝试使用我的第一种方法来完成所有 9x 工作表。

公式可能可以改进以提高可读性,但我不想花上几个小时来验证。也许我可以使用sequence()来改进它,以创建正确大小的网格,然后……之后就不确定了。

无论如何,这些公式的优点在于它们 100% 非易失性。我在一些地方使用了 unique、sort 和 filter,但我认为它们也是非易失性的,因此进一步的研发不会受到影响。

结果

内存使用情况

上图:截图显示了刚打开的文件的内存使用情况。

接口和 VLAN 原始数据

“...(原始)”标签:没有公式;这是严格的原始数据。

已提取 MAC 的接口和 VLAN

“...(精简)”选项卡:VLAN 工作表上的公式“非常相似”。

A1: ='Interface Data (Raw)'!A1
A2: =LET(x,'Interface Data (Raw)'!A:A,FILTER(x,(x>0)*(x<>A1)))
B1: ='Interface Data (Raw)'!B1
B2: =LET(x,'Interface Data (Raw)'!B:B,FILTER(x,(x>0)*(x<>B1)))
C1: ='Interface Data (Raw)'!D1
C2: =LET(x,'Interface Data (Raw)'!D:D,FILTER(x,(x>0)*(x<>C1)))
D1: =LET(x,'Interface Data (Raw)'!1:1,FILTER(x,LEFT(x,1)="4"))
D2: =LET(x,'Interface Data (Raw)'!E2:E31634,IF(x="",0,RIGHT(x,LEN(x)-FIND("&",x,5))))

D2 导致数组溢出,1 列的行数与 A、B 和 C 列的行数相等。D2 一直复制/粘贴到单元格 CTO2

按设备汇总的接口和 VLAN

A1: same as above
A2: same as above
A2: =UNIQUE('Interface Data (Streamlined)'!B2#)
B2: =SUM((--('Interface Data (Streamlined)'!$B$2#=$A2))*'Interface Data (Streamlined)'!D$2#)

B2 生成单个单元格,必须将其一直复制/粘贴到单元格 CTM1783

总结了无上行链路和无模块的接口

您通过无聊的细胞了解了这个想法。

B2 (upper): =SUM((--('Interface Data (Streamlined)'!$B$2#=$A2))*'Interface Data (Streamlined)'!D$2#*(NOT(RIGHT('Interface Data (Streamlined)'!C$2#,4)="/1/1")))
B2 (lower): =SUM((--('Interface Data (Streamlined)'!$B$2#=$A2))*'Interface Data (Streamlined)'!D$2#*(NOT(LEFT(RIGHT('Interface Data (Streamlined)'!$C$2#,4),3)<>"/1/")))

B2 也导致出现单个单元格,必须将其一直复制/粘贴到单元格 CTM1783

无管理的 VLAN 汇总

B3、B4 和 C3 并没有什么有趣的东西,上面没有显示出来。

A4: =--ISERROR(MATCH('VLAN Data (Streamlined)'!C2#,E2#,))
G1: =SEQUENCE(,1000,2000)
E2: =E1:ALR1
C4: =SUM(--('VLAN Data (Streamlined)'!$B$2#=$B4)*$A$4#*'VLAN Data (Streamlined)'!D$2#)

E2 是必需的,因为我不确定如何才能获得数组 {1,8,2000,2001...2999},而且我忘了我可以直接开始输入“2000”和“2001”,然后拖动剩下的部分。哦,算了。

C4 也产生了一个单元格,必须将其一直复制/粘贴到单元格 CTN1785。有趣的是,预先计算到 A4 并没有明显加快整体计算速度。

相关内容