Обнаружение изменений значений за порогом данных временных рядов в SQL - PullRequest
1 голос
/ 23 марта 2019

В PostgreSQL я пытаюсь найти субъектов, которые имеют последовательность значений ниже 60, за которой следуют два последовательных значения выше 60, которые появляются позже. Меня также интересует промежуток времени между первым записанным значением ниже 60 и вторым значением выше 60. Это событие может происходить несколько раз для каждого субъекта.

Я изо всех сил пытаюсь выяснить, как искать неограниченное количество значений <60, за которыми следуют 2 значения> = 60.

RowID    SubjectID    Value    TimeStamp    
1           1          65   2142-04-29 12:00:00 
2           1          58   2142-04-30 03:00:00 
3           1          55   2142-04-30 04:00:00
4           1          54   2142-04-30 05:00:00
5           1          55   2142-04-30 06:15:00  
6           1          56   2142-04-30 06:45:00
7           1          65   2142-04-30 07:00:00 
8           1          65   2142-04-30 08:00:00
9           2          48   2142-05-04 03:30:00 
10          2          48   2142-05-04 04:00:00
11          2          50   2142-05-04 05:00:00
12          2          69   2142-05-04 06:00:00
13          2          68   2142-05-04 07:00:00
14          2          69   2142-05-04 08:00:00
15          2          50   2142-05-04 09:00:00
16          2          55   2142-05-04 10:00:00
17          2          50   2142-05-04 10:30:00
18          2          67   2142-05-04 11:00:00
19          2          67   2142-05-04 12:00:00

Моя текущая попытка использует функции задержки и опережения, но я не уверен в том, как использовать эти функции, когда не уверен, насколько далеко мне нужно смотреть в будущее. Это пример того, как смотреть вперед на одно значение и за одно значение. Моя проблема в том, что я не знаю, как разделить на subjectID, чтобы посмотреть «t» моменты времени вперед, где «t» может быть разным для каждого предмета.

select t.subjectId, t.didEventOccur,
   (next_timestamp - timestamp) as duration
from (select t.*,
         lag(t.value) over (partition by t.subjectid order by t.timestamp) 
as prev_value,
         lead(t.value) over (partition by t.subjectid order by 
t.timestamp) as next_value,
         lead(t.timestamp) over (partition by t.subjectid order by 
 t.timestamp) as next_timestamp
  from t
 ) t
where value < 60 and next_value < 60 and
  (prev_value is null or prev_value >= 60);

Я надеюсь получить вывод, такой как:

SubjectID  DidEventOccur Duration 
 1          1             05:00:00
 2          1             03:30:00
 2          1             03:00:00

1 Ответ

0 голосов
/ 23 марта 2019

Чистое SQL решение, о котором вы просили:

SELECT subjectid, start_at, next_end_at - start_at AS duration
FROM  (
   SELECT *
        , lead(end_at) OVER (PARTITION BY subjectid ORDER BY start_at) AS next_end_at
   FROM  (
      SELECT subjectid, grp, big
           , min(ts) AS start_at
           , max(ts) FILTER (WHERE big AND big_rn = 2) AS end_at  -- 2nd timestamp
      FROM  (
         SELECT subjectid, ts, grp, big
              , row_number() OVER (PARTITION BY subjectid, grp, big ORDER BY ts) AS big_rn
         FROM  (
            SELECT subjectid, ts
                 , row_number() OVER (PARTITION BY subjectid ORDER BY ts)
                 - row_number() OVER (PARTITION BY subjectid, (value > 60) ORDER BY ts) AS grp
                 , (value > 60) AS big
            FROM   tbl
            ) sub1
         ) sub2
      GROUP  BY subjectid, grp, big
      ) sub3
   ) sub4
WHERE  NOT big                 -- identifies block of values <= 60 ...
AND    next_end_at IS NOT NULL -- ...followed by at least 2 values > 60
ORDER  BY subjectid, start_at;

Я опустил бесполезный столбец DidEventOccur и добавил вместо него start_at. В противном случае точно желаемый результат.

дБ <> скрипка здесь

Рассмотрим процедурное решение в plpgsql (или любом PL) вместо этого, должно быть быстрее. Simpler? Я бы сказал, да, но это зависит от того, кто судит. См. ( с объяснением для техники и ссылки на более):

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