null

Циклическое кодирование времени для прикладных задач

Сегодня мы поговорим о простом, но действенном математическом способе преобразования данных о времени из линейного промежутка в окружность, имитирующую циферблат часов. Необходимость таких трансформаций появляется в тех случаях, когда алгоритму машинного обучения или разрабатываемому нами сервису важно понимать, что время в сутках идет по кругу и что разница в отметках (например, времени входа) 23:59 и 00:01 незначительная. Подобным образом происходит трансформация периодических временных признаков (не только часы, но и дни, месяцы) в непрерывные числовые значения, сохраняющие связь между концом и началом цикла. Это может использоваться, скажем, при обнаружении аномалий в поведении пользователя. Так, при моделировании профиля поведения нам важно знать, что активность, происходящая в ночь с одного дня на другой, принадлежит примерно к одному промежутку времени, а не к противоположным на временной оси точкам. Кроме того, применяться метод может при прогнозировании сезонных значений разного рода признаков от погоды и климата до числа продаж.

Итак, каким же образом мы можем проецировать время на окружность, где 23:00 и 00:00 окажутся рядом? Для любого временного признака расчет выполняется через перевод значения в радианы внутри текущего цикла по формулам:

В этих формулах:

  • t - текущее значение времени: час (0-23), месяц (1-12), день недели (0-6 или 1-7) и т.п.
  • T - максимальный период цикла, т.е. количество делений в периоде: 24 для часов в сутках (можно на 60 умножить для минут), 12 для месяцев, 7 для дней недели и т.п.

Для примера возьмем случай, когда нам нужно анализировать время входа в приложение в часах и минутах. Покажем, как 23:15 и 00:45 в случае кодирования через синусы и косинусы оказываются примерно в одной области нашей воображаемой временной окружности. В первую очередь, переведем часы в минуты от начала суток (это будет значение t). В таком случае 23:15 превратится в 1395 минут, а 00:45 соответственно в 45 минут. Значение T в таком случае будет 24*60=1440 минут.

Далее по формулам выше рассчитаем, что для 23:15:

  • xsin = -0,195
  • xcos = 0,981
  • Итоговые координаты для кодирования времени: (xcos ; xsin) = (0,981; -0,195)

Для 00:45:

  • xsin = 0,195
  • xcos = 0,981
  • Итоговые координаты для кодирования времени: (xcos ; xsin) = (0,981; 0,195)

Примерно так это будет выглядеть на тригонометрическом круге, имитирующем циферблат часов:

Как мы видим, 23:15 и 00:45 оказались достаточно близко, расстояние между ними небольшое (длина хорды около 0,4, тогда как для полуночи и полудня как самых отдаленных точек оно равняется 2,0). В связи с этим модель поймет, что данные временные отметки располагаются достаточно близко друг к другу, что события происходят примерно в одно время, тем самым существенно повышая точность решаемой задачи. Программная реализация в общем случае также не является сложной. Типичный пример кода на Python:

import numpy as np

def encode_time_with_minutes(time_str):
    hours, minutes = map(int, time_str.split(':'))
    t = hours * 60 + minutes
    T = 24 * 60 
    sin_val = np.sin(2 * np.pi * t / T)
    cos_val = np.cos(2 * np.pi * t / T)
    return round(cos_val, 3), round(sin_val, 3)

print("23:15 ->", encode_time_with_minutes("23:15"))
print("00:45 ->", encode_time_with_minutes("00:45"))

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

Next