EasyFeature Engineering

Cyclic Encoding

Feature Engineering

Easy

Problem

Cyclic features wrap around, so the end of one period is adjacent to its beginning. Encode each value as a point on the unit circle.

First compute its angle:

\theta = \frac{2\pi v}{P}

Then compute its two coordinates:

\operatorname{encoded}(v) = [\sin(\theta), \cos(\theta)]

Here, v is an input value and P is period. Return the sine and cosine pair for every value in the original order.

Theory

Cyclic encoding (also called circular encoding or trigonometric encoding) is a technique for representing features that have a cyclical or periodic nature. It transforms a single cyclic feature into two features using sine and cosine functions, preserving the circular relationship between values.

This is essential for features like hours of the day, days of the week, months of the year, or angles.


The Problem with Linear Encoding

Consider the hour of the day (0-23):

With simple integer encoding:

The numerical difference is 23, but these hours are actually adjacent (just 1 hour apart).

The issue: Linear encoding does not capture that the feature wraps around. The model sees 23 and 0 as very different, when they should be very similar.


The Solution: Sine and Cosine

Transform the cyclic feature using trigonometric functions:

x_{sin} = \sin\left(\frac{2\pi \cdot x}{\text{period}}\right)

x_{cos} = \cos\left(\frac{2\pi \cdot x}{\text{period}}\right)

where:


Why Sine AND Cosine?

Using only sine would create ambiguity:

\sin(30°) = \sin(150°) = 0.5

Two different values map to the same sine value.

Combining both resolves ambiguity:

The pair (\sin, \cos) uniquely identifies every point on the cycle.


Geometric Interpretation

The transformation maps each value to a point on the unit circle:

(x_{cos}, x_{sin}) = (\cos(\theta), \sin(\theta))

where \theta = \frac{2\pi \cdot x}{\text{period}}

Properties of points on unit circle:


Hour of Day Example

Feature: Hour (0-23)

Period: 24 hours

Formula:

\text{hour}_{sin} = \sin\left(\frac{2\pi \cdot \text{hour}}{24}\right)

\text{hour}_{cos} = \cos\left(\frac{2\pi \cdot \text{hour}}{24}\right)

Calculations for specific hours:

Hour 0:

Hour 6:

Hour 12:

Hour 18:


Verifying Adjacency is Preserved

Hour 23 vs Hour 0:

Hour 23:

Hour 0:

Euclidean distance:

d = \sqrt{(0 - (-0.259))^2 + (1 - 0.966)^2} = \sqrt{0.067 + 0.001} \approx 0.261

This is the same distance as between any two adjacent hours, confirming the cyclic relationship is preserved.


Day of Week Example

Feature: Day (0-6, where 0 = Sunday)

Period: 7 days

Formula:

\text{day}_{sin} = \sin\left(\frac{2\pi \cdot \text{day}}{7}\right)

\text{day}_{cos} = \cos\left(\frac{2\pi \cdot \text{day}}{7}\right)

Sample values:


Month of Year Example

Feature: Month (1-12 or 0-11)

Period: 12 months

Formula (if months are 1-12):

\text{month}_{sin} = \sin\left(\frac{2\pi \cdot (\text{month} - 1)}{12}\right)

\text{month}_{cos} = \cos\left(\frac{2\pi \cdot (\text{month} - 1)}{12}\right)

Note: Subtract 1 if months start at 1 to make the range 0-11.

Key insight: December (12) and January (1) will be close in the encoded space.


Angle Encoding

Feature: Angle in degrees (0-360)

Period: 360 degrees

Formula:

\text{angle}_{sin} = \sin\left(\frac{2\pi \cdot \text{angle}}{360}\right) = \sin(\text{angle in radians})

\text{angle}_{cos} = \cos\left(\frac{2\pi \cdot \text{angle}}{360}\right) = \cos(\text{angle in radians})

For radians (0 to 2\pi):

Simply use \sin(\text{angle}) and \cos(\text{angle}) directly.


Wind Direction Example

Feature: Wind direction (0-359 degrees, where 0 = North)

Period: 360 degrees

Sample encodings:

Benefit: Directions 359° and 1° are correctly recognized as close (both near North).


Mathematical Properties

1. Bounded output:

Both sine and cosine are bounded in [-1, 1].

2. Continuous:

Small changes in input produce small changes in output.

3. Periodic:

Values that differ by exactly one period map to the same point.

4. Pythagorean identity:

\sin^2(\theta) + \cos^2(\theta) = 1

All encoded points lie on the unit circle.


Distance Between Cyclic Values

The Euclidean distance in the encoded space reflects the true cyclic distance:

d = \sqrt{(\sin(\theta_1) - \sin(\theta_2))^2 + (\cos(\theta_1) - \cos(\theta_2))^2}

This can be simplified using trigonometric identities:

d = 2\sin\left(\frac{|\theta_1 - \theta_2|}{2}\right)

Maximum distance is 2 (for values half a period apart).


Comparison with One-Hot Encoding

One-hot encoding for hours (24 features):

Problems:

Cyclic encoding advantages:


Comparison with Ordinal Encoding

Ordinal encoding: Hour = 0, 1, 2, ..., 23

Problems:

Cyclic encoding fixes this by making hour 23 and hour 0 close in the feature space.


When to Use Cyclic Encoding

Good use cases:

Not appropriate for:


Handling Different Ranges

General formula for range [a, b] with period = b - a:

x_{sin} = \sin\left(\frac{2\pi \cdot (x - a)}{b - a}\right)

x_{cos} = \cos\left(\frac{2\pi \cdot (x - a)}{b - a}\right)

This shifts the range to start at 0 before applying the transformation.


Multiple Cyclic Features

When you have multiple cyclic features, encode each separately:

Example: Timestamp with hour and day of week

Total: 4 encoded features from 2 original features.


Cyclic Encoding in Time Series

For time series with multiple seasonal patterns:

Daily seasonality: Encode hour of day

Weekly seasonality: Encode day of week

Yearly seasonality: Encode day of year (period = 365 or 366)

Multiple encodings can capture multiple overlapping cycles.


Combining with Other Features

Cyclic encoded features can be:

Example: hour_sin * temperature might capture how temperature effect varies by time of day.


Numerical Precision

Near boundaries:

At exactly 0 and 2\pi:

Mitigation: The errors are typically negligible for machine learning purposes.


Alternative: Radial Basis Functions

Another approach for cyclic features:

Create Gaussian bumps at regular intervals around the cycle:

f_i(x) = \exp\left(-\frac{(x - c_i)^2}{2\sigma^2}\right)

where c_i are center points and distance is measured cyclically.

This creates more features but can capture more complex patterns.


Common Mistakes

1. Forgetting to use both sine and cosine:

Using only sine loses information and creates ambiguity.

2. Wrong period:

Using period = 23 for hours instead of 24 (hours go 0-23 but period is 24).

3. Not handling the range correctly:

If months are 1-12, need to adjust to 0-11 before encoding.

4. Using cyclic encoding for non-cyclic features:

Age is not cyclic (90 should not be close to 0).


Benefits Summary

1. Compact representation: 2 features instead of many one-hot columns

2. Preserves cyclic nature: Adjacent values remain adjacent after encoding

3. Continuous: Works well with gradient-based optimization

4. Scale-invariant: Output always in [-1, 1]

5. Works with any model: Linear models, trees, neural networks all benefit

Examples

Example 1

Input
values = [0, 6, 12, 18], period = 24
Output
[[0.0, 1.0], [1.0, 0.0], [0.0, -1.0], [-1.0, 0.0]]
Explanation
The four values are quarter-period steps and therefore occupy quarter turns on the unit circle.

Example 2

Input
values = [0, 1, 2, 3], period = 4
Output
[[0.0, 1.0], [1.0, 0.0], [0.0, -1.0], [-1.0, 0.0]]

Hints

  1. Use 2 * math.pi * value / period for the angle.
  2. Append math.sin(angle) and math.cos(angle) as a two-item list.

Requirements

Constraints

Starter Code

import math

def cyclic_encoding(values: list, period: float) -> list:
    """
    Returns the sine and cosine encoding of every cyclic value.
    """
    # Write code here
    pass

Test Cases

CaseMatches
Hours of day (quarter turns)public
Four values evenly spacedpublic