
Many prediction tasks require time information as model input. Think of a regression model to forecast lemonade sales of a retail company (you might remember the example from my article about context enriched features). The demand for refreshing drinks is obviously higher in summer resulting in a periodic sales curve with peaks in July/August (thinking of a location in Europe here).

In this case, the time within the year is obviously a valuable seasonal information that we should feed into the model. But how should we do that? Dates are difficult, the number of days change depending on the month (and for February even depending to the year) and they exist in various formats:
13th Jan 2023
13.01.2023
2023/03/13
First of all, we can omit the year. To account for a seasonal effect, we just need day and month. In a very simple (and not very thoughtful) approach, we could just input the month as one number and the day as another number.

Why is that a bad idea? The model would have to learn how the Christian Gregorian Calendar works (around 30 days per month, 12 months per year, leap years etc.). With sufficient training data, a deep-learning model will sure be able to "understand" our calendar. "Understanding" means in this case: The model can infer the relative time position within the year from month and date inputs. But we should make learning as easy as possible for our model and take this job on our shoulders (at least we already know how the calendar works). We make use of Python’s datetime library and calculate the relative time within the year with quite simple logic:
from datetime import datetime
import calendar
year = 2023
month = 12
day = 30
passed_days = (datetime(year, month, day) - datetime(year, 1, 1)).days + 1
nr_of_days_per_year= 366 if calendar.isleap(year) else 365
position_within_year = passed_days / nr_of_days_per_year
The resulting _postion_withinyear feature with a value range from close to 0.0 (January 1) to 1.0 (December 31) is much easier to interpret by the model than the (damn complicated) Gregorian Calendar.

But it’s still not ideal. The _position_withinyear feature describes a "saw-tooth" pattern with a hard jump from 1.0 to 0.0 at each turn of the year. This sharp discontinuity can be a problem for effective learning. December 31 and January 1 are very similar dates: They are direct neighbors and have much in common (e.g. similar weather conditions), and they probably have a similar potential for lemonade sales. However, the feature _position_withinyear does not reflect this similarity for December 31 and January 1; in fact, it is as different as it can be.
Ideally, time points in close proximity to each other should have similar time values. We somehow have to design a feature that represents the cyclical nature of the year. In other words, by December 31 we should arrive at the position where we started on January 1. So, of course, it makes sense to model the position within the year as the position on a circle. We can do this by transforming _position_withinyear into the x and y coordinate of a unit circle.
For this we use the sine and cosine functions:
sin(α) = x
cos(α) = y
where α is the the angle applied to the circle. If the unit circle represents the year, α represents the time within the year that has already passed.

α is thus equivalent to the _position_withinyear feature, the only difference being that α has a different scale (α: 0.0–_2π_¹, _position_withinyear: 0.0–1.0).
By simply scaling _position_withinyear to α and calculating sine and cosine, we transform the "saw-tooth" pattern to a circular representation with soft transitions.
import math
# scale to 2pi (360 degrees)
alpha = position_within_year * math.pi * 2
year_circle_x = math.sin(alpha)
year_circle_y = math.cos(alpha)
# scale between 0 and 1 (original unit circle positions are between -1 and 1)
year_circle_x = (year_circle_x + 1) / 2
year_circle_y = (year_circle_y + 1) / 2
time_feature = (year_circle_x, year_circle_y) # so beautiful ;)

The resulting _timefeature is a two-element vector scaled between 0 and 1 that is easy to digest by your prediction model. With a few lines of code, we took lots of unnecessary learning effort from our model’s shoulders.
The unit circle model can be applied to any periodic time information such as day of the month, day of the week, time of the day, minute of the hour etc. The concept can also be expanded to cyclic features outside the time domain:
- Logistics/Public Transport: Relative position of a bus on its round-trip through the city

- Biology: Status of a cell within the cell cycle.
- Do you have other use cases in mind? You’re welcome to write a comment!
Further Information / Connection Points
- A great hands-on article about the same topic from Pierre-Luis Bescond.
- You want to learn more about Feature Engineering for deep-learning models? Check my article about context enriched data.
- You have questions? You need a freelance expert for AI, Data Science, Data Engineering or Python Development? Visit my website and write me a message.
[1] The angle is given in radians here. 0 in radians correspond to 0°, 2π in radians correspond to 360°.
All figures were created by the author.