简体   繁体   English

如何使用 Postgres SQL 计算平滑移动平均线

[英]How to calculate smoothed moving average using Postgres SQL

I have a table in my Postgresql DB that has the fields: product_id, date, sales_amount .我的 Postgresql 数据库中有一个表,其中包含以下字段: product_id, date, sales_amount I am calculating a simple moving average for the last 1 week using the below SQL我正在使用以下 SQL 计算过去 1 周的简单移动平均线

SELECT date,  
       AVG(amount)
       OVER(PARTITION BY product_id ORDER BY date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS avg_amount
FROM sales 

How can I calculate a smoothed moving average(smma) instead the simple moving average above?如何计算平滑移动平均线(smma)而不是上面的简单移动平均线? I have found that the formula is smma_today = smma_yesterday * (lookback_period - 1) + amount) / lookback_period我发现公式是smma_today = smma_yesterday * (lookback_period - 1) + amount) / lookback_period
but how to translate to SQL?但是如何翻译成SQL? A CTE or function or query approach suggestion will be appreciated CTE 或 function 或查询方法建议将不胜感激

I am pretty sure you will need recursion since your formula depends on using a value calculated for a previous row in the current row.我很确定您将需要递归,因为您的公式取决于使用为当前行中的前一行计算的值。

with recursive mma as (
  (select distinct on (product_id) *, ddate as basedate, 
          amount as sm_mov_avg
     from smma
    order by product_id, ddate)
  union all
  select smma.*, mma.basedate,
         (  mma.sm_mov_avg
          * least(smma.ddate - mma.basedate, 6)
          + smma.amount) / least(smma.ddate - mma.basedate + 1, 7)
    from mma
         join smma on smma.product_id = mma.product_id
          and smma.ddate = mma.ddate + 1
)
select ddate, product_id, amount, round(sm_mov_avg, 2) as sm_mov_avg,
       round(
         avg(amount) over (partition by product_id
                               order by ddate
                        rows between 6 preceding
                                 and current row), 2) as mov_avg
  from mma;

Please note how the smooth moving average and the moving average begin to diverge after you reach the lookback of seven days:请注意平滑移动平均线和移动平均线在您达到 7 天的回溯后是如何开始背离的:

 ddate      | product_id | amount | sm_mov_avg | mov_avg
 :--------- | ---------: | -----: | ---------: | ------:
 2020-11-01 |          1 |      8 |       8.00 |    8.00
 2020-11-02 |          1 |      4 |       6.00 |    6.00
 2020-11-03 |          1 |      7 |       6.33 |    6.33
 2020-11-04 |          1 |      9 |       7.00 |    7.00
 2020-11-05 |          1 |      4 |       6.40 |    6.40
 2020-11-06 |          1 |      6 |       6.33 |    6.33
 2020-11-07 |          1 |      4 |       6.00 |    6.00
 2020-11-08 |          1 |      1 |       5.29 |    5.00
 2020-11-09 |          1 |      8 |       5.67 |    5.57
 2020-11-10 |          1 |     10 |       6.29 |    6.00
 2020-11-11 |          1 |      8 |       6.54 |    5.86
 2020-11-12 |          1 |      4 |       6.17 |    5.86
 2020-11-13 |          1 |      3 |       5.72 |    5.43
 2020-11-14 |          1 |      2 |       5.19 |    5.14
 2020-11-15 |          1 |      5 |       5.16 |    5.71
 2020-11-16 |          1 |      8 |       5.57 |    5.71
 2020-11-17 |          1 |      4 |       5.34 |    4.86
 2020-11-18 |          1 |     10 |       6.01 |    5.14
 2020-11-19 |          1 |      5 |       5.86 |    5.29
 2020-11-20 |          1 |      3 |       5.46 |    5.29
 2020-11-21 |          1 |      3 |       5.10 |    5.43
 2020-11-22 |          1 |      9 |       5.66 |    6.00
 2020-11-23 |          1 |      7 |       5.85 |    5.86
 2020-11-24 |          1 |      1 |       5.16 |    5.43
 2020-11-25 |          1 |     10 |       5.85 |    5.43
 2020-11-26 |          1 |      7 |       6.01 |    5.71
 2020-11-27 |          1 |      8 |       6.30 |    6.43
 2020-11-28 |          1 |      8 |       6.54 |    7.14
 2020-11-29 |          1 |      1 |       5.75 |    6.00
 2020-11-30 |          1 |      9 |       6.21 |    6.29

Working Fiddle 工作小提琴

Many thanks for Mike Organek's answer that showed the way forward for the calculation was a recursive approach on the query.非常感谢Mike Organek 的回答,该回答显示了计算的前进方向是查询的递归方法。 I am starting off with the simple moving average at some distant point in the past and thereafter using the smoothed average daily which has given us exactly what we needed我从过去某个遥远点的简单移动平均线开始,然后每天使用平滑平均线,这正是我们所需要的

with recursive my_table_with_rn as 
(
SELECT 
    product_id,
    amount,
    sale_date,
    row_number() over (partition by product_id order by sale_date) as rn
FROM sale
where 1=1
    and sale_date > '01-Jan-2018'
    order by sale_date
),
rec_query(rn, product_id, amount, sale_date, smma) as 
(
    SELECT 
        rn,
        product_id,
        amount,
        sale_date,
        AVG(amount)  OVER(PARTITION BY product_id ORDER BY date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS smma --first entry is a simple moving average to start off
        from my_table_with_rn 
        where rn = 1
    union all
    select 
        t.rn, t.product_id, t.amount, t.sale_date, 
        (p.smma * (7 - 1) + amount) / 7 -- 7 is the lookback_period; formula is smma_today = smma_previous * (lookback_period - 1) + amount) / lookback_period
    from rec_query p
    join my_table_with_rn t on 
    (t.rn = p.rn + 1 AND t.product_id = p.product_id)
)   
SELECT * FROM rec_query

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM