结转舍入

时间:2018-12-10 15:42:32

标签: clickhouse

我有一组数组

    [0.21, 0.21, 0.21, 0.21] as a,
    [0.31, 0.31, 0.31, 0.31] as b,
    [0.48, 0.48, 0.48, 0.48] as c 

如果您注意到a1 + b1 + c1 = 1(其中a1是数组a的第一个元素)。 我想实现一个四舍五入的答案,

    [0, 0, 1, 0] for a
    [0, 1, 0, 0] for b
    [1, 0, 0, 1] for c

步骤1。 对a1,b1,c1做一个舍入函数->这将给我们a1 = 0,b1 = 0和c1 = 0的值(对于下一组值,即a2,结转值为0.21、0.31和0.48 ,b2和c2。

第2步。 但是,由于在步骤1之后,round(a1)+ round(b1)+ round(c1)

例如 第一次分配后,c1将四舍五入为1(得到的0.52应该从c2的0.48-0.52 = -0.04减少)。类似地,由于四舍五入后a1和b1分别为0和0,因此我们会将0.21和0.31结转到a2,b2给我们a2 = 0.21 + 0.21 = 0.42和b2 = 0.31 + 0.31 = 0.62和c2 = 0.48-0.52 = -0.04 < / p>

步骤3: 对第二个元素重复步骤1,在这种情况下将变为round(a2)= round(0.42)= 0,round(b2)= round(0.62)= 1,round(c2)= round(-0.04)= 0。

Carryforwrd从a2-> 0.42,从b2-> -0.38,从c2 = -0.04到下一个元素的差异

a3将变为0.21 + 0.42 = 0.63,b3将变为= 0.31-0.38 = -0.07,而c3将变为0.48-0.04 = 0.44

将第3个元素舍入后,a3的舍入将变为1,b3-> 0,c3-> 0

...等等。

有什么办法可以使用功能强大的数组来做到这一点?

1 个答案:

答案 0 :(得分:1)

对于ClickHouse而言,这不是一项非常适合的任务,但是您可以利用以下事实:不会将数组拆分为两行,而是使用自定义函数来处理该数组。

这个想法很简单。首先使用groupArrayForEach将三个数组组装成一个数组。所以

[0.21, 0.21, 0.21, 0.21] as a,
[0.31, 0.31, 0.31, 0.31] as b,
[0.48, 0.48, 0.48, 0.48] as c 

成为

[[0.21, 0.31, 0.48], [0.21, 0.31, 0.48], [0.21, 0.31, 0.48]]

然后创建一个类似Carry Forward Rounding的arrayReduce函数。看看如何在https://github.com/yandex/ClickHouse/blob/master/dbms/src/Functions/arrayReduce.cpp#L169

中实现arrayReduce

实际上,您不需要聚合器,一个简单的循环就足够了。

如果ClickHouse支持有状态的lambda,那会更好。我希望这样的事情

select
arrayCum
(
arr, old_carry =>
    with
     arrayMap(x, y -> x + y, arr, old_carry) as arr,
     arrayEnumerate(arr) as idx,
     arrayReduce('max', arr) as m
     arrayFirstIndex(e -> e = m, arr) as i,
     arrayMap(j -> if(i = j, 1, 0), idx) as rounded,
     arrayMap(x, y -> x - y, arr, rounded) as carry
     --
     rounded, carry
     , arr, arrayMap(x -> 0, arr)
)
from
( select groupArrayForEach(a) arr from data )