2015-01-26 17 views
5

C'è un modo per calcolare una media mobile ponderata con una dimensione fissa della finestra in Amazon Redshift? Più in dettaglio, data una tabella con una colonna data e una colonna valore, per ogni data calcolare il valore medio ponderato su una finestra di una dimensione specificata, con pesi specificati in una tabella ausiliaria.Media mobile ponderata in Amazon Redshift

I miei tentativi di ricerca finora hanno fornito molti esempi per fare ciò con le funzioni di finestra per media semplice (senza pesi), ad esempio here. Esistono anche alcuni suggerimenti correlati per postgres, ad esempio this SO question, tuttavia il set di funzionalità di Redshift è piuttosto scarso rispetto a Postgres e non supporta molte delle funzionalità avanzate suggerite.

+0

hai riusciti a trovare una soluzione nel frattempo? – thpl

risposta

0

Supponendo che abbiamo le seguenti tabelle:

create temporary table _data (ref_date date, value int); 
insert into _data values 
    ('2016-01-01', 34) 
    , ('2016-01-02', 12) 
    , ('2016-01-03', 25) 
    , ('2016-01-04', 17) 
    , ('2016-01-05', 22) 
; 

create temporary table _weight (days_in_past int, weight int); 
insert into _weight values 
    (0, 4) 
    , (1, 2) 
    , (2, 1) 
; 

Poi, se vogliamo calcolare una media mobile su una finestra di tre giorni (compresa la data corrente) in cui i valori più vicini alla data attuale sono assegnato un peso maggiore di quelli più in passato, ci aspettiamo per la media ponderata per 2016-01-05 (basata su valori da 2016-01-05, 2016-01-04 e 2016-01-03):

(22*4 + 17*2 + 25*1)/(4+2+1) = 147/7 = 21 

e la query potrebbe apparire come segue:

with _prepare_window as (
    select 
     t1.ref_date 
     , datediff(day, t2.ref_date, t1.ref_date) as days_in_past 
     , t2.value * weight as weighted_value 
     , weight 
     , count(t2.ref_date) over(partition by t1.ref_date rows between unbounded preceding and unbounded following) as num_values_in_window 
    from 
     _data t1 
    left join 
     _data t2 on datediff(day, t2.ref_date, t1.ref_date) between 0 and 2 
    left join 
     _weight on datediff(day, t2.ref_date, t1.ref_date) = days_in_past 
    order by 
     t1.ref_date 
     , datediff(day, t2.ref_date, t1.ref_date) 
) 
select 
    ref_date 
    , round(sum(weighted_value)::float/sum(weight), 0) as weighted_average 
from 
    _prepare_window 
where 
    num_values_in_window = 3 
group by 
    ref_date 
order by 
    ref_date 
; 

Dare il risultato:

ref_date | weighted_average 
------------+------------------ 
2016-01-03 |    23 
2016-01-04 |    19 
2016-01-05 |    21 
(3 rows) 
+0

Molto bello. Lo proverò. –