Есть ли простой способ рассчитать скользящее среднее за 12 месяцев в PostgreSQL? - PullRequest
0 голосов
/ 24 октября 2019

Этот очень простой SQL может вычислять средние, медианы и т. Д. Для четко определенных периодов, таких как год, месяц, квартал, неделя, день:

SELECT
  date_trunc('year', t.time2), -- or hour, day, week, month, year
  count(1), 
  percentile_cont(0.25) within group (order by t.price) as Q1,
  percentile_cont(0.5) within group (order by t.price) as Q2,
  percentile_cont(0.75) within group (order by t.price) as Q3,
  avg(t.price) as A,
  min(t.price) as Mi,
  max(t.price) as Mx

FROM my_table AS t
GROUP BY 1
ORDER BY date_trunc

Таблица содержит список отдельных транзакций с датой (отметка времени) и цена (bigint).

Однако я изо всех сил пытаюсь адаптировать ее для расчета текущих / движущихся значений (например, 4 недели, или 6 месяцев, или 2 квартала, или 12 месяцев). Как этого достичь?

РЕДАКТИРОВАТЬ Вот как выглядят данные:

enter image description here

И это ожидаемый результат:

enter image description here

РЕДАКТИРОВАТЬ 2:

Другая проблема, с которой я сталкиваюсь, заключается в том, что должен быть полный набор данных, включенных в перемещение avg, median и т. Д. расчеты.

Я имею в виду, что если ряд данных начинается в январе 2000 года, то первые «12-месячные скользящие средние», которые имеют смысл, могут быть рассчитаны только в декабре 2000 года (т. Е. Первый месяц, который содержит полные 12 месяцев). данных). В случае 3-месячного смещения avg, первое значимое значение будет в марте 2000 года и т. Д.

Итак, я думаю, что логика для этого запроса должна быть:

1) определить дату начала и окончания для использования при вычислении avg, медианы и т. д., затем

2) цикл по вычислениям avg, median и т. д. ДЛЯ КАЖДОЙ пары начальных и конечных дат.

Для иллюстрации, первая часть может быть:

WITH range_values AS ( -- get min and max values for the data series
  SELECT date_trunc('month', min(time2)) as minval,
         date_trunc('month', max(time2)) as maxval
  FROM my_table),
period_range(d) AS ( -- generate complete list of periods eg. weeks, months, years for the data series 
  SELECT generate_series(minval, maxval, '1 month'::interval) as timeint
  FROM range_values
),
lookup_range AS ( -- generate start-end date pairs based on the data series
        select d as enddate, d- interval '11month' as startdate
from period_range
)
SELECT startdate, enddate
from lookup_range, range_values as p
where enddate  >= p.minval + interval '11month'; -- clip date range to calculate 12 months avg using 12 months of data only

Вторая часть может быть (не правильный запрос, а просто для иллюстрации логики):

SELECT
  count(1),
  percentile_cont(0.5) within group (order by t.price) as median_price,
  avg(t.price) as avg_price
FROM my_table as t, lookup_range as l
WHERE t.time2>= 'startdate' AND t.time2 < 'enddate'  

Теперь задача состоит в том, какобъединить два? И как заставить его работать с минимальными строками кода?

Ответы [ 2 ]

2 голосов
/ 24 октября 2019

Я бы сначала агрегировал по месяцам, а затем вычислял скользящее среднее:

SELECT mon,
       sum(s_price) OVER w / sum(c_price) OVER w
FROM (SELECT date_trunc('month', time2::timestamp) AS mon,
             sum(price) AS s_price,
             count(price) AS c_prize
      FROM my_table
      GROUP BY date_trunc('month', time2::timestamp)) AS q
WINDOW w AS (ORDER BY mon
             RANGE BETWEEN '6 months'::interval PRECEDING
                       AND '6 months'::interval FOLLOWING);
0 голосов
/ 04 ноября 2019

Итак, опять же, мне осталось решить головоломку самостоятельно. Интересно, мои вопросы так чертовски сложны или это я?

В любом случае, если кто-то ищет решение для расчета 1,2,3,4, .. 6, ... 12 лет / кварталов / месяцев / недель / дней / часов, скользящего среднего, медианы, процентилейи т. д. сводная статистика за один раз, вот ответ:

WITH grid AS (
      SELECT end_time, start_time
      FROM (

            SELECT end_time
          , lag(end_time, 12, 'infinity') OVER (ORDER BY end_time) AS start_time
            FROM (

                SELECT
                generate_series(date_trunc('month', min(time2))
              , date_trunc('month', max(time2)) + interval '1 month', interval '1 month') AS end_time
                FROM   my_table

                ) sub

           ) sub2

      WHERE end_time > start_time

)

SELECT
    to_char(date_trunc('month',a.end_time - interval '1 month'), 'YYYY-MM') as d
  , count(e.time2)
  , percentile_cont(0.25) within group (order by e.price) as Q1
  , percentile_cont(0.5) within group (order by e.price) as median
  , percentile_cont(0.75) within group (order by e.price) as Q3
  , avg(e.price) as Aver
  , min(e.price) as Mi
  , max(e.price) as Mx

FROM grid a

LEFT JOIN my_table e ON e.time2 >= a.start_time

                   AND e.time2 <  a.end_time

GROUP  BY end_time
ORDER  BY d DESC

Обратите внимание, что таблица содержит список отдельных временных записей (например, транзакции продаж и т. д.), как в примере, представленном вактуальный вопрос.

И этот бит:

to_char(date_trunc('month',a.end_time - interval '1 month'), 'YYYY-MM') as d

предназначен только для отображения. То есть в PostgreSQL принято считать, что «конец месяца» фактически равен «0 часу» следующего месяца (т. Е. Конец октября 2019 года - это «2019.11.01 в 00:00:00»). То же самое относится к любому временному диапазону (например, конец 2019 года фактически равен «2020.01.01 в 00:00:00»). Таким образом, если «- интервал« 1 месяц »» не включен, 12-месячная скользящая статистика, заканчивающаяся октябрем 2019 года, будет отображаться как «за» 1 ноября 2019 года в 00:00:00 (транкинг по 2019-11).

...