как например:
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
для каждой допустимой строки в динамическом смысле терминов (неизвестное количество строк и неизвестное количество столбцов)?
если вы оказались здесь случайно для бегущей / кумулятивной / скользящей средней, см.: https://stackoverflow.com/a/59120993/5632629
если все 5 ячеек в диапазоне C2:G имеют значения:
=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", ))))
если пустые ячейки не считаются нулями и не должны быть пропущены:
=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)
здесь мы использовали так называемое «вертикальное разбиение запроса», которое берет все значения в заданном диапазоне и концентрирует их в одном столбце, где все ячейки в каждой строке соединяются с пустым пространством в качестве побочного продукта:
=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
- это своего рода формула тяжелого класса, которая может выполнять сложение, вычитание, умножение, деление, даже промежуточный итог по массивам/матрицам... однако, чем больше набор данных, тем медленнее вычисление формулы (потому что в 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)))
объединяя все вышеперечисленное, чтобы сделать его бесконечно динамичным и по-прежнему ограниченным допустимым набором данных:
=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)))
опять же, не включая ячейки с нулями в диапазоне:
с 20 сентября 2022 года мы можем использовать новые функции, упрощающие работу:
поэтому, чтобы сразу перейти к закрытому диапазону, мы можем взять среднее значение, например:
=IFERROR(BYROW(C2:G9, LAMBDA(x, AVERAGE(x))))
и чтобы получить среднее значение по столбцам, мы просто заменяем BYROW на BYCOL. теперь, чтобы сделать диапазон открытым и по-настоящему динамичным, мы можем изменить приведенную выше формулу следующим образом:
=IFERROR(BYROW(INDEX(INDIRECT("C2:"&ADDRESS(
MAX((INDIRECT("C2:"&ROWS(A:A))<>"")*ROW(A2:A)),
MAX((INDIRECT("C2:"&ROWS(A:A))<>"")*COLUMN(C2:2))))), LAMBDA(x, AVERAGE(x))))
мы можем сделать это короче на 12 символов, например:
=IFERROR(BYROW(INDEX(OFFSET(C2,,,
MAX((INDIRECT("C2:"&ROWS(C:C))<>"")*ROW(C2:C)),
MAX((INDIRECT("C2:"&ROWS(C:C))<>"")*COLUMN(C2:2)))), LAMBDA(x, AVERAGE(x))))
чтобы исключить нули из вывода:
=INDEX(IFERROR(1/(1/BYROW(OFFSET(C2,,,
MAX((INDIRECT("C2:"&ROWS(C:C))<>"")*ROW(C2:C)),
MAX((INDIRECT("C2:"&ROWS(C:C))<>"")*COLUMN(C2:2))), LAMBDA(x, AVERAGE(x))))))
чтобы исключить нули из ввода:
=INDEX(IFERROR(1/(1/BYROW(OFFSET(C2,,,
MAX((INDIRECT("C2:"&ROWS(C:C))<>"")*ROW(C2:C)),
MAX((INDIRECT("C2:"&ROWS(C:C))<>"")*COLUMN(C2:2))), LAMBDA(x, AVERAGEIF(x, ">0"))))))
или если пустые ячейки следует рассматривать как нули:
=INDEX(IFERROR(1/(1/BYROW(1*OFFSET(C2,,,
MAX((INDIRECT("C2:"&ROWS(C:C))<>"")*ROW(C2:C)),
MAX((INDIRECT("C2:"&ROWS(C:C))<>"")*COLUMN(C2:2))), LAMBDA(x, AVERAGE(x))))))
также стоит упомянуть ограничение BYROW в ~ 99990 строк
полной противоположностью предыдущей формулы будет запуск 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)))
для фиксированного диапазона 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)''"))))
Несколько вопросов, если у вас есть время о =INDEX(MAX(IF(TRIM(FLATTEN(QUERY(TRANSPOSE( INDIRECT("C2:"&ROWS(A:A))),,9^9))) = "",,ROW(A2:A))))
. 1. INDIRECT(...)
чем-то лучше, чем OFFSET(C2, 0, 0, ROWS(C2:C), COLUMNS(C2:2))
? 2. FLATTEN
здесь просто транспонирует результат QUERY
, почему бы не использовать TRANSPOSE
?
@кишкин конечно. FLATTEN
имеет меньше символов, чем TRANSPOSE
:D то же самое с INDEX
против ARRYFORMULA
. как МэттКинг упомянул несколько недель назад, FLATTEN
навсегда останется в GS, так что теперь это официальный эффект. что касается OFFSET(...)
, это тоже длиннее, чем INDIRECT(...)
- для этого нет другой причины. это просто короткая альтернатива
спасибо за пояснения! И отличный ответ во всех смыслах! Несколько замечаний: в IF
нет необходимости в IF(C2:G9>0, 1)*1
(и им подобных), можно просто (C2:G9>0)*1
; 2. Вы не отфильтровываете внутренние пустые столбцы и строки, если они есть :). Можно сделать с VLOOKUP(ROW(B2:B), {non_empty_row_numbers, avg_on_totally_filtered_out_range}, 2, 0)
. Или это слишком?
@kishkin действительно, это имеет смысл. а для VLOOKUP
... это не приходило мне в голову... Я был больше сосредоточен на поиске внешних границ. в любом случае, это интересный подход, но мне интересно, как он будет работать с точки зрения скорости на каком-то огромном наборе данных с более чем 20 000 строк. На днях обязательно протестирую.
"потрясающий двойной запрос на 360°" X_X лол
Ты тратишь на это кучу времени. Я надеюсь, что люди оценят это, тем более, что вы сделали это для всех остальных, а не для себя.
Глядя на ваши окончательные формулы, они должны давать те же результаты (укажите данные в 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))))
хотя это интересное сокращение размера формулы, вычисленные матрицы будут включать общую площадь C2:?(all rows, all columns)
вместо действительной площади C2:?(excluding empty rows and columns)
, например. избегая массовых вычислений 0 × 0 = 0
Выполнение любой из сеток из 5000 строк по 50 столбцов занимает менее секунды для расчета. Вероятность того, что люди будут пытаться вычислить такой большой диапазон чисел, очень мала. И, конечно же, можно «обуздать» применимое поле, добавив один или два символа (например, изменив C1:1 на C1:M1, C:C на C2:C500 и т. д.), чтобы отразить их фактический максимум. известный диапазон для обработки. Таким образом, в реальном мире они будут работать с дополнениями к формулам или без них.
Тем не менее, я рассматриваю все эти формулы как возможность для людей учиться и пробовать новые вещи, а не как «лучшие практики». И ваша тщательная работа по объяснению функций, почему и почему будет иметь большое значение, если люди захотят потратить время на их разбор, потому что вы дали отличный контекст.
согласованный. к сожалению, через год это будет около 200 просмотров xD
Я попытаюсь сделать небольшое дополнение к ответу @player0. И я буду очень признателен за любые комментарии по оптимизации этого.
Если внутри диапазона данных много пустых строк и столбцов, их также можно исключить из MMULT
.
У нас есть диапазон данных: от 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
)
)
)
Чтобы получить массив только с непустыми номерами столбцов, мы можем использовать почти ту же формулу:
=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
s:
=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
)
)
)
Исходный диапазон данных внутренне станет следующим:
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
)
)
)
Это немного не так в отношении исходных строк данных.
Чтобы средние значения соответствовали исходным строкам данных, мы можем использовать 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-й до последней непустой. Мы не будем заполнять все строки пустыми строками.QUERY(UNIQUE(FLATTEN((C2:K <> "") * ROW(C2:K))), "WHERE Col1 <> 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
для создания столбца или строки 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
, который на один столбец больше, чем на самом деле существует на листе. Он не только дает диапазон всех столбцов справа от C2
и всех строк от него, но и обновляется в случае добавления другого столбца справа от листа: демо. Хотя это и не подчёркивается. Этот 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))
.Я думаю, что есть простой ответ для среднего по строкам с использованием 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
можно убрать из формулы, а Col1
из оператора SELECT
.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
, который на один столбец больше, чем существует на листе. Он не только дает диапазон всех столбцов справа от C2
и всех строк от него, но и обновляется в случае добавления другого столбца справа от листа: демо. Хотя это и не подчёркивается. Этот 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
весело! :) И кстати \s
включает \n
.
@player0 Я использую математику (вы имеете в виду +
и SEQUENCE
, верно?), чтобы быть явным, и я стараюсь избегать разделения соединения (ограничение 50 000 символов, нельзя использовать ♥ в моих данных). Также есть соседняя более сложная проблема, которую я решил с помощью MOD
и SEQUENCE
, не уверен в более простом решении. Сумма по строкам с шагом столбца, 2-й лист в A3
, если интересно.
Ограничение 50 КБ для JOIN, TEXTJOIN, TRIM, REGEXREPLACE, SUBSTITUTE и т. д., но не в том случае, если вы используете &
или QUERY
— в таких случаях ограничений нет. да, для математической части... иногда кажется, что математика замедляет вычисления на чрезвычайно огромных наборах данных, даже если они такие простые, как 0 * 1. интересное использование мода вы там получили
@kishkin как OFFSET(C2,,,9^9,9^9) для бесконечных диапазонов 2d вместо необходимости ссылаться на столбцы() и строки()
@MattKing тебе не нравится C2:J
вместо OFFSET(C2,,,9^9,9^9)
? :) Также спасибо за идею не использовать WHERE
, что избавляет от необходимости использовать VLOOKUP
.
ОБНОВЛЕНИЕ: я обновил формулу из моего исходного сообщения. 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 он не будет компенсировать окончательный вывод со всеми пустыми ячейками в целой строке. Я протестировал его кучу. Единственное, что требуется, это чтобы большинство значений не были пустыми.
и все дело в том, чтобы НЕ использовать «где Col2 не равен нулю»
Я скопировал формулу из вашего ответа: i.stack.imgur.com/RA5Hj.png так как вы видите ее смещение на 1-3 строки, первое значение неверно, и большинство ячеек не пустой. красный столбец A содержит стандартные перетаскиваемые AVERAGE(C2:2)
эффекты для сравнения. что я напутал?
@MattKing, ты забыл несколько запятых в конце SPLIT
. В противном случае (в случае пустых исходных ячеек данных) вы иногда получаете номера строк в 1-м столбце, потому что SPLIT
удаляет пустые результаты. i.imgur.com/xECBRWs.png
@kishkin а, ты пишешь. Когда я впервые сделал это, я сначала написал номер строки, поэтому мне не нужны были эти лишние запятые. (потому что ROW() никогда не бывает пустым) Сейчас я изменю свой ответ. Надеюсь, это также прояснит ситуацию для player0.
@player0 обновленная формула. Я забыл, что вам нужно, чтобы ROW() был первым. Попробуйте снова
@player0 получу ли я свой "уровень" в исходном посте :D ?
конечно, вы будете :D это неизбежно :)
только что понял, что вы сами спросили об этом. 😀 Спасибо, что поделились!