2012-01-15 15 views
15

私はポストグルに指数移動平均(EMA)を実装しようとしていますが、ドキュメントをチェックして考えてみると、もっと混乱していきます。 EMA(x)ためポストグルの指数移動平均を計算するには?

式は次のとおりです。

EMA(x1) = x1 
EMA(xn) = α * xn + (1 - α) * EMA(xn-1) 

ここで行う必要があります正確に何である最後に計算要素の結果を保ち、アグリゲータに最適であると思われます。しかし、アグリゲータは1つの結果(reduceまたはfoldとして)を生成します。ここでは、結果のリスト(カラム)が必要です(マップとして)。プロシージャとファンクションの動作を確認していますが、AFAIKは列ではなく1つの出力を生成します。私は多くの手続きと関数を見てきましたが、これが関係代数とどのようにやり取りするのか、実際にはこのようなことをするとき、EMAということを実際には理解できません。

私はこれまでインターネットを検索していませんでした。しかし、EMAの定義は非常に単純です。この定義をポストグルで動作するものに変換することが可能であり、単純で効率的なものになることを願っています。なぜなら、NoSQLへの移行は私の状況では過度になるためです。

ありがとうございます。

PD:ここにあなたが例を見ることができます:
https://docs.google.com/spreadsheet/ccc?key=0AvfclSzBscS6dDJCNWlrT3NYdDJxbkh3cGJ2S2V0cVE

+0

ポストサンプル・テーブル・ソース期待される結果:あなたはそれらのいずれかの名前でパラメータを参照することはできませんが、ただ、SQL関数とすることができます。これは助けることができます。 – danihp

答えて

14

あなたがあなた自身の集約関数を定義することができ、その後、単一の値ではなく、各ステージで集計出力を取得するためにウィンドウ仕様で使用します。

したがって、集合体は状態の一部であり、各行の状態を変更する変換関数と、オプションで状態を出力値に変換するためのファイナライズ関数です。このような単純なケースでは、変換関数で十分です。私を与え

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); 

は:

[email protected]@[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がでする必要がないようにシンプルですが、

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 
$$; 
+0

+1私はそのようなことを念頭に置いていました。 –

+0

これは、入力データのすべてのサブリストに対してすべての行で結果を生成する集計を計算していますか?それは、行nまでアグリゲーターを使用しているように見えるので、結果を戻し、行0に戻って、行n + 1までの集計を再度計算します。 累積または静的変数を使用する方法はありますか(Cのように)、これを一度計算する必要がありますか? ありがとうございます。 – Trylks

+0

いいえ、累積された値を使用しています。 "raise info"コマンドのコメントを外してクエリを実行すると、その関数が各行出力に対して1回だけ呼び出されることがわかります。 Postgresqlは各行に状態値を出力します(finalfuncが定義されていれば、状態を出力値に変換するために呼び出されます)。 – araqnid

1

このタイプのクエリは、再帰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; 
+1

私はいくつかの小さな修正を適用する自由を取った。先頭の ';'を削除しました。これはtSQLでは必須ですが、PostgreSQLでは必要ありません。書式付きコード。 JOIN条件を改善しました。 CTEから1つの値をインクリメントする方が、テーブルからすべての値を減らすよりも高速です。 (私は実際にpg 9.0で検証するための簡単なテストを実行しました) –

+0

クロス結合に基づくソリューションは、データセットが小さすぎると処理が遅すぎます。代わりにウィンドウ関数を使用してください。 –

+0

@PavelStehule:クロスジョイントされたテーブルは、その中に(仮想)レコードが1つしかありません。唯一の目的はアルファをパラメータとして受け入れることです。 –

0
--$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;