Найти максимумы и минимумы значений временных рядов с помощью SQL - PullRequest
1 голос
/ 13 апреля 2019

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

enter image description here

Я попытался разделить значения по диапазону, и я определенно не думаю, что делаю это правильно. Вот запрос, который я написал, который в лучшем случае дает мне заказанные даты

SELECT
  date,
  MAX(index) OVER (PARTITION BY MAX(CAST(index AS numeric))
  ORDER BY
    date)
FROM (
  SELECT
    (value1 - value2) AS index,
    date
  FROM
    `project.dataset.table` )
GROUP BY
  date,
  index
ORDER BY
  date

Мое окончательное решение заключается в том, что я хочу реализовать запрос, который приводит к чему-то вроде этого при запросе значений минимума и аналогичным образом для максимумов

Row |    date    |       minimas
-------------------------------------
1   | 2017-02-04 | 0.3149100257069409
2   | 2017-12-05 | 0.5784622847441183

Ответы [ 3 ]

3 голосов
/ 13 апреля 2019

Обрабатывать смежные дубликаты значений сложно. Вы не указываете, как вы хотите, чтобы они обрабатывались. Если вам нужно только первое такое значение, то фильтрация работает:

Для локальных минимумов:

SELECT Row, date, f0 AS minimal
FROM (SELECT t.*,
             LEAD(f0) OVER (ORDER BY DATE) as f0_lead
      FROM (SELECT t.*,
                   LAG(f0) OVER (ORDER BY date) AS f0_lag
            FROM `project.dataset.table` t
           ) t
      WHERE f0_lag IS NULL or f0_lag <> f0
     ) t
WHERE (f0 < f0_lag or f0_lag is null) and
      (f0 < f0_lead or f0_lead is null);

Или, если хотите, вы можете упростить сравнения:

SELECT Row, date, f0 AS minimal
FROM (SELECT t.*,
             LEAD(f0) OVER (ORDER BY DATE) as f0_lead
      FROM (SELECT t.*,
                   LAG(f0) OVER (ORDER BY date) AS f0_lag
            FROM t
           ) t
      WHERE f0_lag IS NULL or f0 < f0_lag
     ) t
WHERE f0 < f0_lead or f0_lead is null;

Локальные максимумы могут следовать той же логике с изменением < с на > с.

Здесь - это скрипта db <> (которая использует Postgres, но это не имеет значения).

EDIT:

Возврат всех минимумов / максимумов подряд более сложен. Следующие работы в BigQuery:

WITH t AS (
    SELECT 1 AS Row, '2017-01-19' AS date, 0.3904 AS f0 UNION ALL
    SELECT 2,  '2017-02-04', 0.3149 UNION ALL
    SELECT 2.5,  '2017-02-05', 0.3149 UNION ALL
    SELECT 3,  '2017-03-24', 0.3302 UNION ALL
    SELECT 4,  '2017-04-09', 0.5339 UNION ALL
    SELECT 5,  '2017-05-11', 0.7753 UNION ALL
    SELECT 6,  '2017-05-27', 0.8539 UNION ALL
    SELECT 7,  '2017-09-16', 0.8803 UNION ALL
    SELECT 7.5,  '2017-09-17', 0.8803 UNION ALL
    SELECT 7.7,  '2017-09-18', 0.8803 UNION ALL
    SELECT 8,  '2017-10-02', 0.8570 UNION ALL
    SELECT 9,  '2017-11-03', 0.7744 UNION ALL
    SELECT 10, '2017-11-19', 0.6092 UNION ALL
    SELECT 11, '2017-12-05', 0.5785
)
SELECT t.*
FROM (SELECT t.*,
             MAX(f0_lag) OVER (PARTITION BY grp) as grp_f0_lag,
             MAX(f0_lead) OVER (PARTITION BY grp) as grp_f0_lead
      FROM (SELECT t.*,
                   COUNTIF(f0_lag <> f0) OVER (ORDER BY DATE) as grp,
                   LEAD(f0) OVER (ORDER BY DATE) as f0_lead
            FROM (SELECT t.*,
                         LAG(f0) OVER (ORDER BY date) AS f0_lag
                  FROM t
                 ) t
           ) t
     ) t
WHERE (f0 < grp_f0_lag or grp_f0_lag is null) and
      (f0 < grp_f0_lead or grp_f0_lead is null) ;

По сути, это идентифицирует группы смежных значений. Затем он распространяет максимальные значения lag() и lead() по группе (для максимума вы хотите распространить минимальные значения).

Вся группа затем обрабатывается как единое целое и в наборе результатов.

1 голос
/ 13 апреля 2019

Ниже для BigQuery Standard SQL

#standardSQL
SELECT * EXCEPT(prev, next), 
  CASE 
    WHEN prev < next THEN 'min'
    WHEN prev > next THEN 'max'
    WHEN prev IS NULL THEN 'start'
    WHEN next IS NULL THEN 'finish'
  END extremum
FROM (
  SELECT *, 
    SIGN(index - LAG(index) OVER(ORDER BY DAY)) prev, 
    SIGN(LEAD(index) OVER(ORDER BY DAY) - index) next
  FROM `project.dataset.table`
)
WHERE IFNULL(prev != next, TRUE)

Вы можете проверить, поиграть с выше, используя примеры данных из вашего вопроса, как в примере ниже

#standardSQL
WITH `project.dataset.table` AS (
  SELECT DATE '2017-01-19' day, 0.39 index UNION ALL
  SELECT '2017-02-04', 0.31 UNION ALL
  SELECT '2017-03-24', 0.33 UNION ALL
  SELECT '2017-04-09', 0.53 UNION ALL
  SELECT '2017-05-11', 0.77 UNION ALL
  SELECT '2017-05-27', 0.85 UNION ALL
  SELECT '2017-09-16', 0.88 UNION ALL
  SELECT '2017-10-02', 0.85 UNION ALL
  SELECT '2017-11-03', 0.77 UNION ALL
  SELECT '2017-11-19', 0.61 UNION ALL
  SELECT '2017-12-05', 0.57 
)
SELECT * EXCEPT(prev, next), 
  CASE 
    WHEN prev < next THEN 'min'
    WHEN prev > next THEN 'max'
    WHEN prev IS NULL THEN 'start'
    WHEN next IS NULL THEN 'finish'
  END extremum
FROM (
  SELECT *, 
    SIGN(index - LAG(index) OVER(ORDER BY DAY)) prev, 
    SIGN(LEAD(index) OVER(ORDER BY DAY) - index) next
  FROM `project.dataset.table`
)
WHERE IFNULL(prev != next, TRUE)
-- ORDER BY day

с результатом

Row day         index   extremum     
1   2017-01-19  0.39    start    
2   2017-02-04  0.31    min  
3   2017-09-16  0.88    max  
4   2017-12-05  0.57    finish    
0 голосов
/ 13 апреля 2019

Мы можем определить локальный минимум как точку на оси x времени, где значение отклика как до, так и после больше значения в минимальной точке.В случае конечной точки на любом конце, только одно значение должно быть больше.Мы можем попробовать использовать функции LEAD и LAG здесь:

SELECT Row, date, f0 AS minimal
FROM
(
    SELECT Row, date, f0,
        LAG(f0, 1, f0 + 0.1) OVER (ORDER BY date) AS f0_lag,
        LEAD(f0, 1, f0 + 0.1) OVER (ORDER BY date) AS f0_lead
    FROM project.dataset.table
) t
WHERE f0 < f0_lag AND f0 < f0_lead;

Вот демонстрация в SQL Server с использованием ваших данных примера.Поскольку я основываю свой ответ на SQL Server, поскольку у меня нет доступа к BigQuery, возможно, вам придется немного подправить синтаксис, который я использовал.

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