Google 张中无限真实动态范围的平均数组公式
ArrayFormula of Average on Infinite Truly Dynamic Range in Google Sheets
根据示例:
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级:
如果 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
换成 max
或 min
:
将其从 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)
这里我们使用了so-called “vertical query smash”,它获取给定范围内的所有值并将其集中到一个列中,其中每一行的所有单元格作为副产品与空 space 连接:
=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)
当然所有查询 smash 名称均受版权保护
回到主题...如上所述,范围内每行的所有单元格都与空 space 连在一起,即使是那些空单元格,所以我们遇到了双倍或多倍 spaces 值之间。为了解决这个问题,我们使用 TRIM
并引入一个简单的 IF
语句来为给定范围内的空行分配 0 值,例如。抵消偏移量:
多
3级:
MMULT
是一种重class公式,可以对arrays/matrixes进行加减乘除甚至运行宁总...然而,数据集越大 = 公式计算越慢(因为在 MMULT
中,即使是空行也需要时间来执行 + - × ÷
操作)...除非我们使用 真正的动态范围 双向无限...
获取具有给定范围值的最后一行:
=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)))
同样,不包括范围内为零的单元格:
荣誉奖:
等级:
与前面的公式相反的是 运行 MMULT
在
C2:?
的总面积(all rows, all columns)
而不是
- 有效区域
C2:?
(excluding empty rows and columns)
避开了0 × 0 = 0
的mass-calculations
包括零:
=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)))
等级:
对于固定范围 C2:G9
,MMULT
平均值为:
=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, )))
等级:
=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)''"))))
您为此投入了大量时间。我希望人们欣赏它,更希望你这样做是为了其他人,而不是为了你自己。
查看您的最终公式,它们应该产生相同的结果(在 C2:? 中提供数据,如您的示例所示):
在 B2 中(包括零):
=ArrayFormula(IFERROR(MMULT(INDIRECT("C2:"&ROWS(C:C))*1,SEQUENCE(COLUMNS(C1:1),1,1,0))/ MMULT(IF(INDIRECT("C2:"&ROWS(C:C))<>"",1,0),SEQUENCE(COLUMNS(C1:1),1,1,0))))
在 B2 中(不包括零):
=ArrayFormula(IFERROR(MMULT(INDIRECT("C2:"&ROWS(C:C))*1,SEQUENCE(COLUMNS(C1:1),1,1,0))/ MMULT(IF(INDIRECT("C2:"&ROWS(C:C))<>0,1,0),SEQUENCE(COLUMNS(C1:1),1,1,0))))
我会尝试对@player0 的回答做一些补充。我将非常感谢任何关于优化此的评论。
如果数据范围内有很多空行和空列,不妨将它们从 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)
,详见文末
为了过滤掉空列,我们还使用 FILTER
和 MATCH
条件来搜索数组中的列号:
=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
)
)
)
为了过滤掉空行和空列,我们只使用两个 FILTER
:
=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 <> "") * ROW(C2:K)) - 1, 1, ROW(C2))
是一个行号数组,从第2行到最后none-空行。我们不会用空字符串填充所有行。
QUERY(UNIQUE(FLATTEN((C2:K <> "") * ROW(C2:K))), "WHERE Col1 <> 0")
是一个非空行号的数组,0
过滤掉用作搜索的关键字。
IFNA
将 return 一个空字符串放在空数据行旁边。
最终公式
综合起来:
=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
.[=133 的明确性=]
- 为了清楚起见,我使用
SEQUENCE
来构造一列或一行1
。比如这个
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
比 sheet 上实际存在的多一列。它不仅给出了 C2
右侧的所有列的范围以及它下面的所有行,而且还会在 sheet 右侧添加另一列的情况下进行更新:a demo。虽然它不会被突出显示。这个 C2:K
几乎可以完美地替换那些方法:
INDIRECT("C2:" & ROWS(C:C))
OFFSET(C2,,, ROWS(C2:C), COLUMNS(C2:2))
- 使用
C2:K
有一个小缺点:=ARRAYFORMULA(COLUMN(C2:K))
会return一个列号数组,即使是不存在的,所以我们需要使用=SEQUENCE(1, COLUMNS(C2:K), COLUMN(C2))
相反。
我认为使用 VLOOKUP
和 QUERY
.
的行平均有一个简单的答案
这个在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
)
)
)
- 这可以很容易地更改为最大值、最小值、总和、计数 - 只需更改
QUERY
语句中的聚合函数。
- 同样的方法可以用于按列聚合。
FLATTEN(C2:J)
可以改为:
FLATTEN(--C2:J)
将空单元格视为 0
s;
FLATTEN(IFERROR(1/(1/C2:J)))
从平均值中排除 0
s。
- 如果没有中间空行,可以从公式中删除
VLOOKUP
,以及从 SELECT
语句中删除 Col1
。
- 有一个较短的版本(感谢@MattKing!)没有
VLOOKUP
和 WHERE Col...
:
=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
,比 sheet 上实际存在的多一列。它不仅给出了 C2
右侧的所有列的范围以及它下面的所有行,而且还会在 sheet 右侧添加另一列的情况下进行更新:a demo。虽然它不会被突出显示。这个 C2:J
几乎可以完美地(如果 sheet 上实际上有 ZZZ
列就会出现问题)替换这些方法:
INDIRECT("C2:" & ROWS(C:C))
OFFSET(C2,,, ROWS(C2:C), COLUMNS(C2:2))
- 使用
C2:J
有一个小缺点:=ARRAYFORMULA(0 * COLUMN(C2:J))
将 return 一个列号数组,即使对于不存在的列号也是如此(乘以 0
),所以我们需要使用 =SEQUENCE(1, COLUMNS(C2:J),,)
来代替。
@player0,有什么想法吗?
更新:我更新了我原来的公式 post。 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 或过滤器或任何东西。
根据示例:
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级:
如果 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
换成 max
或 min
:
将其从 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)
这里我们使用了so-called “vertical query smash”,它获取给定范围内的所有值并将其集中到一个列中,其中每一行的所有单元格作为副产品与空 space 连接:
=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)
当然所有查询 smash 名称均受版权保护
回到主题...如上所述,范围内每行的所有单元格都与空 space 连在一起,即使是那些空单元格,所以我们遇到了双倍或多倍 spaces 值之间。为了解决这个问题,我们使用 TRIM
并引入一个简单的 IF
语句来为给定范围内的空行分配 0 值,例如。抵消偏移量:
多
3级:
MMULT
是一种重class公式,可以对arrays/matrixes进行加减乘除甚至运行宁总...然而,数据集越大 = 公式计算越慢(因为在 MMULT
中,即使是空行也需要时间来执行 + - × ÷
操作)...除非我们使用 真正的动态范围 双向无限...
获取具有给定范围值的最后一行:
=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)))
同样,不包括范围内为零的单元格:
荣誉奖:
等级:
与前面的公式相反的是 运行 MMULT
在
C2:?
的总面积(all rows, all columns)
而不是- 有效区域
C2:?
(excluding empty rows and columns)
避开了0 × 0 = 0
的mass-calculations
包括零:
=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)))
等级:
对于固定范围 C2:G9
,MMULT
平均值为:
=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, )))
等级:
=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)''"))))
您为此投入了大量时间。我希望人们欣赏它,更希望你这样做是为了其他人,而不是为了你自己。
查看您的最终公式,它们应该产生相同的结果(在 C2:? 中提供数据,如您的示例所示):
在 B2 中(包括零):
=ArrayFormula(IFERROR(MMULT(INDIRECT("C2:"&ROWS(C:C))*1,SEQUENCE(COLUMNS(C1:1),1,1,0))/ MMULT(IF(INDIRECT("C2:"&ROWS(C:C))<>"",1,0),SEQUENCE(COLUMNS(C1:1),1,1,0))))
在 B2 中(不包括零):
=ArrayFormula(IFERROR(MMULT(INDIRECT("C2:"&ROWS(C:C))*1,SEQUENCE(COLUMNS(C1:1),1,1,0))/ MMULT(IF(INDIRECT("C2:"&ROWS(C:C))<>0,1,0),SEQUENCE(COLUMNS(C1:1),1,1,0))))
我会尝试对@player0 的回答做一些补充。我将非常感谢任何关于优化此的评论。
如果数据范围内有很多空行和空列,不妨将它们从 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)
,详见文末
为了过滤掉空列,我们还使用 FILTER
和 MATCH
条件来搜索数组中的列号:
=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
)
)
)
为了过滤掉空行和空列,我们只使用两个 FILTER
:
=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 <> "") * ROW(C2:K)) - 1, 1, ROW(C2))
是一个行号数组,从第2行到最后none-空行。我们不会用空字符串填充所有行。QUERY(UNIQUE(FLATTEN((C2:K <> "") * ROW(C2:K))), "WHERE Col1 <> 0")
是一个非空行号的数组,0
过滤掉用作搜索的关键字。IFNA
将 return 一个空字符串放在空数据行旁边。
最终公式
综合起来:
=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
.[=133 的明确性=]- 为了清楚起见,我使用
SEQUENCE
来构造一列或一行1
。比如这个
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
比 sheet 上实际存在的多一列。它不仅给出了C2
右侧的所有列的范围以及它下面的所有行,而且还会在 sheet 右侧添加另一列的情况下进行更新:a demo。虽然它不会被突出显示。这个C2:K
几乎可以完美地替换那些方法:
INDIRECT("C2:" & ROWS(C:C))
OFFSET(C2,,, ROWS(C2:C), COLUMNS(C2:2))
- 使用
C2:K
有一个小缺点:=ARRAYFORMULA(COLUMN(C2:K))
会return一个列号数组,即使是不存在的,所以我们需要使用=SEQUENCE(1, COLUMNS(C2:K), COLUMN(C2))
相反。
我认为使用 VLOOKUP
和 QUERY
.
这个在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
)
)
)
- 这可以很容易地更改为最大值、最小值、总和、计数 - 只需更改
QUERY
语句中的聚合函数。 - 同样的方法可以用于按列聚合。
FLATTEN(C2:J)
可以改为:FLATTEN(--C2:J)
将空单元格视为0
s;FLATTEN(IFERROR(1/(1/C2:J)))
从平均值中排除0
s。
- 如果没有中间空行,可以从公式中删除
VLOOKUP
,以及从SELECT
语句中删除Col1
。 - 有一个较短的版本(感谢@MattKing!)没有
VLOOKUP
和WHERE Col...
:
=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
,比 sheet 上实际存在的多一列。它不仅给出了C2
右侧的所有列的范围以及它下面的所有行,而且还会在 sheet 右侧添加另一列的情况下进行更新:a demo。虽然它不会被突出显示。这个C2:J
几乎可以完美地(如果 sheet 上实际上有ZZZ
列就会出现问题)替换这些方法:
INDIRECT("C2:" & ROWS(C:C))
OFFSET(C2,,, ROWS(C2:C), COLUMNS(C2:2))
- 使用
C2:J
有一个小缺点:=ARRAYFORMULA(0 * COLUMN(C2:J))
将 return 一个列号数组,即使对于不存在的列号也是如此(乘以0
),所以我们需要使用=SEQUENCE(1, COLUMNS(C2:J),,)
来代替。
@player0,有什么想法吗?
更新:我更新了我原来的公式 post。 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 或过滤器或任何东西。