Google表格中无限真实动态范围平均值的ArrayFormula

Posted

技术标签:

【中文标题】Google表格中无限真实动态范围平均值的ArrayFormula【英文标题】:ArrayFormula of Average on Infinite Truly Dynamic Range in Google Sheets 【发布时间】:2021-04-02 17:28:24 【问题描述】:

例如:

     A       B      C     D     E     F     G     ∞
  |======|=======|=====|=====|=====|=====|=====|=====
1 |      |AVERAGE|     |     |     |     |     |        
  |======|=======|=====|=====|=====|=====|=====|=====
2 | xx 1 |       |   1 |   2 | 0.5 |  10 |     |        
  |======|=======|=====|=====|=====|=====|=====|=====
3 | xx 2 |       |   7 |   1 |     |     |     |       
  |======|=======|=====|=====|=====|=====|=====|=====
4 |      |       |   0 |     |     |     |     |       
  |======|=======|=====|=====|=====|=====|=====|=====
5 | xx 3 |       |   9 |   8 |   7 |   6 |     |       
  |======|=======|=====|=====|=====|=====|=====|=====
6 | xx 4 |       |   0 |   1 |   2 |   1 |     |       
  |======|=======|=====|=====|=====|=====|=====|=====
7 |      |       |   1 |     |   4 |     |     |       
  |======|=======|=====|=====|=====|=====|=====|=====
8 | xx 5 |       |     |     |     |     |     |       
  |======|=======|=====|=====|=====|=====|=====|=====
9 |      |       |     |     |     |     |   5 |           
  |======|=======|=====|=====|=====|=====|=====|=====
∞ |      |       |     |     |     |     |     |       

对于动态意义上的每个有效行(未知数量的行&未知数量的列),获取AVERAGE 的最佳方式是什么?

【问题讨论】:

刚刚意识到你自己问过这个问题。 ???谢谢分享! 【参考方案1】:

查询

1 级:

如果 C2:G 范围内的所有 5 个单元格都有值:

=QUERY(QUERY(C2:G, "select (C+D+E+F+G)/5"), "offset 1", )

如果不是,则跳过行:

如果空单元格被视为零:

=INDEX(QUERY(QUERY(C2:G*1, "select (Col1+Col2+Col3+Col4+Col5)/5"), "offset 1", ))

要删除零值,我们使用IFERROR(1/(1/...)) 包装:

=INDEX(IFERROR(1/(1/QUERY(QUERY(C2:G*1, 
 "select (Col1+Col2+Col3+Col4+Col5)/5"), "offset 1", ))))

要使 Col 引用动态,我们可以这样做:

=INDEX(IFERROR(1/(1/QUERY(QUERY(C2:G*1, 
 "select "&
 "("&JOIN("+", "Col"&ROW(INDIRECT("1:"&COLUMNS(C:G))))&")/"&COLUMNS(C:G)), 
 "offset 1", ))))


2 级:

如果空单元格不被视为零并且不应被跳过:

=INDEX(TRANSPOSE(QUERY(TRANSPOSE(E2:I), 
 "select "&TEXTJOIN(",", 1, IF(A2:A="",,
 "avg(Col"&ROW(A2:A)-ROW(A2)+1&")")))),, 2)

请注意,这取决于 A 列,因此 A 列中的缺失值将抵消结果

有趣的事实!我们可以将avg 换成maxmin

将其从 A 列的限制中解放出来并使其适用于任何有效行:

=INDEX(IFERROR(1/(1/TRANSPOSE(QUERY(TRANSPOSE(
 IF(TRIM(TRANSPOSE(QUERY(TRANSPOSE(C2:G),,9^9)))="", C2:G*0, C2:G)), 
 "select "&TEXTJOIN(",", 1, 
 "avg(Col"&ROW(A2:A)-ROW(A2)+1&")"))))),, 2)

如果范围内的 0 不应该被平均,我们可以添加一个小的 IF 语句:

=INDEX(IFERROR(1/(1/TRANSPOSE(QUERY(TRANSPOSE(
 IF(TRIM(TRANSPOSE(QUERY(TRANSPOSE(
 IF(C2:G>0, C2:G, )),,9^9)))="", C2:G*0, 
 IF(C2:G>0, C2:G, ))), 
 "select "&TEXTJOIN(",", 1, 
 "avg(Col"&ROW(A2:A)-ROW(A2)+1&")"))))),, 2)

这里我们使用了所谓的“垂直查询粉碎”,它获取给定范围内的所有值并将其集中到一列,其中每行的所有单元格都用空白空间连接起来作为副产品:

=FLATTEN(QUERY(TRANSPOSE(C2:G),,9^9))

除此之外,还有“横向查询粉碎”

=QUERY(C2:G,,9^9)

还有“终极 360° 双重查询粉碎”,它将范围内的所有单元格放在一个单元格中:

=QUERY(FLATTEN(QUERY(TRANSPOSE(C2:G),,9^9)),,9^9)

最后是“臭名昭著的负 360° 反向双重查询粉碎”,它将列优先于行:

=QUERY(FLATTEN(QUERY(C2:G,,9^9)),,9^9)

所有查询粉碎名称当然都受版权保护

回到主题...如上所述,范围内每行的所有单元格都与空白空间相连,即使是那些空白的,所以我们遇到了一种情况,即值之间有两个或多个空格。为了解决这个问题,我们使用TRIM 并引入一个简单的IF 语句来为给定范围内的空行分配0 值,例如。抵消偏移量:


MMULT

3 级:

MMULT 是一种重类公式,它能够执行加法、减法、乘法、除法甚至对数组/矩阵求和...但是,更大的数据集 = 更慢的公式计算(因为在 @987654397 @ 即使是空行也需要时间来执行 + - × ÷ 操作)...除非我们在两个方向上使用真正的动态范围无限...

获取具有给定范围值的最后一行:

=INDEX(MAX(IF(TRIM(FLATTEN(QUERY(TRANSPOSE(
 INDIRECT("C2:"&ROWS(A:A))),,9^9)))="",,ROW(A2:A))))

获取具有给定范围值的最后一列:

=INDEX(MAX(IF(TRIM(QUERY(INDIRECT("C2:"&ROWS(A:A)),,9^9))="",,COLUMN(C2:2))))

现在我们可以用一种简单的方式构造它:

=INDIRECT("C2:"&ADDRESS(9, 7))

等同于:

=INDEX(INDIRECT("C2:"&ADDRESS(MAX(IF(TRIM(FLATTEN(QUERY(TRANSPOSE(
 INDIRECT("C2:"&ROWS(A:A))),,9^9)))="",,ROW(A2:A))), 
 MAX(IF(TRIM(QUERY(INDIRECT("C2:"&ROWS(A:A)),,9^9))="",,COLUMN(C2:2))))))

或更短的替代:

=INDEX(INDIRECT("C2:"&ADDRESS(
 MAX((INDIRECT("C2:"&ROWS(A:A))<>"")*ROW(A2:A)), 
 MAX((INDIRECT("C2:"&ROWS(A:A))<>"")*COLUMN(C2:2)))))

因此简化的 MMULT 公式为:

=ARRAYFORMULA(IFERROR(
 MMULT(N(   C2:G9),           ROW(INDIRECT("C1:C"&COLUMNS(C:G)))^0)/
 MMULT(N(IF(C2:G9<>"", 1, )), ROW(INDIRECT("C1:C"&COLUMNS(C:G)))^0)))

如果我们想从范围中排除零值,公式为:

=ARRAYFORMULA(IFERROR(
 MMULT(N(   C2:G9),         ROW(INDIRECT("C1:C"&COLUMNS(C:G)))^0)/
 MMULT(N(IF(C2:G9>0, 1, )), ROW(INDIRECT("C1:C"&COLUMNS(C:G)))^0)))

4 级:

将以上所有内容组合在一起,使其无限动态,但仍仅限于有效数据集:

=INDEX(IFERROR(
 MMULT(N(   INDIRECT("C2:"&ADDRESS(
 MAX((INDIRECT("C2:"&ROWS(A:A))<>"")*ROW(A2:A)), 
 MAX((INDIRECT("C2:"&ROWS(A:A))<>"")*COLUMN(C2:2))))),           ROW(INDIRECT("C1:C"&
 MAX((INDIRECT("C2:"&ROWS(A:A))<>"")*COLUMN(C2:2))-(COLUMN(C2)-1)))^0)/
 MMULT(N(IF(INDIRECT("C2:"&ADDRESS(
 MAX((INDIRECT("C2:"&ROWS(A:A))<>"")*ROW(A2:A)), 
 MAX((INDIRECT("C2:"&ROWS(A:A))<>"")*COLUMN(C2:2))))<>"", 1, )), ROW(INDIRECT("C1:C"&
 MAX((INDIRECT("C2:"&ROWS(A:A))<>"")*COLUMN(C2:2))-(COLUMN(C2)-1)))^0)))

同样,不包括范围内为零的单元格:


荣誉奖:

@Erik Tyler级别:

与上一个公式相反的是运行MMULT

C2:?(all rows, all columns) 的总面积而不是 有效区域C2:? (excluding empty rows and columns) 避免0 × 0 = 0 的质量计算

包括零:

=INDEX(IFERROR(
 MMULT(   INDIRECT("C2:"&ROWS(C:C))*1,         SEQUENCE(COLUMNS(C2:2))^0)/ 
 MMULT(IF(INDIRECT("C2:"&ROWS(C:C))<>"", 1)*1, SEQUENCE(COLUMNS(C2:2))^0)))

不包括零:

=INDEX(IFERROR(
 MMULT(   INDIRECT("C2:"&ROWS(C:C))*1,       SEQUENCE(COLUMNS(C2:2))^0)/ 
 MMULT(IF(INDIRECT("C2:"&ROWS(C:C))>0, 1)*1, SEQUENCE(COLUMNS(C2:2))^0)))

@kishkin级别:

对于固定范围C2:G9MMULT 的平均值为:

=INDEX(IFERROR(
 MMULT( C2:G9*1,    FLATTEN(COLUMN(C:G))^0)/ 
 MMULT((C2:G9>0)*1, FLATTEN(COLUMN(C:G))^0)))

=INDEX(IFNA(VLOOKUP(ROW(C2:C), 
 QUERY(SPLIT(FLATTEN(ROW(C2:C)&"×"&C2:J), "×"),
 "select Col1,avg(Col2)
  where Col2 is not null
  group by Col1"), 2, )))

@MattKing级别:

=INDEX(QUERY(SPLIT(FLATTEN(ROW(C2:C)&"×"&OFFSET(C2,,,9^9, 9^9)), "×"),
 "select avg(Col2) 
  group by Col1  
  label avg(Col2)''"))

不包括零:

=INDEX(QUERY(SPLIT(FLATTEN(ROW(C2:C)&"×"&OFFSET(C2,,,9^9, 9^9)), "×"),
 "select avg(Col2)
  where Col2 <> 0 
  group by Col1  
  label avg(Col2)''"))

包括空单元格:

=INDEX(IFERROR(1/(1/QUERY(SPLIT(FLATTEN(ROW(C2:C)&"×"&OFFSET(C2,,,9^9, 9^9)*1), "×"),
 "select avg(Col2)
  group by Col1  
  label avg(Col2)''"))))

【讨论】:

如果您有时间请咨询=INDEX(MAX(IF(TRIM(FLATTEN(QUERY(TRANSPOSE( INDIRECT("C2:"&amp;ROWS(A:A))),,9^9)))="",,ROW(A2:A))))。 1. INDIRECT(...) 是否比 OFFSET(C2, 0, 0, ROWS(C2:C), COLUMNS(C2:2)) 更好? 2.FLATTEN这里只是转置了QUERY的结果,为什么不用TRANSPOSE呢? @kishkin 当然。 FLATTEN 的字符数少于 TRANSPOSE :D 与 INDEXARRYFORMULA 相同。正如 MattKing 几周前提到的那样,FLATTEN 将永远留在 GS,所以它现在是官方的 fx。至于OFFSET(...) 那也比INDIRECT(...) 长-没有其他理由。它只是一个简短的选择 感谢您的解释!并且在各个方面都是一个很好的答案!一些注意事项:在IF 中不需要IF(C2:G9&gt;0, 1)*1(和类似的),可能只是(C2:G9&gt;0)*1; 2.你不要过滤掉内部的空列和行,以防有一些:)。可以通过VLOOKUP(ROW(B2:B), non_empty_row_numbers, avg_on_totally_filtered_out_range, 2, 0) 完成。还是太多了? @kishkin 确实,这是有道理的。而对于VLOOKUP...我并没有想到...我更专注于寻找外部边界。无论如何,这是一种有趣的方法,但让我想知道它如何在具有 20k+ 行的巨大数据集上以速度表现。我肯定有一天会测试它。【参考方案2】:

您为此投入了大量时间。我希望人们欣赏它,更希望你这样做是为了其他人,而不是为了你自己。

看看你的最终公式,这些应该产生相同的结果(在 C2:? 中给出数据,就像你的例子一样):

在 B2 中(包括零):

=ArrayFormula(IFERROR(MMULT(INDIRECT("C2:"&amp;ROWS(C:C))*1,SEQUENCE(COLUMNS(C1:1),1,1,0))/ MMULT(IF(INDIRECT("C2:"&amp;ROWS(C:C))&lt;&gt;"",1,0),SEQUENCE(COLUMNS(C1:1),1,1,0))))

在 B2 中(不包括零):

=ArrayFormula(IFERROR(MMULT(INDIRECT("C2:"&amp;ROWS(C:C))*1,SEQUENCE(COLUMNS(C1:1),1,1,0))/ MMULT(IF(INDIRECT("C2:"&amp;ROWS(C:C))&lt;&gt;0,1,0),SEQUENCE(COLUMNS(C1:1),1,1,0))))

【讨论】:

虽然这是一个有趣的公式大小缩减,但计算出的矩阵将包括 C2:?(all rows, all columns) 的总面积,而不是有效面积 C2:?(excluding empty rows and columns) 例如。避免 0 × 0 = 0 的质量计算 在 5000 行 x 50 列的网格中运行所需的计算时间不到一秒。人们试图计算这么大范围的数字的可能性非常小。当然,可以通过添加一个或两个字符(例如,将 C1:1 更改为 C1:M1、C:C 更改为 C2:C500 等)来“控制”适用字段以反映其实际最大值要处理的已知范围。那么,在实际使用中,无论是否添加公式,这些都可以使用。 但是,我将所有这些公式视为人们学习和尝试事物的机会,而不是“最佳实践”。如果人们想花时间分解它们,你解释功能、原因和原因的详尽工作将大有帮助,因为你提供了很好的背景。 同意。可悲的是,一年后这将获得大约 200 次观看 xD【参考方案3】:

更新:我已经更新了原始帖子中的公式。 ROW() 应该始终排在第一位,这样数据中的缺失值就不会导致拆分。

=ARRAYFORMULA(QUERY(SPLIT(FLATTEN(ROW(C2:C)&"|"&OFFSET(C2,,,9^9,9^9)),"|"),"select AVG(Col2) group by Col1 label AVG(Col2)''"))

应该有效,除非我误解了这个问题。

不需要 vlookups 或 mmults 或过滤器或任何东西。

【讨论】:

不错的一个。如果某行包含全空单元格,这将抵消最终输出。另外,我需要用 where Col2 is not null 扩展它,因为第一个值被搞砸了 - i.stack.imgur.com/9EScK.png @player0 它不会用整行中的所有空单元格抵消最终输出。我已经测试了一堆。唯一需要的是大多数值不能为空。 @MattKing 你忘记了SPLIT 末尾的几个逗号。否则(如果原始数据单元格为空),您有时会在第一列中获得行号,因为SPLIT 会删除空结果。 i.imgur.com/xECBRWs.png @kishkin 啊,你写的。当我第一次这样做时,我先写了行号,所以我不需要那些额外的逗号。 (因为 ROW() 永远不会为空)我现在会改变我的答案。希望这也能为玩家 0 解决问题 @player0 更新了公式。我忘记了你需要 ROW() 是第一个。再试一次【参考方案4】:

我会尝试对@player0 的回答做一点补充。我将非常感谢任何 cmet 对此进行优化。


如果数据范围内有很多空行和列,不妨从MMULT 中排除。

第 1 步 - 过滤掉空行

我们有一个数据范围:从C2 到最后一行,再到最后一列(即J:J)。我将使用C2:K,详细解释见下文。

这个公式将为我们提供一个行号数组,其中至少有一个非空单元格。如果有空行,它也会有一个0,但是在这个数组中搜索并不重要,或者我们会在重要时将其过滤掉:

=ARRAYFORMULA(
  UNIQUE(FLATTEN((C2:K <> "") * ROW(C2:K)))
)

因此,为了从数据范围中过滤掉空行,我们使用FILTER 从上方检查我们的数组中是否有一行,如果在这种情况下则离开:

=ARRAYFORMULA(
  FILTER(
    C2:K*1,
    MATCH(
      ROW(C2:K),
      UNIQUE(FLATTEN((C2:K <> "") * ROW(C2:K))),
      0
    )
  )
)

第 2 步 - 过滤掉空列

要获得一个只有非空列号的数组,我们可以使用几乎相同的公式:

=ARRAYFORMULA(
  UNIQUE(FLATTEN((C2:K <> "") * SEQUENCE(1, COLUMNS(C2:K), COLUMN(C2))))
)

为什么使用SEQUENCE(1, COLUMNS(C2:K), COLUMN(C2))而不是COLUMN(C2:K)详见文末。

为了过滤掉空列,我们还使用FILTERMATCH 条件来搜索我们数组中的列号:

=ARRAYFORMULA(
  FILTER(
    C2:K*1,
    MATCH(
      SEQUENCE(1, COLUMNS(C2:K), COLUMN(C2)),
      UNIQUE(FLATTEN((C2:K <> "") * SEQUENCE(1, COLUMNS(C2:K), COLUMN(C2)))),
      0
    )
  )
)

为了过滤掉空行和空列,我们只使用两个FILTERs:

=ARRAYFORMULA(
  FILTER(
    FILTER(
      C2:K*1,
      MATCH(
        ROW(C2:K),
        UNIQUE(FLATTEN((C2:K <> "") * ROW(C2:K))),
        0
      )
    ),
    MATCH(
      SEQUENCE(1, COLUMNS(C2:K), COLUMN(C2)),
      UNIQUE(FLATTEN((C2:K <> "") * SEQUENCE(1, COLUMNS(C2:K), COLUMN(C2)))),
      0
    )
  )
)

原始数据范围将在内部变为:

第 3 步 - 执行MMULT

现在我们可以使用 MMULT 和该数据集来计算平均值:

=ARRAYFORMULA(
  MMULT(
    FILTER(
      FILTER(
        C2:K*1,
        MATCH(
          ROW(C2:K),
          UNIQUE(FLATTEN((C2:K <> "") * ROW(C2:K))),
          0
        )
      ),
      MATCH(
        SEQUENCE(1, COLUMNS(C2:K), COLUMN(C2)),
        UNIQUE(FLATTEN((C2:K <> "") * SEQUENCE(1, COLUMNS(C2:K), COLUMN(C2)))),
        0
      )
    ),
    SEQUENCE(
      ROWS(
        QUERY(
          UNIQUE(FLATTEN((C2:K <> "") * SEQUENCE(1, COLUMNS(C2:K), COLUMN(C2)))),
          "WHERE Col1 <> 0"
        )
      ),
      1,
      1,
      0
    )
  ) /
  MMULT(
    FILTER(
      FILTER(
        (C2:K <> "")*1,
        MATCH(
          ROW(C2:K),
          UNIQUE(FLATTEN((C2:K <> "") * ROW(C2:K))),
          0
        )
      ),
      MATCH(
        SEQUENCE(1, COLUMNS(C2:K), COLUMN(C2)),
        UNIQUE(FLATTEN((C2:K <> "") * SEQUENCE(1, COLUMNS(C2:K), COLUMN(C2)))),
        0
      )
    ),
    SEQUENCE(
      ROWS(
        QUERY(
          UNIQUE(FLATTEN((C2:K <> "") * SEQUENCE(1, COLUMNS(C2:K), COLUMN(C2)))),
          "WHERE Col1 <> 0"
        )
      ),
      1,
      1,
      0
    )
  )
)

关于原始数据行有点偏离。

第 4 步 - 填写平均列

为了使平均值与原始数据行一致,我们可以像这样使用VLOOKUP

=ARRAYFORMULA(
  IFNA(VLOOKUP(
    SEQUENCE(MAX((C2:K <> "") * ROW(C2:K)) - 1, 1, ROW(C2)),
    
      QUERY(UNIQUE(FLATTEN((C2:K <> "") * ROW(C2:K))), "WHERE Col1 <> 0"),
      MMULT(
        ...
      ) /
      MMULT(
        ...
      )
    ,
    2,
    0
  ))
)

在哪里

SEQUENCE(MAX((C2:K &lt;&gt; "") * ROW(C2:K)) - 1, 1, ROW(C2)) 是从第二个到最后一个非空的行号数组。我们不会用空字符串填充所有行。 QUERY(UNIQUE(FLATTEN((C2:K &lt;&gt; "") * ROW(C2:K))), "WHERE Col1 &lt;&gt; 0") 是一个非空行号数组,过滤掉的 0 用作搜索键。 IFNA 将返回一个空字符串以放在一个空数据行旁边。

最终公式

把它们放在一起:

=ARRAYFORMULA(
  IFNA(VLOOKUP(
    SEQUENCE(MAX((C2:K <> "") * ROW(C2:K)) - 1, 1, ROW(C2)),
    
      QUERY(UNIQUE(FLATTEN((C2:K <> "") * ROW(C2:K))), "WHERE Col1 <> 0"),
      MMULT(
        FILTER(
          FILTER(
            C2:K*1,
            MATCH(
              ROW(C2:K),
              UNIQUE(FLATTEN((C2:K <> "") * ROW(C2:K))),
              0
            )
          ),
          MATCH(
            SEQUENCE(1, COLUMNS(C2:K), COLUMN(C2)),
            UNIQUE(FLATTEN((C2:K <> "") * SEQUENCE(1, COLUMNS(C2:K), COLUMN(C2)))),
            0
          )
        ),
        SEQUENCE(
          ROWS(
            QUERY(
              UNIQUE(FLATTEN((C2:K <> "") * SEQUENCE(1, COLUMNS(C2:K), COLUMN(C2)))),
              "WHERE Col1 <> 0"
            )
          ),
          1,
          1,
          0
        )
      ) /
      MMULT(
        FILTER(
          FILTER(
            (C2:K <> "")*1,
            MATCH(
              ROW(C2:K),
              UNIQUE(FLATTEN((C2:K <> "") * ROW(C2:K))),
              0
            )
          ),
          MATCH(
            SEQUENCE(1, COLUMNS(C2:K), COLUMN(C2)),
            UNIQUE(FLATTEN((C2:K <> "") * SEQUENCE(1, COLUMNS(C2:K), COLUMN(C2)))),
            0
          )
        ),
        SEQUENCE(
          ROWS(
            QUERY(
              UNIQUE(FLATTEN((C2:K <> "") * SEQUENCE(1, COLUMNS(C2:K), COLUMN(C2)))),
              "WHERE Col1 <> 0"
            )
          ),
          1,
          1,
          0
        )
      )
    ,
    2,
    0
  ))
)


一些细节

为简洁起见,可以使用 INDEX 代替 ARRAYFORMULA(感谢 @player0,几个月前教我),但我喜欢 ARRAYFORMULA 的明确性。 为了清楚起见,我使用SEQUENCE 来构造1s 的列或行以明确。比如这个
SEQUENCE(
  ROWS(
    QUERY(
      UNIQUE(FLATTEN((C2:K <> "") * SEQUENCE(1, COLUMNS(C2:K), COLUMN(C2)))),
      "WHERE Col1 <> 0"
    )
  ),
  1,
  1,
  0
)

可以替换为

SIGN(
  QUERY(
    UNIQUE(FLATTEN((C2:K <> "") * SEQUENCE(1, COLUMNS(C2:K), COLUMN(C2)))),
    "WHERE Col1 <> 0"
  )
)

这有点短。 @player0 在这里还展示了一种提升到0 力量的方法:

QUERY(
  UNIQUE(FLATTEN((C2:K <> "") * SEQUENCE(1, COLUMNS(C2:K), COLUMN(C2)))),
  "WHERE Col1 <> 0"
)^0

但是(这只是我的猜测)我认为SEQUENCE的内部实现应该比提升到幂的操作更简单。

我使用范围C2:K,它比工作表上实际存在的列多一列。它不仅给出了C2 右侧的所有列及其下方的所有行的范围,而且在工作表右侧添加另一列时它也会更新:a demo。虽然它不会被突出显示。这个C2:K 几乎可以完美地替代这些方法(如果工作表上实际上存在ZZZ 列,则会出现问题):
INDIRECT("C2:" & ROWS(C:C))

OFFSET(C2,,, ROWS(C2:C), COLUMNS(C2:2))
使用C2:K 有一个小缺点:=ARRAYFORMULA(COLUMN(C2:K)) 将返回一个列号数组,即使是不存在的列号,所以我们需要改用=SEQUENCE(1, COLUMNS(C2:K), COLUMN(C2))

【讨论】:

【参考方案5】:

我认为使用VLOOKUPQUERY 的逐行平均有一个简单的答案。

这个在B2:

=ARRAYFORMULA(
  IFNA(
    VLOOKUP(
      ROW(B2:B),
      QUERY(
        
          FLATTEN(ROW(C2:J) + SEQUENCE(1, COLUMNS(C2:J),,)),
          FLATTEN(C2:J)
        ,
        "SELECT Col1, AVG(Col2)
         WHERE Col2 IS NOT NULL
         GROUP BY Col1"
      ),
      2,
      0
    )
  )
)

这可以轻松更改为 max、min、sum、count - 只需更改 QUERY 语句中的聚合函数。 同样的方法可用于按列聚合。 FLATTEN(C2:J) 可以更改为: FLATTEN(--C2:J) 将空单元格视为0s; FLATTEN(IFERROR(1/(1/C2:J))) 从平均值中排除 0s。 如果没有中间空行,则可以从公式中删除VLOOKUP,以及从SELECT 语句中删除Col1。 有一个没有VLOOKUPWHERE Col... 的较短版本(感谢@MattKing!):
=ARRAYFORMULA(
  QUERY(
    
      FLATTEN(ROW(C2:J) + SEQUENCE(1, COLUMNS(C2:J),,)),
      FLATTEN(IFERROR(1/(1/C2:J)))
    ,
    "SELECT AVG(Col2)
     GROUP BY Col1
     LABEL AVG(Col2) ''"
  )
)

我使用C2:J 范围,其列最多为I:I,有关此的一些详细信息:

范围C2:J 比工作表上实际存在的列多一列。它不仅给出了C2 右侧的所有列及其下方的所有行的范围,而且在工作表右侧添加另一列时也会更新:a demo。虽然它不会被突出显示。这个C2:J 几乎可以完美地替换这些方法(如果工作表上实际上存在ZZZ 列,则会出现问题):
INDIRECT("C2:" & ROWS(C:C))

OFFSET(C2,,, ROWS(C2:C), COLUMNS(C2:2))
使用C2:J 有一个小缺点:=ARRAYFORMULA(0 * COLUMN(C2:J)) 将返回一个列号数组,即使是不存在的列号(乘以0),所以我们需要改用=SEQUENCE(1, COLUMNS(C2:J),,)。李>

@player0,对此有什么想法吗?

【讨论】:

我确实喜欢它 如果你想检查的话,我把它缩短了大约 30 个字符,并且没有太多的数学运算:docs.google.com/spreadsheets/d/… @player0 不错!删除所有 \s is fun! :) 顺便说一句,\s 包括 \n @player0 我用数学(你的意思是+SEQUENCE,对吗?)是明确的,我尽量避免join-split(50k个字符限制,不能在我的数据中使用♥ )。还有一个相邻的更复杂的问题,我用MODSEQUENCE 解决了,不确定是否有更简单的解决方案。与列步长的逐行求和,如果好奇,2nd sheet in A3 50k 限制适用于 JOIN、TEXTJOIN、TRIM、REGEXREPLACE、SUBSTITUTE 等,但如果您使用 &amp;QUERY,则不会 - 在这种情况下,没有限制。是的,对于数学部分...有时感觉数学会减慢对极其庞大的数据集的计算速度,即使它很简单,例如 0*1。你到那里的有趣的 MOD 用法

以上是关于Google表格中无限真实动态范围平均值的ArrayFormula的主要内容,如果未能解决你的问题,请参考以下文章

使用 Google 表格中的单个公式从范围创建动态排序和过滤列表

Google表格中的动态货币换算

使用 Sumifs,其中一个范围可以匹配多个条件

新的 Google 表格自定义功能有时会无限期显示“正在加载...”

请求 Google 驱动器范围权限时会提供无限加载对话框

计算 Pandas 数据框中的平均真实范围列 [重复]