Как правильно кодировать циклические признаки
Что проверяют
Понимаете ли вы, почему час 23 и час 0 для модели не должны быть далеко друг от друга, и умеете ли выбрать кодирование под конкретный алгоритм. Следом обычно спрашивают, нужно ли это градиентному бустингу.
Как отвечать
Циклический признак это значение с периодом: час суток (24), день недели (7), месяц (12), день года, направление ветра в градусах (360). Если подать его числом, линейная модель, kNN или нейросеть увидят между 23 и 0 максимальное расстояние, хотя это соседние часы.
- sin/cos-кодирование: значение переводят в точку на окружности,
hour_sin = np.sin(2 * np.pi * hour / 24)иhour_cosаналогично через косинус. Нужны оба: один синус даёт одно значение для разных часов, например для 1 и 11. Евклидово расстояние между точками теперь отражает близость по циклу. Значения уже лежат от минус единицы до единицы, отдельно масштабировать их не нужно; - one-hot: для малого числа значений (день недели) часто проще и не хуже, особенно если эффект у каждого дня свой, а не плавный;
- периодические сплайны:
SplineTransformer(extrapolation="periodic")в scikit-learn даёт несколько гладких базисных функций вместо двух и лучше ловит сложную форму, например два пика трафика в сутки; - радиальные базисные функции: похожая идея, несколько «колоколов» по кругу.
Про деревья стоит сказать отдельно. Бустинг и случайный лес режут признаки по одному, а пару sin/cos имеет смысл читать только вместе, поэтому деревьям сырое целое число часто подходит не хуже.
Цикличность не заменяет тренд. Год или номер недели от начала наблюдений кодируют обычным числом, иначе модель потеряет рост во времени.
Где спотыкаются
Делят на неправильный период: час от 0 до 23 делят на 23 вместо 24. Тогда 0 и 23 получают одинаковые координаты и сливаются в одно значение. День недели по той же причине делят на 7, а не на 6.
Разбивают пару при отборе признаков. Если выкинуть hour_cos как менее важный, оставшийся синус снова путает разные часы.
Берут единый период для дня месяца, забывая, что в месяцах разное число дней. Конец короткого месяца и начало следующего оказываются не рядом, надёжнее брать долю от длины конкретного месяца.