没有内置函数如何实现累加?
How to realize cummulative sum without built-in function?
我需要实现每天的累计求和。
比如我的数据集如下:
buyer | bread | date |
---------------------------
b1 | 2 | 2018-01-01|
b1 | 3 | 2018-01-02|
b1 | 1 | 2018-01-04|
b2 | 2 | 2018-01-02|
我需要选择如下:
buyer | cum_sum_on_01_01 | cum_sum_on_01_02 | cum_sum_on_01_03 | cum_sum_on_01_04 | cum_sum_on_01_05 |...
----------------------------------------------------------------------------------------------------------
b1 | 2 | 5 | 5 | 6 | 6 |...
b2 | 0 | 2 | 2 | 2 | 2 |...
怎么做?
without built-in function
有什么意义?目前在 ClickHouse 中实现累加和的唯一方法是 arrayCumSum
。所以答案是构建候选数组并将其传递给arrayCumSum
。以下是步骤:
第 1 步:为每个买家构建面包数组
SELECT
buyer,
groupArray(bread) AS breads
FROM
(
SELECT
buyer,
sum(bread) AS bread,
date
FROM bbd
ALL RIGHT JOIN
(
WITH
toDate('2018-01-01') AS min_date,
toDate('2018-01-31') AS max_date
SELECT
arrayJoin(buyers) AS buyer,
arrayJoin(arrayMap(i -> (min_date + toIntervalDay(i)), range(toUInt64((max_date - min_date) + 1)))) AS date
FROM
(
SELECT groupUniqArray(buyer) AS buyers
FROM bbd
)
) USING (buyer, date)
GROUP BY
buyer,
date
ORDER BY
buyer ASC,
date ASC
)
GROUP BY buyer
┌─buyer─┬─breads──────────────────────────────────────────────────────────┐
│ b1 │ [2,3,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] │
│ b2 │ [0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] │
└───────┴─────────────────────────────────────────────────────────────────┘
第 2 步:为每个买家应用 arrayCumSum
将groupArray(bread) AS breads
替换为arrayCumSum(groupArray(bread)) AS breads
┌─buyer─┬─breads──────────────────────────────────────────────────────────┐
│ b1 │ [2,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6] │
│ b2 │ [0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2] │
└───────┴─────────────────────────────────────────────────────────────────┘
接受的答案非常好,您确实应该使用内置的 arrayCumSum 函数来计算累积和。但是,如果最初问题的动机之一是找出如何创建 accumulate/folding 样式函数 一般 当它们不受 ClickHouse 原生支持时(例如,CumMax, CumMin 等),这是一种适用于 ClickHouse 中的任何聚合函数的方法。
实现这一点的核心逻辑是使用 arrayReduceInRanges 并使用 arrayMap 和 arrayEnumerate 生成 (1, 1), (1, 2), ... (1, n)
形式的所有元组范围。然后,无论您选择哪个函数作为 arrayReduceInRanges 的高阶聚合函数,例如'sum' 或 'max',将变成函数的累积数组形式。该逻辑如下所示:
WITH arr as (SELECT groupArray(some_col) AS arr_some_col FROM some_table)
SELECT
arrayReduceInRanges(
'sum'
arrayMap(x -> (1, x), arrayEnumerate(arr_some_col))
arr_some_col
)
FROM arr
从这里开始,您可以将数组中返回的值进行数组联接,或者将它们保留为数组形式以供进一步计算。
对于面包的特定应用程序,这里有一些可以使用上述核心逻辑的东西(假设你的 table 被命名为 bread_data):
WITH ordered AS (SELECT * FROM bread_data ORDER BY date, buyer),
agg AS (
SELECT
buyer,
untuple(
arrayJoin(
arrayZip(
groupArray(date),
arrayReduceInRanges(
-- 'sum' or any ClickHouse aggregate function.
'sum',
arrayMap(x -> (1, x), arrayEnumerate(groupArray(bread))),
groupArray(bread)
)
)
)
)
FROM ordered
GROUP BY buyer
)
SELECT buyer, _ut_1 AS date, _ut_2 as cum_bread
FROM agg
ORDER BY date
请注意第一个 WITH
子句,它按日期和买家对 table 进行排序,以便保证后续的 groupArray 调用以相同、一致的顺序构建它们的数组(ClickHouse 文档指出否则任何对 groupArray 的调用都可以以随机顺序构造元素。
它可能看起来很复杂,但是当你使用第一个核心逻辑片段分解它时,事实上这里的很多语法都是围绕数组分组和取消分组的,这样我们就可以在数组中完成我们的主要工作space,它应该有一些直观的意义。
输出将如下所示:
+-------+------------+-----------+
| buyer | date | cum_bread |
+-------+------------+-----------+
| b1 | 2018-01-01 | 2 |
| b2 | 2018-01-02 | 2 |
| b1 | 2018-01-02 | 5 |
| b1 | 2018-01-04 | 6 |
+-------+------------+-----------+
我需要实现每天的累计求和。
比如我的数据集如下:
buyer | bread | date |
---------------------------
b1 | 2 | 2018-01-01|
b1 | 3 | 2018-01-02|
b1 | 1 | 2018-01-04|
b2 | 2 | 2018-01-02|
我需要选择如下:
buyer | cum_sum_on_01_01 | cum_sum_on_01_02 | cum_sum_on_01_03 | cum_sum_on_01_04 | cum_sum_on_01_05 |...
----------------------------------------------------------------------------------------------------------
b1 | 2 | 5 | 5 | 6 | 6 |...
b2 | 0 | 2 | 2 | 2 | 2 |...
怎么做?
without built-in function
有什么意义?目前在 ClickHouse 中实现累加和的唯一方法是 arrayCumSum
。所以答案是构建候选数组并将其传递给arrayCumSum
。以下是步骤:
第 1 步:为每个买家构建面包数组
SELECT
buyer,
groupArray(bread) AS breads
FROM
(
SELECT
buyer,
sum(bread) AS bread,
date
FROM bbd
ALL RIGHT JOIN
(
WITH
toDate('2018-01-01') AS min_date,
toDate('2018-01-31') AS max_date
SELECT
arrayJoin(buyers) AS buyer,
arrayJoin(arrayMap(i -> (min_date + toIntervalDay(i)), range(toUInt64((max_date - min_date) + 1)))) AS date
FROM
(
SELECT groupUniqArray(buyer) AS buyers
FROM bbd
)
) USING (buyer, date)
GROUP BY
buyer,
date
ORDER BY
buyer ASC,
date ASC
)
GROUP BY buyer
┌─buyer─┬─breads──────────────────────────────────────────────────────────┐
│ b1 │ [2,3,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] │
│ b2 │ [0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] │
└───────┴─────────────────────────────────────────────────────────────────┘
第 2 步:为每个买家应用 arrayCumSum
将groupArray(bread) AS breads
替换为arrayCumSum(groupArray(bread)) AS breads
┌─buyer─┬─breads──────────────────────────────────────────────────────────┐
│ b1 │ [2,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6] │
│ b2 │ [0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2] │
└───────┴─────────────────────────────────────────────────────────────────┘
接受的答案非常好,您确实应该使用内置的 arrayCumSum 函数来计算累积和。但是,如果最初问题的动机之一是找出如何创建 accumulate/folding 样式函数 一般 当它们不受 ClickHouse 原生支持时(例如,CumMax, CumMin 等),这是一种适用于 ClickHouse 中的任何聚合函数的方法。
实现这一点的核心逻辑是使用 arrayReduceInRanges 并使用 arrayMap 和 arrayEnumerate 生成 (1, 1), (1, 2), ... (1, n)
形式的所有元组范围。然后,无论您选择哪个函数作为 arrayReduceInRanges 的高阶聚合函数,例如'sum' 或 'max',将变成函数的累积数组形式。该逻辑如下所示:
WITH arr as (SELECT groupArray(some_col) AS arr_some_col FROM some_table)
SELECT
arrayReduceInRanges(
'sum'
arrayMap(x -> (1, x), arrayEnumerate(arr_some_col))
arr_some_col
)
FROM arr
从这里开始,您可以将数组中返回的值进行数组联接,或者将它们保留为数组形式以供进一步计算。
对于面包的特定应用程序,这里有一些可以使用上述核心逻辑的东西(假设你的 table 被命名为 bread_data):
WITH ordered AS (SELECT * FROM bread_data ORDER BY date, buyer),
agg AS (
SELECT
buyer,
untuple(
arrayJoin(
arrayZip(
groupArray(date),
arrayReduceInRanges(
-- 'sum' or any ClickHouse aggregate function.
'sum',
arrayMap(x -> (1, x), arrayEnumerate(groupArray(bread))),
groupArray(bread)
)
)
)
)
FROM ordered
GROUP BY buyer
)
SELECT buyer, _ut_1 AS date, _ut_2 as cum_bread
FROM agg
ORDER BY date
请注意第一个 WITH
子句,它按日期和买家对 table 进行排序,以便保证后续的 groupArray 调用以相同、一致的顺序构建它们的数组(ClickHouse 文档指出否则任何对 groupArray 的调用都可以以随机顺序构造元素。
它可能看起来很复杂,但是当你使用第一个核心逻辑片段分解它时,事实上这里的很多语法都是围绕数组分组和取消分组的,这样我们就可以在数组中完成我们的主要工作space,它应该有一些直观的意义。
输出将如下所示:
+-------+------------+-----------+
| buyer | date | cum_bread |
+-------+------------+-----------+
| b1 | 2018-01-01 | 2 |
| b2 | 2018-01-02 | 2 |
| b1 | 2018-01-02 | 5 |
| b1 | 2018-01-04 | 6 |
+-------+------------+-----------+