2015-01-26 5 views
5

Gibt es eine Möglichkeit, einen gewichteten gleitenden Durchschnitt mit einer festen Fenstergröße in Amazon Redshift zu berechnen? Genauer gesagt wird bei einer Tabelle mit einer Datumsspalte und einer Wertspalte für jeden Zeitpunkt der gewichtete Durchschnittswert über ein Fenster einer bestimmten Größe berechnet, wobei Gewichtungen in einer Hilfstabelle angegeben sind.Gewichtete gleitenden Durchschnitt in Amazon Redshift

Meine bisherigen Suchversuche ergaben viele Beispiele dafür mit Fensterfunktionen für einfachen Durchschnitt (ohne Gewichte), zum Beispiel here. Es gibt auch einige verwandte Vorschläge für Postgres, z. B. this SO question, jedoch ist das Feature-Set von Redshift ziemlich spärlich im Vergleich zu Postgres und es unterstützt nicht viele der erweiterten Funktionen, die vorgeschlagen werden.

+0

haben es geschafft, Sie mittlerweile eine Lösung zu finden? – thpl

Antwort

0

Angenommen, wir haben die folgenden Tabellen:

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

Wenn wir dann einen gleitenden Durchschnitt über ein Fenster von 3 Tage (einschließlich des aktuellen Datums) berechnet werden soll, wo Werte näher an dem aktuellen Datum sind ein höheres Gewicht als diejenigen, die weiter in der Vergangenheit zugewiesen, die wir für 2016-01-05 für den gewichteten Durchschnitt erwarten würden (basierend auf den Werten von 2016-01-05, 2016-01-04 und 2016-01-03):

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

Und die Abfrage wie folgt aussehen könnte:

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 
; 

Geben Sie das Ergebnis:

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

Sehr schön. Ich werde es versuchen. –