2012-01-15 13 views
15

Postgre'lerde üstel hareketli bir ortalama (EMA) uygulamaya çalışıyorum, ancak belgelere bakıp bunu düşündüğümde daha fazla kafa karıştırmaya çalışıyorum. EMA(x) içinPostgre'lerde üstel hareketli ortalama nasıl hesaplanır?

formüldür:

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

Burada yapılması gerektiğini tam olarak ne son hesaplanan elemanın sonucunu tutarak bir toplayıcı için mükemmel gibi görünüyor. Ancak bir toplayıcı tek bir sonuç üretir (küçülme ya da katlama gibi) ve burada sonuçların bir listesini (sütun olarak) (harita olarak) ihtiyacımız var. Prosedürlerin ve fonksiyonların nasıl çalıştığını kontrol ettim, fakat AFAIK bir sütun değil, tek bir çıktı ürettiler. Çok sayıda prosedür ve fonksiyon gördüm, ama bu, özellikle böyle bir şey yaparken, bir EMA yaparken, ilişkisel cebir ile nasıl etkileşime girdiğini anlayamıyorum.

Internets'i şu ana kadar aramakta şansım olmadı. Fakat bir EMA'nın tanımı oldukça basittir, umarım bu tanımı postgreslerde çalışan bir şeye çevirmek mümkündür ve basit ve etkilidir, çünkü NoSQL'e geçmek benim bağlamımda aşırı olacaktır.

Teşekkür ederiz.

PD: burada bir örnek görebilirsiniz:
sorgunun Bu tür özyinelemeli CTE ile çözülebilir https://docs.google.com/spreadsheet/ccc?key=0AvfclSzBscS6dDJCNWlrT3NYdDJxbkh3cGJ2S2V0cVE

+0

Mesaj örnek tablo kaynağı beklenen bir sonuç: Eğer onlardan birine adıyla parametreleri başvuramaz rağmen, sadece bir sql fonksiyonu olabilir. Bu yardımcı olabilir. – danihp

cevap

14

ve sonra her bir aşamada toplanmış çıktıyı tek bir değer yerine elde etmek için bir pencere belirtimiyle kullanın.

Bu nedenle, bir grup, her satır için bu durumu ve isteğe bağlı olarak durumu bir çıkış değerine dönüştürmek için bir sonlandırma işlevini değiştirmek üzere bir dönüşüm durumu ve bir dönüşüm işlevidir. Böyle basit bir durum için, sadece bir dönüşüm işlevi yeterli olmalıdır. bana verir

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 

Bu numaralar soruya eklenen elektronik tabloya maç gibi görünüyor.

Ayrıca, bildirgemden bir parametre olarak alfa geçmesine işlevini tanımlayabilirsiniz:

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 

Ayrıca, bu işlev aslında hiç plpgsql olması gerekmez, böylece basit, ama

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 Ayrıntılı olarak olmasa da aklımda böyle bir şey vardı. –

+0

Bu, girdi verilerinin her alt listesi için her satırda sonucu üreten kümelenmeyi hesaplıyor mu? Çünkü, toplayıcıyı n numaralı satıra kadar kullanıyor, sonucu döndürüyor ve ardından n + 1 satırına kadar olan kümelenmeyi yeniden hesaplamak için satır 0'a gidiyor. Biriktirme veya bazı statik değişkenleri (C cinsinden) kullanmanın bir yolu var mıdır? Teşekkür ederiz. – Trylks

+0

Hayır, birikmiş değeri kullanıyor. Sorguyu "bilgi yükselt" komutuyla birlikte çalıştırmazsanız, işlevin yalnızca her satır çıkışı için bir kez çağrıldığını görebilirsiniz. Postgresql, her satırdaki durum değerini verir (bir finalfunc tanımlıysa, durumu bir çıkış değerine dönüştürmek için çağrılırdı). – araqnid

1

- deneyin: Kendi toplama işlevini tanımlayabilirsiniz

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

Bazı küçük düzeltmeleri uygulama özgürlüğünü aldım. Lider '' '- kaldırıldı, tSQL’de gerekli ancak PostgreSQL’de değil. Biçimlendirilmiş kod. JOIN koşulunu iyileştirdi. CTE'den bir değerin arttırılması, tüm değerleri tablodan düşürmekten daha hızlıdır. (Pg 9.0'da doğrulamak için hızlı bir test yaptım.) –

+0

Çapraz birleştirmeye dayalı herhangi bir çözüm küçükten büyük veri kümeleri için çok yavaş olacaktır. Bunun yerine pencere işlevini kullanın. –

+0

@PavelStehule: Çapraz birleştirilen tablonun yalnızca bir (sanal) kaydı olacak - tek amacı alfa parametresi olarak kabul etmektir. –

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; 
İlgili konular