Skip to content

Adaptive local linear trend decomposition for time series analysis.

License

chotanansub/autotrend

Repository files navigation

πŸ“ˆ AutoTrend: Local Linear Trend Extraction for Time Series

AutoTrend Logo
Python 3.8+ MIT License PyPI version

πŸš€ Demo: Google Colab

AutoTrend is a lightweight, iterative method for extracting local linear trends from time series data. Unlike traditional sliding window approaches that fit a model at every point, AutoTrend achieves computational efficiency by training a single linear regression model per focus region and extending the trend forward, measuring prediction errors without repeated model fitting.

Iterative Refinement Process

πŸ“¦ Installation

pip install autotrend

Or install from source:

git clone https://github.com/chotanansub/autotrend.git
cd autotrend
pip install -e .

πŸš€ Quick Start

import numpy as np
from autotrend import decompose_llt

# Generate or load your time series
sequence = np.sin(np.linspace(0, 50, 500)) + np.linspace(0, 5, 500)

# Run LLT decomposition
result = decompose_llt(
    seq=sequence,
    max_models=5,
    window_size=10,
    error_percentile=40
)

# Visualize results
result.plot_full_decomposition()

# Access results
print(f"Number of iterations: {result.get_num_iterations()}")
print(f"Trend segments: {result.get_trend_segments()}")

Output:

  • result.trend_marks: Array indicating which iteration labeled each point
  • result.prediction_marks: Predicted values for each point
  • result.models: List of LinearRegression models from each iteration
  • result.process_logs: Detailed logs for visualization

πŸ’‘ Core Concept

The Problem

Traditional sliding window regression methods fit a new model at every time point, leading to high computational costs. Change point detection methods often require complex algorithms and parameter tuning.

The Solution

AutoTrend uses an iterative, focus-based approach:

  1. Single Model per Region: Train one linear regression model at the start of each focus region
  2. Trend Extension: Extend the trend line forward without retraining
  3. Error-Based Refinement: Identify high-error points and focus on them in the next iteration
  4. Adaptive Segmentation: Automatically discover trend boundaries based on prediction error

Key Advantages

βœ… Computationally Efficient: Minimal model training compared to full sliding windows
βœ… Adaptive: Automatically discovers trend boundaries without predefined change points
βœ… Interpretable: Clear linear segments with explicit slopes and intercepts
βœ… Flexible: Adjustable error thresholds and iteration limits
βœ… Lightweight: No complex optimization or parameter search required


βš™οΈ Algorithm Overview

Input

  • Sequence: Univariate time series y = [yβ‚€, y₁, ..., yβ‚œ]
  • Parameters:
    • window_size: Size of training window (default: 5)
    • max_models: Maximum iterations (default: 10)
    • error_percentile: Error threshold percentile (default: 40)
    • percentile_step: Increment per iteration (default: 0)
    • update_threshold: Whether to update threshold each iteration (default: False)

Process

Step 1: Initialization

Define initial focus targets covering all predictable points:

focus_targets = [window_size, window_size+1, ..., T-1]

Step 2: Train Linear Model

For each iteration, train a model on the first window of the focus region:

X_train = [0, 1, ..., window_size-1]
y_train = sequence[start:end]
model = LinearRegression().fit(X_train, y_train)

Step 3: Extend Trend and Measure Error

Predict forward using the trained model's trend offset:

Ξ” = Ε·_window_size - y_start
Ε·_t = y_(t-window_size) + Ξ”
error_t = |y_t - Ε·_t|

Step 4: Segment by Error Threshold

threshold = percentile(errors, error_percentile)
low_error_points = {t | error_t ≀ threshold}
high_error_points = {t | error_t > threshold}
  • Low error points: Assigned to current iteration, marked as resolved
  • High error points: Become focus targets for next iteration

Step 5: Iterate

Repeat Steps 2-4 on high-error regions until:

  • All points meet the error criterion, OR
  • Maximum iterations reached

Output

LLTResult(
    trend_marks: np.ndarray,      # Iteration labels for each point
    prediction_marks: np.ndarray,  # Predicted values
    models: List[LinearRegression], # Trained models per iteration
    process_logs: List[Tuple]      # Detailed iteration logs
)

πŸ“‚ Directory Structure

autotrend/
β”œβ”€β”€ autotrend/
β”‚   β”œβ”€β”€ __init__.py                    # Main package exports
β”‚   β”œβ”€β”€ core/
β”‚   β”‚   β”œβ”€β”€ __init__.py
β”‚   β”‚   β”œβ”€β”€ llt_algorithm.py           # Core LLT implementation
β”‚   β”‚   β”œβ”€β”€ llt_result.py              # Result dataclass with plotting methods
β”‚   β”‚   β”œβ”€β”€ decompose_llt_class.py     # Object-based API (DecomposeLLT)
β”‚   β”‚   β”œβ”€β”€ functional_api.py          # Functional API (decompose_llt)
β”‚   β”‚   └── utility.py                 # Helper functions (extract_ranges, split_by_gap)
β”‚   β”œβ”€β”€ data/
β”‚   β”‚   β”œβ”€β”€ __init__.py
β”‚   β”‚   β”œβ”€β”€ sythn_data/
β”‚   β”‚   β”‚   β”œβ”€β”€ __init__.py
β”‚   β”‚   β”‚   β”œβ”€β”€ generate_simple_wave.py          # Stationary sine wave generator
β”‚   β”‚   β”‚   β”œβ”€β”€ generate_nonstationary_wave.py   # Amplitude-modulated wave generator
β”‚   β”‚   β”‚   └── generate_piecewise_linear.py     # Piecewise linear sequence generator
β”‚   β”‚   └── datasets/                  # Future: Real-world dataset loaders
β”‚   β”œβ”€β”€ visualization/
β”‚   β”‚   β”œβ”€β”€ __init__.py
β”‚   β”‚   β”œβ”€β”€ plot.py                    # Main plotting module
β”‚   β”‚   β”œβ”€β”€ plot_error.py              # Error analysis visualization
β”‚   β”‚   β”œβ”€β”€ plot_slope.py              # Slope comparison plots
β”‚   β”‚   β”œβ”€β”€ plot_full_decomposition.py # Full decomposition view
β”‚   β”‚   β”œβ”€β”€ plot_iteration_grid.py     # Iteration grid visualization
β”‚   β”‚   └── plot_model_statistics.py   # Model statistics plots
β”‚   └── decomposition/
β”‚       └── __init__.py                # Future: Trend-seasonal decomposition
β”œβ”€β”€ demo/
β”‚   β”œβ”€β”€ __init__.py
β”‚   β”œβ”€β”€ demo_runner.py                 # Demo configuration and utilities
β”‚   β”œβ”€β”€ cases/
β”‚   β”‚   β”œβ”€β”€ __init__.py
β”‚   β”‚   β”œβ”€β”€ simple_wave.py             # Sine wave demo
β”‚   β”‚   β”œβ”€β”€ nonstationary.py           # Non-stationary wave demo
β”‚   β”‚   └── piecewise_linear.py        # Piecewise linear demo
β”‚   └── run_all.py                     # Run all demos
β”œβ”€β”€ examples/
β”‚   β”œβ”€β”€ 01_quick_start.py
β”‚   └── 02_basic_usage.py
β”œβ”€β”€ output/                            # Generated plots and logs
β”‚   β”œβ”€β”€ simple_wave/
β”‚   β”œβ”€β”€ nonstationary_wave/
β”‚   └── piecewise_linear/
β”œβ”€β”€ README.md
β”œβ”€β”€ setup.py
β”œβ”€β”€ pyproject.toml
β”œβ”€β”€ requirements.txt
β”œβ”€β”€ MANIFEST.in
β”œβ”€β”€ update_package.sh
└── .gitignore