Как агрегировать записи, когда они равны или «близки» по нескольким столбцам (временные, непрерывные, категориальные)? - PullRequest
0 голосов
/ 07 июля 2019

Контекст

Я пытаюсь предсказать изменение количества фотографий, сделанных на пляже B i (с 1 <= i <= N с N количеством пляжей), учитывая уровень моря и некоторые особенности специфично для B <sub>i .

Уровень моря изменяется во времени, которое указывается в базе данных двумя временными метками: start_datetime и end_datetime. Это может измениться по 10 + причинам (source_id), которые я хочу принять во внимание, в том числе:

  • ДАТЧИК: датчик, отправляющий новое измерение (может быть относительно случайным, если изменяется уровень моря);
  • ЛОДКА: Лодка только что приблизилась к датчику, увеличив уровень.

Я бы хотел избежать 200 последовательных записей для одного и того же уровня моря, потому что, когда я внутренне присоединяюсь к таблице, содержащей сделанные фотографии (чтобы не было записей 600M + для интервалов, когда у меня нет фотографий) многие из этих коротких записей отбрасываются, тогда как если бы они были агрегированы, они охватывали бы более длительные интервалы и для большинства из них соответствовали сфотографированным фотографиям и, следовательно, не удалялись.

Где я нахожусь на

  • Я думал об использовании функции окна STDDEV, но я не знаю, как выбрать соответствующий динамический размер окна.
  • Я думал о вычислении инкрементного изменения между каждой соседней записью, но я не хочу агрегировать записи, которые идут от 0 до 1000 с шагом 0,1.

Примеры * * 1 027 СЛУЧАЙ 1: ОДИН ИСТОРИЧЕСКИЙ ИСТОЧНИК, НЕПРЕРЫВНЫЙ ВРЕМЯ, КРАСНЫЕ ИЗМЕНЕНИЯ НА УРОВНЕ МОРЯ Я хотел бы объединить небольшие изменения (+ - 1% макс.) Относительно первого значения уровня моря (здесь 1732), смежное по времени ({start_time строки i th }} {end_datetime i-1 th row} + -2 секунды) и из того же source_id. Исходя из этого: | start_datetime | end_datetime | sea_level | source_id | |----------------|---------------|-----------|-----------| | 4/30/19 20:15 | 4/30/19 21:36 | 1731 | SENSOR | | 4/30/19 19:52 | 4/30/19 20:15 | 1734 | SENSOR | | 4/30/19 19:29 | 4/30/19 19:52 | 1731 | SENSOR | | 4/30/19 19:20 | 4/30/19 19:29 | 1732 | SENSOR | | 4/30/19 18:32 | 4/30/19 19:20 | 1734 | SENSOR | | 4/30/19 18:22 | 4/30/19 18:32 | 1732 | SENSOR | | 4/30/19 18:04 | 4/30/19 18:22 | 1734 | SENSOR | | 4/30/19 17:49 | 4/30/19 18:04 | 1731 | SENSOR | | 4/30/19 17:31 | 4/30/19 17:49 | 1734 | SENSOR | | 4/30/19 17:22 | 4/30/19 17:31 | 1732 | SENSOR | | 4/30/19 16:53 | 4/30/19 17:22 | 1734 | SENSOR | | 4/30/19 16:39 | 4/30/19 16:53 | 1731 | SENSOR | | 4/30/19 16:16 | 4/30/19 16:39 | 1734 | SENSOR | | 4/30/19 16:09 | 4/30/19 16:16 | 1731 | SENSOR | | 4/30/19 15:24 | 4/30/19 16:09 | 1734 | SENSOR | | 4/30/19 15:07 | 4/30/19 15:24 | 1732 | SENSOR | 1st value , которые будут объединены в это: | start_datetime | end_datetime | sea_level | source_id | |----------------|---------------|-----------|-----------| | 4/30/19 15:07 | 4/30/19 21:36 | 1732 | SENSOR | 1732 = AVG(AGG RECORDS) СЛУЧАЙ 2: ИЗМЕНЕНИЕ SOURCE_ID Если есть другой source_id, независимо от изменения уровня моря, я хочу сохранить эту разницу, исходя из этого: | start_datetime| end_datetime | sea_level | source_id | |---------------|---------------|-----------|-----------| | 4/20/19 7:26 | 4/20/19 7:32 | 1732 | SENSOR |° | 4/20/19 7:19 | 4/20/19 7:26 | 1734 | SENSOR |° | 4/20/19 7:10 | 4/20/19 7:19 | 1731 | SENSOR |° | 4/20/19 6:47 | 4/20/19 7:10 | 1732 | SENSOR |° | 4/20/19 6:11 | 4/20/19 6:47 | 1731 | SENSOR |° | 4/20/19 5:54 | 4/20/19 6:11 | 1732 | SENSOR |° | 4/20/19 5:49 | 4/20/19 5:54 | 1734 | SENSOR |° | 4/20/19 5:37 | 4/20/19 5:49 | 1732 | SENSOR |° | 4/20/19 5:23 | 4/20/19 5:37 | 1731 | SENSOR |° | 4/20/19 5:04 | 4/20/19 5:23 | 1734 | SENSOR |° | 4/19/19 23:22 | 4/20/19 5:04 | 1968 | BOAT | *** | 4/19/19 23:05 | 4/19/19 23:22 | 1731 | SENSOR |o | 4/19/19 23:00 | 4/19/19 23:05 | 1732 | SENSOR |o | 4/19/19 22:49 | 4/19/19 23:00 | 1734 | SENSOR |o | 4/19/19 22:38 | 4/19/19 22:49 | 1731 | SENSOR |o | 4/19/19 22:32 | 4/19/19 22:38 | 1734 | SENSOR |o | 4/19/19 22:25 | 4/19/19 22:32 | 1731 | SENSOR |o , которые будут объединены в это: | start_datetime| end_datetime | sea_level | source_id | |---------------|---------------|-----------|-----------| | 4/20/19 5:04 | 4/20/19 7:32 | 1734 | SENSOR |° | 4/19/19 23:22 | 4/20/19 5:04 | 1968 | BOAT | *** | 4/19/19 22:25 | 4/19/19 23:22 | 1733 | SENSOR |o Случай 3: ЖЕ SOURCE_ID, НО БОЛЬШОЕ ИЗМЕНЕНИЕ Если причина изменения та же, но одно (или более) изменение превышает изменение + -1%, я не хочу, чтобы оно было агрегировано с другими записями, например: | start_datetime | end_datetime | sea_level | source_id | |----------------|---------------|-----------|-----------| | 4/20/19 12:23 | 4/20/19 12:37 | 1731 | SENSOR |° | 4/20/19 12:10 | 4/20/19 12:23 | 1732 | SENSOR |° | 4/20/19 11:49 | 4/20/19 12:10 | 1734 | SENSOR |° | 4/20/19 11:43 | 4/20/19 11:49 | 1731 | SENSOR |° | 4/20/19 11:36 | 4/20/19 11:43 | 1734 | SENSOR |° | 4/20/19 11:31 | 4/20/19 11:36 | 1732 | SENSOR |° | 4/20/19 11:25 | 4/20/19 11:31 | 1910 | SENSOR | *** | 4/20/19 11:18 | 4/20/19 11:25 | 1911 | SENSOR | *** | 4/20/19 11:11 | 4/20/19 11:18 | 1910 | SENSOR | *** | 4/20/19 11:03 | 4/20/19 11:11 | 1912 | SENSOR | *** | 4/20/19 10:55 | 4/20/19 11:03 | 1732 | SENSOR |o | 4/20/19 10:49 | 4/20/19 10:55 | 1731 | SENSOR |o | 4/20/19 10:35 | 4/20/19 10:49 | 1734 | SENSOR |o | 4/20/19 10:27 | 4/20/19 10:35 | 1731 | SENSOR |o | 4/20/19 10:19 | 4/20/19 10:27 | 1734 | SENSOR |o становится таким: | start_datetime | end_datetime | sea_level | source_id | |----------------|---------------|-----------|-----------| | 4/20/19 11:31 | 4/20/19 12:37 | 1732 | SENSOR |° | 4/20/19 11:03 | 4/20/19 11:31 | 1911 | SENSOR | *** | 4/20/19 10:19 | 4/20/19 11:03 | 1733 | SENSOR |o

Ответы [ 2 ]

1 голос
/ 07 июля 2019

К сожалению, делать именно то, что вы хотите, в SQL невозможно.Проблема в том, когда есть постепенные изменения, которые превышают 1%.То есть последовательность значений, таких как:

  • 1732
  • 1742
  • 1752
  • 1762

Без разрыва 1%, но общая разница, основанная на первом значении, составляет 1%.Вы не можете легко сказать, где новая группа должна начинаться, не просматривая все предыдущие данные.

Некоторые базы данных поддерживают рекурсивные CTE, которые позволяют логике находиться в базе данных.Redshift не является одним из них.И рекурсивные CTE по сути являются лишь несколько более эффективным вычислением RBAR (строка за агонизирующей строкой).

Тем не менее, я не уверен, что это имеет большое значение в вашей ситуации (ни один из ваших трехпримеры есть такие).Давайте проигнорируем постепенные изменения и найдем периоды, которые изменяются на основе:

  • Изменения источника
  • Скачок более чем на 1% между смежными записями

Это выполнимо как проблема пропусков и островов, используя lag() и совокупную сумму:

select min(start_datetime) as start_datetime,
       max(end_datetime) as end_datetime,
       sensor_id,
       avg(sea_level)
from (select t.*,
             sum(case when sensor_id <> prev_sensor_id or
                           sea_level < 0.99 * prev_sea_level or
                           sea_level > 1.01 * prev_sea_level
                      then 1 else 0
                 end) over (order by start_datetime) as grp 
      from (select t.*,
                   lag(sensor_id) over (order by start_datetime) as prev_sensor_id,
                   lag(sea_level) over (order by start_datetime) as prev_sea_level
            from t
           ) t
     ) t
group by grp, sensor_id;

Самый внутренний подзапрос получает информацию о предыдущем датчике и уровне моря.Это используется, чтобы определить, где начинается группа.Совокупная сумма в среднем подзапросе присваивает идентификатор группы на основе совокупной суммы запусков до каждой записи.

Наконец, внешний запрос агрегирует значение.

0 голосов
/ 07 июля 2019

Это ужасно похоже на WINDOW FUNCTIONS на помощь.Вы можете найти разницу между предыдущей датой окончания и следующей датой начала следующим образом:

SELECT TOP 10 *, DATEDIFF(SECOND, end_datetime, LEAD(start_datetime, 1) OVER (ORDER BY start_datetime)) [RecordDifference],
LEAD(sea_level,1) OVER (ORDER BY start_datetime) [NextSeaLevel]
FROM <YOUR_TABLE_NAME_HERE>

После того, как вы это сделаете, вы можете GROUP BY для агрегирования:

SELECT MIN(start_datetime) [start_datetime], MAX(end_datetime) [end_datetime], [RecordDifference]
FROM
(
    SELECT start_datetime, end_datetime, sea_level, DATEDIFF(SECOND, end_datetime, LEAD(start_datetime, 1) OVER (ORDER BY start_datetime)) [RecordDifference]
    FROM <YOUR_TABLE_NAME_HERE>
) [rawData]
GROUP BY [RecordDifference]

YouВы можете прочитать больше о оконных функциях здесь .

Для записи это синтаксис SQL Server, но он довольно похож на RedShift.

...