Продолжить округление

У меня есть множество массивов

    [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 (в результате значение переноса будет равно 0.21, 0.31 и 0.48 для следующего набора значений то есть a2, b2 и c2.

Шаг 2. Однако, поскольку после шага 1 раунд (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

Шаг 3: Повторите Шаг 1 для 2-х элементов, которые в этом случае станут круглыми (a2) = круглыми (0,42) =0, круглыми (b2) = круглыми (0,62)=1, круглыми (c2)= круглыми (-0,04) = 0

Переносим разницу, от 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 ответ

Это не очень хорошая задача для 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]]

Затем создайте функцию типа arrayReduce, которая Carry Forward Rounding, Посмотрите, как реализован arrayReduce в https://github.com/yandex/ClickHouse/blob/master/dbms/src/Functions/arrayReduce.cpp#L169

На самом деле вам не нужен агрегатор, достаточно простого цикла.

Было бы лучше, если бы ClickHouse поддерживал лямбды с сохранением состояния. Я ожидал что-то подобное

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 )
Другие вопросы по тегам