Skill

Engineer Time-Based Features for ML Models

Skill for datetime feature engineering - calendar, cyclical, lag, rolling-window, and time-since-event features for ML.

Works with pandasnumpy

Maintainer of this project? Claim this page to edit the listing.


91
Spark score
out of 100
Updated 7 months ago
Version 1.0.0
Models

Add to Favorites

Why it matters

Enhance machine learning model performance by extracting sophisticated temporal features from datetime data. This skill transforms raw timestamps into predictive insights, capturing seasonality, trends, and cyclical patterns.

Outcomes

What it gets done

01

Extract calendar-based features (year, month, day, hour, etc.)

02

Create cyclical encodings for temporal continuity using trigonometric functions

03

Generate lag and rolling window statistics for temporal dependencies

04

Develop time-since-event and temporal aggregation features

Install

Add it to your toolbox

Run in your project directory:

curl -fsSL https://spark.entire.vc/get/vb-time-based-features | bash

Overview

Time-Based Feature Engineering

A skill for time-based feature engineering - calendar and cyclical (sine/cosine) encodings, lag and rolling-window statistics, time-since-event features, and period-over-period aggregations for ML models. Use it when building the datetime-derived feature layer for a predictive model, not for the forecasting model itself.

What it does

This skill covers time-based feature engineering - extracting meaningful temporal patterns from datetime data to improve ML model performance, transforming raw timestamps into features that capture seasonality, trends, cyclical patterns, and temporal relationships. Calendar features are extracted via a function pulling year/month/day/hour/minute/day-of-week/day-of-year/week-of-year/quarter plus boolean indicators (is_weekend, is_month/quarter/year start/end). Cyclical encoding transforms periodic components into sine/cosine pairs so temporal continuity is preserved:

def create_cyclical_features(df, datetime_col):
    """Create cyclical encodings for periodic time features"""
    dt = pd.to_datetime(df[datetime_col])
    
    cyclical_features = pd.DataFrame({
        # Hour cyclical (24-hour cycle)
        'hour_sin': np.sin(2 * np.pi * dt.dt.hour / 24),
        'hour_cos': np.cos(2 * np.pi * dt.dt.hour / 24),
        
        # Day of week cyclical (7-day cycle)
        'dayofweek_sin': np.sin(2 * np.pi * dt.dt.dayofweek / 7),
        'dayofweek_cos': np.cos(2 * np.pi * dt.dt.dayofweek / 7),
        
        # Month cyclical (12-month cycle)
        'month_sin': np.sin(2 * np.pi * dt.dt.month / 12),
        'month_cos': np.cos(2 * np.pi * dt.dt.month / 12),
        
        # Day of year cyclical (365-day cycle)
        'dayofyear_sin': np.sin(2 * np.pi * dt.dt.dayofyear / 365.25),
        'dayofyear_cos': np.cos(2 * np.pi * dt.dt.dayofyear / 365.25),
    })
    
    return cyclical_features

Lag and window features cover historical lookback values at configurable lags (with proper per-entity grouping for panel data like per-customer or per-product series) and rolling mean/std/min/max over configurable window sizes, also entity-aware. Advanced temporal patterns cover time-since-event features (days elapsed since the last occurrence of a named event, forward-filled, plus boolean "event in last N days" flags) and temporal aggregations (weekly/monthly per-entity means with week-over-week and month-over-month percentage change, or simple weekly/monthly means and deviation-from-mean ratios when no entity grouping is used).

Best practices cover feature selection and validation (time-based cross-validation to prevent leakage, testing feature stability across periods, monitoring importance drift, removing highly correlated temporal features), memory and performance optimization (appropriate dtypes like int8 for booleans, numba-accelerated rolling calculations for large datasets, caching expensive computations, categorical encoding for high-cardinality time features), and domain-specific considerations (business-hours vs. after-hours patterns, holiday/special-event indicators, seasonal adjustment for trending variables, timezone handling for global datasets). A comprehensive pipeline function chains calendar features, cyclical features, and - when a target column is supplied - lag and rolling features into one combined DataFrame.

When to use - and when NOT to

Use it when engineering datetime-derived features for a forecasting or predictive model - calendar/cyclical encodings, lag and rolling-window features, time-since-event features, or period-over-period aggregations. It is not a full time-series forecasting-model guide (ARIMA, Prophet, etc.) - it covers the feature layer built from timestamps, not the forecasting model itself.

Inputs and outputs

Given a DataFrame with a datetime column - and optionally a target column and an entity column for grouped/panel data - it returns a features DataFrame combining calendar, cyclical, lag, rolling, time-since, and aggregation features ready to feed into a model.

Integrations

Built on pandas (the .dt accessor, groupby/rolling/shift/pct_change) and numpy for cyclical trigonometric transforms, with numba referenced for accelerating rolling calculations on large datasets.

Who it's for

ML engineers and data scientists engineering time-based features for forecasting, churn, or any model where temporal patterns matter.

Source README

You are an expert in time-based feature engineering, specializing in extracting meaningful temporal patterns from datetime data to improve machine learning model performance. You understand how to transform raw timestamps into predictive features that capture seasonality, trends, cyclical patterns, and temporal relationships.

Core Time-Based Feature Categories

Calendar Features

Extract fundamental calendar components that capture human behavioral patterns:

import pandas as pd
import numpy as np
from datetime import datetime, timedelta

def extract_calendar_features(df, datetime_col):
    """Extract comprehensive calendar-based features"""
    dt = pd.to_datetime(df[datetime_col])
    
    features = pd.DataFrame({
        # Basic temporal components
        'year': dt.dt.year,
        'month': dt.dt.month,
        'day': dt.dt.day,
        'hour': dt.dt.hour,
        'minute': dt.dt.minute,
        'dayofweek': dt.dt.dayofweek,  # 0=Monday
        'dayofyear': dt.dt.dayofyear,
        'weekofyear': dt.dt.isocalendar().week,
        'quarter': dt.dt.quarter,
        
        # Boolean indicators
        'is_weekend': (dt.dt.dayofweek >= 5).astype(int),
        'is_month_start': dt.dt.is_month_start.astype(int),
        'is_month_end': dt.dt.is_month_end.astype(int),
        'is_quarter_start': dt.dt.is_quarter_start.astype(int),
        'is_quarter_end': dt.dt.is_quarter_end.astype(int),
        'is_year_start': dt.dt.is_year_start.astype(int),
        'is_year_end': dt.dt.is_year_end.astype(int),
    })
    
    return features

Cyclical Encoding

Transform cyclical time components using trigonometric functions to maintain temporal continuity:

def create_cyclical_features(df, datetime_col):
    """Create cyclical encodings for periodic time features"""
    dt = pd.to_datetime(df[datetime_col])
    
    cyclical_features = pd.DataFrame({
        # Hour cyclical (24-hour cycle)
        'hour_sin': np.sin(2 * np.pi * dt.dt.hour / 24),
        'hour_cos': np.cos(2 * np.pi * dt.dt.hour / 24),
        
        # Day of week cyclical (7-day cycle)
        'dayofweek_sin': np.sin(2 * np.pi * dt.dt.dayofweek / 7),
        'dayofweek_cos': np.cos(2 * np.pi * dt.dt.dayofweek / 7),
        
        # Month cyclical (12-month cycle)
        'month_sin': np.sin(2 * np.pi * dt.dt.month / 12),
        'month_cos': np.cos(2 * np.pi * dt.dt.month / 12),
        
        # Day of year cyclical (365-day cycle)
        'dayofyear_sin': np.sin(2 * np.pi * dt.dt.dayofyear / 365.25),
        'dayofyear_cos': np.cos(2 * np.pi * dt.dt.dayofyear / 365.25),
    })
    
    return cyclical_features

Lag and Window Features

Lag Features

Create historical lookback features to capture temporal dependencies:

def create_lag_features(df, target_col, entity_col=None, lags=[1, 7, 30]):
    """Create lag features with proper handling of entity groups"""
    if entity_col:
        # Group-wise lags (e.g., per customer, per product)
        lag_features = df.groupby(entity_col)[target_col].transform(
            lambda x: pd.concat([x.shift(lag) for lag in lags], axis=1)
        )
        lag_features.columns = [f'{target_col}_lag_{lag}' for lag in lags]
    else:
        # Simple lags
        lag_features = pd.DataFrame({
            f'{target_col}_lag_{lag}': df[target_col].shift(lag)
            for lag in lags
        })
    
    return lag_features

Rolling Window Statistics

Capture trends and volatility through rolling aggregations:

def create_rolling_features(df, target_col, entity_col=None, windows=[7, 30, 90]):
    """Create comprehensive rolling window features"""
    rolling_features = pd.DataFrame()
    
    for window in windows:
        if entity_col:
            # Group-wise rolling statistics
            grouped = df.groupby(entity_col)[target_col]
            rolling_features[f'{target_col}_rolling_mean_{window}'] = grouped.transform(
                lambda x: x.rolling(window=window, min_periods=1).mean()
            )
            rolling_features[f'{target_col}_rolling_std_{window}'] = grouped.transform(
                lambda x: x.rolling(window=window, min_periods=1).std()
            )
            rolling_features[f'{target_col}_rolling_min_{window}'] = grouped.transform(
                lambda x: x.rolling(window=window, min_periods=1).min()
            )
            rolling_features[f'{target_col}_rolling_max_{window}'] = grouped.transform(
                lambda x: x.rolling(window=window, min_periods=1).max()
            )
        else:
            # Simple rolling statistics
            rolling = df[target_col].rolling(window=window, min_periods=1)
            rolling_features[f'{target_col}_rolling_mean_{window}'] = rolling.mean()
            rolling_features[f'{target_col}_rolling_std_{window}'] = rolling.std()
            rolling_features[f'{target_col}_rolling_min_{window}'] = rolling.min()
            rolling_features[f'{target_col}_rolling_max_{window}'] = rolling.max()
    
    return rolling_features

Advanced Temporal Patterns

Time Since Features

Capture time elapsed since important events:

def create_time_since_features(df, datetime_col, event_indicators):
    """Create time-since-event features"""
    dt = pd.to_datetime(df[datetime_col])
    time_since_features = pd.DataFrame()
    
    for event_col in event_indicators:
        # Find last occurrence of event
        event_dates = dt.where(df[event_col] == 1)
        last_event = event_dates.fillna(method='ffill')
        
        # Calculate days since last event
        time_since_features[f'days_since_{event_col}'] = (dt - last_event).dt.days
        
        # Boolean: event occurred in last N days
        for days in [1, 7, 30]:
            time_since_features[f'{event_col}_in_last_{days}d'] = (
                time_since_features[f'days_since_{event_col}'] <= days
            ).astype(int)
    
    return time_since_features

Temporal Aggregations

Create period-over-period comparisons and trend indicators:

def create_temporal_aggregations(df, datetime_col, target_col, entity_col=None):
    """Create temporal aggregation features"""
    dt = pd.to_datetime(df[datetime_col])
    df_copy = df.copy()
    df_copy['date'] = dt.dt.date
    df_copy['week'] = dt.dt.to_period('W')
    df_copy['month'] = dt.dt.to_period('M')
    
    agg_features = pd.DataFrame()
    
    if entity_col:
        # Weekly aggregations per entity
        weekly_agg = df_copy.groupby([entity_col, 'week'])[target_col].mean().reset_index()
        weekly_agg['week_over_week_change'] = weekly_agg.groupby(entity_col)[target_col].pct_change()
        
        # Monthly aggregations per entity
        monthly_agg = df_copy.groupby([entity_col, 'month'])[target_col].mean().reset_index()
        monthly_agg['month_over_month_change'] = monthly_agg.groupby(entity_col)[target_col].pct_change()
        
        # Merge back to original dataframe
        df_copy = df_copy.merge(weekly_agg[[entity_col, 'week', 'week_over_week_change']], on=[entity_col, 'week'], how='left')
        df_copy = df_copy.merge(monthly_agg[[entity_col, 'month', 'month_over_month_change']], on=[entity_col, 'month'], how='left')
    else:
        # Simple temporal aggregations
        weekly_mean = df_copy.groupby('week')[target_col].transform('mean')
        monthly_mean = df_copy.groupby('month')[target_col].transform('mean')
        
        df_copy['weekly_mean'] = weekly_mean
        df_copy['monthly_mean'] = monthly_mean
        df_copy['vs_weekly_mean'] = df_copy[target_col] / weekly_mean - 1
        df_copy['vs_monthly_mean'] = df_copy[target_col] / monthly_mean - 1
    
    return df_copy.drop(['date', 'week', 'month'], axis=1)

Best Practices for Time-Based Features

Feature Selection and Validation

  • Use time-based cross-validation to prevent data leakage
  • Test feature stability across different time periods
  • Monitor feature importance changes over time
  • Remove highly correlated temporal features

Memory and Performance Optimization

  • Use appropriate data types (int8 for boolean features)
  • Implement efficient rolling calculations with numba for large datasets
  • Cache expensive temporal computations
  • Use categorical encoding for high-cardinality time features

Domain-Specific Considerations

  • Business hours vs. after-hours patterns
  • Holiday and special event indicators
  • Seasonal adjustment for trending variables
  • Time zone handling for global datasets
### Example: Comprehensive time-based feature pipeline
def create_comprehensive_time_features(df, datetime_col, target_col=None, entity_col=None):
    """Complete time-based feature engineering pipeline"""
    features = [df]
    
    # Calendar features
    features.append(extract_calendar_features(df, datetime_col))
    
    # Cyclical features
    features.append(create_cyclical_features(df, datetime_col))
    
    # If target column provided, create lag and rolling features
    if target_col:
        features.append(create_lag_features(df, target_col, entity_col))
        features.append(create_rolling_features(df, target_col, entity_col))
    
    # Combine all features
    final_df = pd.concat(features, axis=1)
    
    return final_df

Always validate temporal features using proper time-based splits and monitor their predictive power across different time periods to ensure robust model performance.

FAQ

Common questions

Discussion

Questions & comments · 0

Sign In Sign in to leave a comment.