Как рассчитать экспоненциальную скользящую среднюю на postgres? - PullRequest
17 голосов
/ 15 января 2012

Я пытаюсь реализовать экспоненциальную скользящую среднюю (EMA) на postgres, но когда я проверяю документацию и думаю об этом, чем больше я пытаюсь, тем больше я запутываюсь.

Формула для EMA(x):

EMA(x<sub>1</sub>) = x<sub>1</sub>
EMA(x<sub>n</sub>) = &alpha; * x<sub>n</sub> + (1 - &alpha;) * EMA(x<sub>n-1</sub>)

Кажется, это идеально для агрегатора, поэтому сохранение результата последнего вычисленного элемента - это именно то, что нужно сделать здесь. Однако агрегатор выдает один единственный результат (как уменьшить или сложить), и здесь нам нужен список (столбец) результатов (как карта). Я проверял, как работают процедуры и функции, но AFAIK они производят один единственный вывод, а не столбец. Я видел множество процедур и функций, но я не могу понять, как это взаимодействует с реляционной алгеброй, особенно при выполнении чего-то подобного, EMA.

Мне не повезло, что я искал Интернет. Но определение для EMA довольно простое, я надеюсь, что можно перевести это определение во что-то, что работает в postgres и является простым и эффективным, потому что переход на NoSQL будет чрезмерным в моем контексте.

Спасибо.

PD: здесь вы можете увидеть пример:
https://docs.google.com/spreadsheet/ccc?key=0AvfclSzBscS6dDJCNWlrT3NYdDJxbkh3cGJ2S2V0cVE

Ответы [ 3 ]

19 голосов
/ 16 января 2012

Вы можете определить свою собственную функцию агрегирования, а затем использовать ее со спецификацией окна, чтобы получить агрегированный вывод на каждом этапе, а не одно значение.

Таким образом, агрегат представляет собой часть состояния и функцию преобразования для изменения этого состояния для каждой строки, а также, при необходимости, функцию финализации для преобразования состояния в выходное значение. Для такого простого случая, как этот, достаточно просто использовать функцию преобразования.

create function ema_func(numeric, numeric) returns numeric
  language plpgsql as $$
declare
  alpha numeric := 0.5;
begin
  -- uncomment the following line to see what the parameters mean
  -- raise info 'ema_func: % %', $1, $2;
  return case
              when $1 is null then $2
              else alpha * $2 + (1 - alpha) * $1
         end;
end
$$;
create aggregate ema(basetype = numeric, sfunc = ema_func, stype = numeric);

, что дает мне:

steve@steve@[local] =# select x, ema(x, 0.1) over(w), ema(x, 0.2) over(w) from data window w as (order by n asc) limit 5;
     x     |      ema      |      ema      
-----------+---------------+---------------
 44.988564 |     44.988564 |     44.988564
   39.5634 |    44.4460476 |    43.9035312
 38.605724 |   43.86201524 |   42.84396976
 38.209646 |  43.296778316 |  41.917105008
 44.541264 | 43.4212268844 | 42.4419368064

Кажется, что эти цифры соответствуют таблице, которую вы добавили к вопросу.

Кроме того, вы можете определить функцию для передачи альфы в качестве параметра из оператора:

create or replace function ema_func(state numeric, inval numeric, alpha numeric)
  returns numeric
  language plpgsql as $$
begin
  return case
         when state is null then inval
         else alpha * inval + (1-alpha) * state
         end;
end
$$;

create aggregate ema(numeric, numeric) (sfunc = ema_func, stype = numeric);

select x, ema(x, 0.5 /* alpha */) over (order by n asc) from data

Кроме того, эта функция на самом деле настолько проста, что вообще не должна быть в plpgsql, но может быть просто функцией sql, хотя вы не можете ссылаться на параметры по имени в одном из них:

create or replace function ema_func(state numeric, inval numeric, alpha numeric)
  returns numeric
  language sql as $$
select case
       when $1 is null then $2
       else $3 * $2 + (1-$3) * $1
       end
$$;
1 голос
/ 21 ноября 2016
--$1 Stock code
--$2 exponential;
create or replace function fn_ema(text,numeric)
    returns numeric as
    $body$
    declare
        alpha numeric := 0.5;
        var_r record;
        result numeric:=0;
        n int;
        p1 numeric;
    begin
        alpha=2/(1+$2);
        n=0;
        for var_r in(select *
        from stock_old_invest
        where code=$1  order by stock_time desc)
        loop
            if n>0 then
                result=result+(1-alpha)^n*var_r.price_now;
            else 
                p1=var_r.price_now;
            end if;
            n=n+1;
        end loop;
        result=alpha*(result+p1);
        return result;
    end
    $body$
    language plpgsql volatile
    cost 100;
    alter function fn_ema(text,numeric)
    owner to postgres;
1 голос
/ 15 января 2012

Этот тип запроса может быть решен с помощью рекурсивного CTE - попробуйте:

with recursive cte as (
select n, x ema from my_table where n = 1
union all
select m.n, alpha * m.x + (1 - alpha) * cte.ema
from cte
join my_table m on cte.n = m.n - 1
cross join (select ? alpha) a)
select * from cte;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...