Получите скользящее среднее за период времени в PostgreSQL с противоречивыми данными - PullRequest
0 голосов
/ 26 апреля 2018

У меня есть таблица с именем answers со столбцами created_at и response, ответом является целое число 0 (для «нет»), 1 (для «да») или 2 ( для «не знаю»). Я хочу получить скользящее среднее для значений ответов, отсеивая 2 секунды для каждого дня, принимая во внимание только предыдущие 30 дней. Я знаю, что вы можете сделать ROWS BETWEEN 29 AND PRECEDING AND CURRENT ROW, но это работает, только если у вас есть данные за каждый день, и в моем случае может не быть данных за неделю или более.

Мой текущий запрос:

SELECT answers.created_at, answers.response,
    AVG(answers.response)
      OVER(ORDER BY answers.created_at::date ROWS 
        BETWEEN 29 PRECEDING AND CURRENT ROW) AS rolling_average
  FROM answers
  WHERE answers.user_id = 'insert_user_id'' 
    AND (answers.response = 0 OR answers.response = 1)
  GROUP BY answers.created_at, answers.response
  ORDER BY answers.created_at::date

Но это вернет среднее значение, основанное на предыдущих строках, если пользователь ответил 1 на 2018-3-30 и 0 на 2018-5-15, скользящее среднее на 2018-5-15 было бы 0.5 вместо 0 как я хочу. Как я могу создать запрос, который будет принимать во внимание только те ответы, которые были созданы за последние 30 дней для скользящего среднего?

Ответы [ 2 ]

0 голосов
/ 15 февраля 2019

С Postgres 11 вы можете сделать это:

SELECT created_at, 
       response,
       AVG(response) OVER (ORDER BY created_at 
                           RANGE BETWEEN '29 day' PRECEDING AND current row) AS rolling_average 
FROM answers
WHERE user_id = 1
  AND response in (0,1)
ORDER BY created_at;
0 голосов
/ 27 апреля 2018

Попробуйте что-то вроде этого:

SELECT * FROM ( SELECT d.created_at, d.response, Avg(d.response) OVER(ORDER BY d.created_at::date rows BETWEEN 29 PRECEDING AND CURRENT row) AS rolling_average FROM ( SELECT COALESCE(a.created_at, d.dates) AS created_at, response, a.user_id FROM (SELECT generate_series('2018-01-01'::date, '2018-05-31'::date, '1day'::interval)::date AS dates) d LEFT JOIN (SELECT * FROM answers WHERE answers.user_id = 'insert_user_id' AND ( answers.response = 0 OR answers.response = 1)) a ON d.dates = a.created_at::date ) d GROUP BY d.created_at, d.response ) agg WHERE agg.response IS NOT NULL ORDER BY agg.created_at::date

  • generate_series создает список дней - вы должны установить разумные границы
  • этот список дней оставлен с предварительно выбранными ответами
  • этот результат используется для расчета скользящего среднего
  • после этого я выбираю только записи с ответом и получаю:

created_at | response | rolling_averagte 2018-03-30 | 1 | 1.00000000000000000000 2018-05-15 | 0 | 0.00000000000000000000

...