2012-01-15 17 views
15

Sto cercando di implementare una media mobile esponenziale (EMA) su postgres, ma mentre controllo la documentazione e ci penso più provo più sono confuso.Come calcolare una media mobile esponenziale su postgres?

La formula per EMA(x) è:

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

Sembra essere perfetto per un aggregatore, mantenendo il risultato dell'ultimo elemento calcolato è esattamente ciò che deve essere fatto qui. Tuttavia un aggregatore produce un singolo risultato (come riduci o piega) e qui abbiamo bisogno di una lista (una colonna) di risultati (come mappa). Ho controllato come funzionano le procedure e le funzioni, ma AFAIK produce un singolo output, non una colonna. Ho visto molte procedure e funzioni, ma non riesco a capire come interagisca con l'algebra relazionale, specialmente quando si fa qualcosa del genere, un EMA.

Non ho avuto fortuna a cercare gli Internet finora. Ma la definizione di un EMA è abbastanza semplice, spero che sia possibile tradurre questa definizione in qualcosa che funziona in postgres ed è semplice ed efficiente, perché il passaggio a NoSQL sarà eccessivo nel mio contesto.

Grazie.

PD: qui potete vedere un esempio:
https://docs.google.com/spreadsheet/ccc?key=0AvfclSzBscS6dDJCNWlrT3NYdDJxbkh3cGJ2S2V0cVE

+0

Pubblica fonte tabella di esempio di un risultato atteso. Questo può aiutare. – danihp

risposta

14

È possibile definire la propria funzione di aggregazione e quindi usalo con una specifica della finestra per ottenere l'output aggregato in ogni fase piuttosto che un singolo valore.

Quindi un aggregato è un pezzo di stato e una funzione di trasformazione per modificare quello stato per ogni riga e facoltativamente una funzione di finalizzazione per convertire lo stato in un valore di uscita. Per un caso semplice come questo, solo una funzione di trasformazione dovrebbe essere sufficiente.

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

che mi dà:

[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 

Questi numeri sembrano corrispondere al foglio di calcolo si è aggiunto alla domanda.

Inoltre, è possibile definire la funzione di passare alfa come parametro dalla dichiarazione:

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 

Inoltre, questa funzione è in realtà così semplice che non ha bisogno di essere in plpgsql a tutti, ma può essere solo una funzione SQL, anche se non è possibile fare riferimento ai parametri per nome in una di quelle:

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 Avevo qualcosa in mente, anche se non così dettagliato. –

+0

Si sta calcolando l'aggregazione che produce il risultato in ogni riga per ogni sottolista dei dati di input? Perché sembra che utilizzi l'aggregatore fino alla riga n, restituendo il risultato e quindi andando alla riga 0 per calcolare nuovamente l'aggregazione fino alla riga n + 1. C'è un modo per usare l'accumulo o qualche variabile statica (come in C) in modo che questo debba essere calcolato una volta? Grazie. – Trylks

+0

No, sta usando il valore accumulato. Se esegui la query con il comando "sollevare informazioni" non commentato, sarai in grado di vedere che la funzione viene richiamata una sola volta per ogni output di riga. Postgresql restituisce il valore di stato su ogni riga (se è stato definito un finalfunc, che verrebbe chiamato per trasformare lo stato in un valore di uscita). – araqnid

1

Questo tipo di query può essere risolto con una CTE ricorsiva - Prova:

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

Mi sono preso la libertà di applicare alcune correzioni minori. Rimosso il comando principale ';' - richiesto in tSQL ma non in PostgreSQL. Codice formattato Migliorata la condizione JOIN. L'incremento di un valore dal CTE è più rapido del decremento di tutti i valori dalla tabella. (In realtà ho eseguito un test rapido per verificare su pg 9.0.) –

+0

Qualsiasi soluzione basata su cross join sarà troppo lenta per set di dati più grandi di quelli piccoli. Usa invece la funzione finestra. –

+0

@PavelStehule: la tabella incrociata avrà solo un record (virtuale) al suo interno - il suo unico scopo è accettare l'alfa come parametro. –

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; 
Problemi correlati