我试图在postgres上实现指数移动平均线(EMA),但是当我检查文档并考虑它时,我越是感到困惑.
EMA(x)的公式为:
EMA(x1) = x1 EMA(xn) = α * xn + (1 - α) * EMA(xn-1)
对于聚合器来说似乎是完美的,保持最后计算的元素的结果正是这里必须完成的.然而,聚合器产生一个单个结果(作为缩减或折叠),这里我们需要一个结果列表(一列)(如map).我一直在检查程序和功能的工作原理,但是AFAIK他们生产一个单一的输出,而不是列.我已经看到了很多程序和功能,但是我不能真正弄清楚它与关系代数的相互作用,特别是当这样做时,EMA.
到目前为止,我没有运气搜索互联网.但是EMA的定义非常简单,希望将这个定义转化为postgres中有用的东西,而且简单高效,因为在我的上下文中移动到Nosql会变得过大.
谢谢.
PD:在这里你可以看到一个例子:
https://docs.google.com/spreadsheet/ccc?key=0AvfclSzBscS6dDJCNWlrT3NYdDJxbkh3cGJ2S2V0cVE
解决方法
您可以定义自己的聚合函数,然后使用窗口规范来获取每个阶段的聚合输出,而不是单个值.
所以一个聚合是一个状态,一个变换函数来修改每个行的状态,以及可选的一个完成函数来将状态转换为一个输出值.对于这样一个简单的例子,只需要一个变换函数即可.
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),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,0.5 /* alpha */) over (order by n asc) from data
另外,这个函数实际上是这么简单的,它根本不需要在plpgsql中,但是可以只是一个sql函数,尽管你不能通过名称引用参数:
create or replace function ema_func(state numeric,alpha numeric) returns numeric language sql as $$ select case when $1 is null then $2 else $3 * $2 + (1-$3) * $1 end $$;