[英]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
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.