Изменение значения идентификатора строки на основе порогового значения интервала времени в столбце даты и времени - PullRequest
1 голос
/ 18 июня 2020

Я работаю над geolife набором данных , который содержит GPS-трек с отметкой времени пользователей в текстовом файле (.plt). Каждый текстовый файл содержит точки GPS пользователя за одну поездку. Поэтому я импортировал набор данных в postgres с помощью сценария python.

Поскольку файлы названы строкой чисел в соответствии с временем начала поездки (так, например, файл, содержащий поездку в таблице ниже, 20070920074804.plt), даю trip id (session_id) имя файла (без расширения). Это необработанный GPS в этой таблице trajectories.

 user_id |    session_id     |       timestamp        |    lat    |    lon     | alt 
---------+-------------------+------------------------+-----------+------------+-----
      11 |    20070920074804 | 2007-09-20 07:48:04+01 |  28.19737 | 113.006795 |  71
      11 |    20070920074804 | 2007-09-20 08:07:09+01 | 28.197685 | 113.006792 |  87
      11 |    20070920074804 | 2007-09-20 08:07:10+01 | 28.197685 |  113.00679 |  87
      11 |    20070920074804 | 2007-09-20 14:03:50+01 | 28.197342 | 113.007422 |  62
      11 |    20070920074804 | 2007-09-20 14:04:59+01 | 28.197108 |  113.00734 |  62
      11 |    20070920074804 | 2007-09-20 14:05:01+01 | 28.197088 |  113.00727 |  62

Для анализа я создал другую таблицу trips_metrics, в которой вычисляю метрики поездки из таблицы trajectories и вставляю результат в trip_metrics. Среди вычисляемых значений - расстояние поездки (haversine) и продолжительность (start time - end time).

Затем я заметил кое-что странное: пользователь взял 8hrs поездки, но преодолел расстояние 321m. Тщательно просматривая файл поездки, я заметил скачок времени поездки, предполагающий перерыв в поездке (возможно, пользователь остается на несколько часов, а затем продолжает). Пример находится в row 3 и row 4 в таблице выше.

Чтобы получить точное время поездки, мне нужно разделить поездки с этими случаями таким образом, чтобы, если временной интервал между последовательными строками превышает 30 минут, это следует рассматривать как новую поездку (т.е. новый идентификатор).

Я намерен добавить di git ..02, ..03, .. к текущему session_id поездки в моей таблице trajectories перед фактическим вычислением показателей поездок (т. е. изменение таблицы trajectories). Итак, для примера в таблице выше я хочу разбить его следующим образом:

 user_id |    session_id     |       timestamp        |    lat    |    lon     | alt 
---------+-------------------+------------------------+-----------+------------+-----
      11 |  20070920074804   | 2007-09-20 07:48:04+01 |  28.19737 | 113.006795 |  71
      11 |  20070920074804   | 2007-09-20 08:07:09+01 | 28.197685 | 113.006792 |  87
      11 |  20070920074804   | 2007-09-20 08:07:10+01 | 28.197685 |  113.00679 |  87
      11 |  2007092007480402 | 2007-09-20 14:03:50+01 | 28.197342 | 113.007422 |  62
      11 |  2007092007480402 | 2007-09-20 14:04:59+01 | 28.197108 |  113.00734 |  62
      11 |  2007092007480402 | 2007-09-20 14:05:01+01 | 28.197088 |  113.00727 |  62

Обратите внимание, как я назначаю session_id для новой поездки (поскольку время между ними составляет более 30 минут).

Как я могу сделать это изменение или изменение в моей исходной таблице GPS (trajectories) в postgres?

EDIT

A: Первый запрос в ответе от @GMB работает, однако он дает каждой новой строке session_id в столбце new_session_id.

+---------+----------------+------------------------+-----------+------------+-----+--------+------------------+
| user_id |   session_id   |       timestamp        |    lat    |    lon     | alt | is_gap |  new_session_id  |
+---------+----------------+------------------------+-----------+------------+-----+--------+------------------+
|      11 | 20070920074804 | 2007-09-20 07:48:04+01 | 28.19737  | 113.006795 |  71 |        |   20070920074804 |
|      11 | 20070920074804 | 2007-09-20 08:07:09+01 | 28.197685 | 113.006792 |  87 |      1 | 2007092007480401 |
|      11 | 20070920074804 | 2007-09-20 08:07:10+01 | 28.197685 | 113.00679  |  87 |      1 | 2007092007480402 |
|      11 | 20070920074804 | 2007-09-20 14:03:50+01 | 28.197342 | 113.007422 |  62 |      1 | 2007092007480403 |
|      11 | 20070920074804 | 2007-09-20 14:04:59+01 | 28.197108 | 113.00734  |  62 |      1 | 2007092007480404 |
|      11 | 20070920074804 | 2007-09-20 14:05:01+01 | 28.197088 | 113.00727  |  62 |      1 | 2007092007480405 |
+---------+----------------+------------------------+-----------+------------+-----+--------+------------------+

Ожидаемый результат:

+---------+----------------+------------------------+-----------+------------+-----+--------+------------------+
| user_id |   session_id   |       timestamp        |    lat    |    lon     | alt | is_gap |  new_session_id  |
+---------+----------------+------------------------+-----------+------------+-----+--------+------------------+
|      11 | 20070920074804 | 2007-09-20 07:48:04+01 | 28.19737  | 113.006795 |  71 |        |   20070920074804 |
|      11 | 20070920074804 | 2007-09-20 08:07:09+01 | 28.197685 | 113.006792 |  87 |        |   20070920074804 |
|      11 | 20070920074804 | 2007-09-20 08:07:10+01 | 28.197685 | 113.00679  |  87 |      1 | 2007092007480401 |
|      11 | 20070920074804 | 2007-09-20 14:03:50+01 | 28.197342 | 113.007422 |  62 |      1 | 2007092007480401 |
|      11 | 20070920074804 | 2007-09-20 14:04:59+01 | 28.197108 | 113.00734  |  62 |      1 | 2007092007480401 |
|      11 | 20070920074804 | 2007-09-20 14:05:01+01 | 28.197088 | 113.00727  |  62 |      1 | 2007092007480401 |
+---------+----------------+------------------------+-----------+------------+-----+--------+------------------+

Идея состоит в том, чтобы присвоить «появляющейся» поездке новый идентификатор old_session_id + 01. При обнаружении еще одной возникающей поездки ему следует присвоить old_session_id + 02 и т. Д.

B: Второй запрос с опцией обновления содержит синтаксическую ошибку:

update trajectories t
from (
    select 
        t.*,
        case when sum(is_gap) over(partition by session_id order by timestamp) > 0
            then session_id * 100 + sum(is_gap) over(partition by session_id order by timestamp)
            else session_id
        end new_session_id
    from (
        select
            t.*,
            (timestamp > lag(timestamp) over(partition by session_id order by timestamp))::int is_gap
        from trajectories t
    ) t
) t1
set session_id = t1.new_session_id
where t1.session_id = t.session_id and t1.timestamp = t.timestamp

ERROR:  syntax error at or near "from"
LINE 2: from (

Ответы [ 2 ]

1 голос
/ 18 июня 2020

Это проблема промежутков и островов. Вы хотите обнаружить последовательные строки с разницей в метках времени, превышающей 30 минут, а затем соответствующим образом изменить session_id.

Можно использовать lag(), а затем совокупное количество пробелов - затем вы можете использовать эту информацию для вычисления нового session_id:

select 
    t.*,
    case when sum(is_gap) over(partition by session_id order by timestamp) > 0
        then session_id * 100 + sum(is_gap) over(partition by session_id order by timestamp)
        else session_id
    end new_session_id
from (
    select
        t.*,
        (timestamp > lag(timestamp) over(partition by session_id order by timestamp))::int is_gap
    from trajectories t
) t

Вы можете включить это в оператор update, если необходимо:

update trajectories t
set session_id = t1.new_session_id
from (
    select 
        t.*,
        case when sum(is_gap) over(partition by session_id order by timestamp) > 0
            then session_id * 100 + sum(is_gap) over(partition by session_id order by timestamp)
            else session_id
        end new_session_id
    from (
        select
            t.*,
            (timestamp > lag(timestamp) over(partition by session_id order by timestamp))::int is_gap
        from trajectories t
    ) t
) t1
where t1.session_id = t.session_id and t1.timestamp = t.timestamp
1 голос
/ 18 июня 2020

Вы можете использовать lag(), кумулятивную сумму для определения сегментов, а затем каким-то образом изменить session_id:

select (case when grp >= 1 then session_id * 100 + grp
             else session_id
        end) as new_session_id,
       t.*
from (select t.*,
             count(*) filter (where prev_ts < timestamp - interval '30 minute') over (partition by session_id, order by timestamp) as grp
      from (select t.*, 
                   lag(timestamp) over (partition by session_id order by timestamp) as prev_ts
            from trajectories t
           ) t
     ) t;

Здесь - это db <> скрипка.

...