什么是提高这个Excel公式性能的最快方法?

这是从以下post进一步。

如何克服Excel中公式的最大长度限制? 一个Excel的错误?

这是我正在努力解决的一个问题。 给定一个包含类别和值的数据集

category value1 value2 a 1.0 ... a 2.0 a 1.0 a 3.0 b 1.0 b 5.0 b 2.0 ... ... 

我想通过检查上面一行的值是否在其类别的一个西格玛偏差之内来validation这些值。 这意味着我们需要跳过每个类别的第一行。

这是我试过的:

下面的公式适用于每个类别的单元格,从第二行开始到每个类别的最后一行。

 =INDIRECT(ADDRESS(ROW(), COLUMN())) - INDIRECT(ADDRESS(ROW()-1, COLUMN())) < 1.0*STDDEV.P(INDIRECT(ADDRESS(MATCH(INDIRECT("A" & ROW()), $A:$A, 0), COLUMN()) & ":" &ADDRESS(MATCH(INDIRECT("A"&ROW()),$A:$A, 1), COLUMN()))) 

它工作得很快,但我们需要清除每个类别的第一行的数据validation。

这是@ user3964075提供的解决scheme

 {=IF($A2<>$A1,TRUE,B2-B1<STDEV.P(IF($A:$A=$A2,B:B)))} 

问题是性能。 200Kb数据集需要十多分钟。

什么是最快的公式来做到这一点?

除了原始问题中的STDDEV.P ( STDEV.P函数 …?)之外,在数据validation等基础过程中公然使用volatile¹函数必须要消除计算周期。 用全列引用代替数组公式²并没有帮助。

使用dynamic命名范围

以下方法将创build一些dynamic命名范围。 对于源自A1的连续数据块“island”将会有一个,对于使用它们的任何行将引用列A,以及在其使用的任何单元之上的单元以及STDEV.P函数的范围将获得其返回的结果。

虽然没有太多的意义使用这些超出了他们打算的工作表,他们将被赋予工作簿(而不是工作表)的范围。 我看不到工作表实际上被命名的任何地方,所以我将使用Sheet2 。 如有必要调整工作表名称。 请注意,您将在单元格区域引用中使用绝对和相对的列和行的不同组合。

  1. 在Sheet2上selectA2。 这是重要的!
  2. 转到公式►定义名称►名称pipe理器。 当“ 名称pipe理器”对话框打开时,单击“ 新build”以创build每个命名的范围。
  3. stdCAT – 将引用列A中的单元格对应于正在使用的任何行。
    • 名称:stdCAT
    • 范围:工作簿
    • 指: =Sheet2!$A2
  4. stdVALa – 将引用它所协助的公式所在的实际单元格。
    • 名称:stdVALa
    • 范围:工作簿
    • 指: =Sheet2!A2
  5. stdVALb – 将引用它所协助的公式所在单元格上方的单元格。
    • 名称:stdVALb
    • 范围:工作簿
    • 指: =Sheet2!A1
  6. stdDATA – 引用从A1向右延伸的数据岛,与第1行一样多的单元格和A1列中与A列单元格相同数量的单元格。
    • 名称:stdDATA
    • 范围:工作簿
    • 指的是: =Sheet2!$A$1:INDEX(Sheet2!$A:$Z, MATCH("zzz", Sheet2!$A:$A), MATCH("zzz", Sheet2!$1:$1))
  7. stdRNG – 将包含列A中关联的第一个实例的行的最后一行引用当前列中的数据范围的范围。
    • 名称:stdRNG
    • 范围:工作簿
    • 指的是: =INDEX(stdDATA, MATCH(Sheet2!$A2, Sheet2!$A:$A, 0), COLUMN()):INDEX(stdDATA, MATCH(Sheet2!$A2, Sheet2!$A:$A, 1), COLUMN())
  8. 当创build这些命名范围时,单击右下angular的closures返回到您的工作表。

selectSheet2!B3:B8 (从您的样品数据中)并selectData(数据)►Data Tools(数据validation)►Data Validation(数据validation)。 select允许:自定义并为来源提供以下内容

 =(stdVALa-stdVALb)<STDEV.P(stdRNG) 

单击确定以创build数据validation规则。 select其他范围并根据需要创build其他数据validation规则。

你所完成的是完全绕过了无引用操作符数据validation限制,并且你已经完成了这个操作,而不需要使用具有完整列引用的volatile¹函数或数组公式2。 在公式中使用时,每个命名范围及其随后的计算负载只有其绝对必须的大。 在MATCH函数中使用的完整列引用不会负面影响计算负载,因为lookup_value必须被发现。

您应该能够识别与所得到的公式和您的原始方法的密切相似之处,那就是devise。 我不打算提供一个新的公式,而是将工作模型重新devise成更高效的版本。


¹ 只要整个工作手册中的任何内容发生变化,挥发性函数就会重新计算,而不仅仅是当影响结果的内容发生变化时。 volatile函数的例子是INDIRECT , ADDRESS , TODAY和OFFSET 。

² 数组公式以参考单元格范围增长的方式对数计算循环。 当在数组公式中使用时,始终尝试将所有单元格区域引用保持为绝对最小值。

数组公式的性能是不好的,因为它包括列A和B中的所有行,直到最大行1048576。如果可以限制最大行,它应该快得多。 例:

 {=IF($A2<>$A1,TRUE,B2-B1<STDEV.P(IF($A$1:$A$1000=$A2,B$1:B$1000)))} 

为了进一步提高性能,可以使用帮助列来计算标准偏差。

例: 在这里输入图像说明

F2公式向下和向右:

 {=IF($A2<>$A1,STDEV.P(IF($A$1:$A$100000=$A2,B$1:B$100000)),F1)} 

这是一个数组公式。 将其放入没有大括号的单元格中,然后按[Ctrl] + [Shift] + [Enter]确认。

用这个公式,只需要在列A的类别改变时计算STDEVP(IF($A$1:$A$100000=$A2,B$1:B$100000)) 。 这应该less得多,就好像每行必须计算一样。

B2:Envalidation的公式为:

 =IF($A2<>$A1,TRUE,B2-B1<F2)