Skip to content

πŸ“Š MovingAverage

πŸ“Š MovingAverage

🟒 Beginner βœ… Stable ⏱️ Time Series

🎯 Overview

The MovingAverage layer extracts trend components from time series data by computing a moving average over a specified window. This is a crucial component in time series decomposition, separating trend from seasonal patterns.

The layer applies padding at both ends (replicating first and last values) to maintain the temporal dimension, making it ideal for sequence-to-sequence models.

πŸ” How It Works

The moving average operates through these steps:

  1. Padding: Replicates first and last values at both ends to maintain sequence length
  2. Window Computation: Slides a window of size kernel_size across the temporal dimension
  3. Averaging: Computes mean of values within each window
  4. Output: Returns trend component with same shape as input
graph LR
    A["Input<br/>(batch, time, features)"] --> B["Pad Front & End<br/>Replicate Edges"]
    B --> C["Create Windows<br/>of Size K"]
    C --> D["Compute Mean<br/>Per Window"]
    D --> E["Output<br/>(batch, time, features)"]

    style A fill:#e6f3ff,stroke:#4a86e8
    style E fill:#e8f5e9,stroke:#66bb6a
    style C fill:#fff9e6,stroke:#ffb74d

πŸ’‘ Why Use This Layer?

Challenge Traditional Approach MovingAverage Solution
Trend Extraction Manual computation 🎯 Automatic trend extraction
Sequence Length Output shorter than input βœ… Preserves temporal dimension
Edge Effects Loses information at boundaries πŸ”„ Replicates boundary values
Decomposition Complex preprocessing 🧩 Integrates seamlessly

πŸ“Š Use Cases

  • Time Series Decomposition: Extract trend from seasonal + residual components
  • Signal Smoothing: Reduce noise while preserving temporal structure
  • Trend Analysis: Identify long-term patterns in time series
  • Preprocessing: Prepare data for forecasting models
  • Pattern Recognition: Detect seasonal cycles separate from trends

πŸš€ Quick Start

Basic Usage

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import keras
from kerasfactory.layers import MovingAverage

# Create sample time series data
batch_size, time_steps, features = 32, 100, 8
x = keras.random.normal((batch_size, time_steps, features))

# Apply moving average for trend extraction
trend = MovingAverage(kernel_size=25)(x)
print(f"Input shape: {x.shape}")      # (32, 100, 8)
print(f"Trend shape: {trend.shape}")  # (32, 100, 8)

Time Series Decomposition

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import keras
from kerasfactory.layers import MovingAverage

# Create synthetic seasonal time series
x = keras.random.normal((16, 200, 4))

# Extract trend
moving_avg = MovingAverage(kernel_size=25)
trend = moving_avg(x)

# Get residual/seasonal component
seasonal = x - trend

print(f"Trend shape: {trend.shape}")
print(f"Seasonal shape: {seasonal.shape}")

In a Model Pipeline

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import keras
from kerasfactory.layers import MovingAverage

def create_forecasting_model(seq_len, n_features):
    inputs = keras.Input(shape=(seq_len, n_features))

    # Extract trend
    trend = MovingAverage(kernel_size=25)(inputs)

    # Process trend
    trend_processed = keras.layers.Dense(64, activation='relu')(trend)
    trend_out = keras.layers.Dense(1)(trend_processed)

    model = keras.Model(inputs, trend_out)
    return model

model = create_forecasting_model(seq_len=100, n_features=8)
model.compile(optimizer='adam', loss='mse')

πŸ“– API Reference

kerasfactory.layers.MovingAverage

Moving Average layer for time series trend extraction.

Classes

MovingAverage
1
2
3
MovingAverage(
    kernel_size: int, name: str | None = None, **kwargs: Any
)

Extracts the trend component using moving average.

This layer computes a moving average over time series to extract the trend component. It applies padding at both ends to maintain the temporal dimension.

Parameters:

Name Type Description Default
kernel_size int

Size of the moving average window.

required
name str | None

Optional name for the layer.

None
Input shape

(batch_size, time_steps, channels)

Output shape

(batch_size, time_steps, channels)

Example
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import keras
from kerasfactory.layers import MovingAverage

# Create sample time series data
x = keras.random.normal((32, 100, 8))  # 32 samples, 100 time steps, 8 features

# Apply moving average
moving_avg = MovingAverage(kernel_size=25)
trend = moving_avg(x)
print("Trend shape:", trend.shape)  # (32, 100, 8)

Initialize the MovingAverage layer.

Parameters:

Name Type Description Default
kernel_size int

Size of the moving average kernel.

required
name str | None

Optional layer name.

None
**kwargs Any

Additional keyword arguments.

{}
Source code in kerasfactory/layers/MovingAverage.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
def __init__(
    self,
    kernel_size: int,
    name: str | None = None,
    **kwargs: Any,
) -> None:
    """Initialize the MovingAverage layer.

    Args:
        kernel_size: Size of the moving average kernel.
        name: Optional layer name.
        **kwargs: Additional keyword arguments.
    """
    # Set private attributes first
    self._kernel_size = kernel_size

    # Validate parameters
    self._validate_params()

    # Set public attributes BEFORE calling parent's __init__
    self.kernel_size = self._kernel_size

    # Call parent's __init__ after setting public attributes
    super().__init__(name=name, **kwargs)

πŸ”§ Parameters Deep Dive

kernel_size (int)

  • Purpose: Size of the moving average window
  • Range: 1 to sequence_length
  • Default: None (required)
  • Impact: Larger kernel β†’ more smoothing, more trend preservation
  • Recommendation: For daily data with weekly patterns, use 7; for monthly patterns, use 25-30

πŸ“ˆ Performance Characteristics

  • Speed: ⚑⚑⚑⚑ Very fast - O(n) complexity
  • Memory: πŸ’ΎπŸ’Ύ Minimal - only input/output tensors
  • Accuracy: 🎯🎯🎯🎯 Preserves trends perfectly
  • Best For: Quick preprocessing and trend extraction

🎨 Examples

Example 1: Smooth Noisy Signal

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import keras
from kerasfactory.layers import MovingAverage

# Create noisy sine wave
time_steps = 200
t = keras.ops.arange(time_steps, dtype='float32') * 0.1
signal = keras.ops.sin(t) + keras.random.normal((time_steps,)) * 0.2
signal = keras.ops.expand_dims(keras.ops.expand_dims(signal, axis=0), axis=-1)

# Apply moving average
ma = MovingAverage(kernel_size=11)
smoothed = ma(signal)

print(f"Original signal shape: {signal.shape}")
print(f"Smoothed signal shape: {smoothed.shape}")

Example 2: Multi-Scale Decomposition

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import keras
from kerasfactory.layers import MovingAverage

def multi_scale_decomposition(x, scales=[7, 25, 50]):
    """Decompose time series at multiple scales."""
    trends = []
    seasonals = []

    for scale in scales:
        ma = MovingAverage(kernel_size=scale)
        trend = ma(x)
        seasonal = x - trend

        trends.append(trend)
        seasonals.append(seasonal)

    return trends, seasonals

# Create time series
x = keras.random.normal((1, 200, 4))

# Multi-scale decomposition
trends, seasonals = multi_scale_decomposition(x)

for i, (t, s) in enumerate(zip(trends, seasonals)):
    print(f"Scale {i}: Trend {t.shape}, Seasonal {s.shape}")

Example 3: Trend Extraction for Forecasting

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import keras
from kerasfactory.layers import MovingAverage

# Create a model that uses both trend and seasonal components
def create_decomposition_forecaster(seq_len, pred_len, n_features):
    inputs = keras.Input(shape=(seq_len, n_features))

    # Decompose into trend and seasonal
    trend = MovingAverage(kernel_size=25)(inputs)
    seasonal = inputs - trend

    # Process trend
    trend_x = keras.layers.Dense(32, activation='relu')(trend)
    trend_x = keras.layers.Dense(16, activation='relu')(trend_x)
    trend_pred = keras.layers.Dense(pred_len)(trend_x)

    # Process seasonal
    seasonal_x = keras.layers.Dense(32, activation='relu')(seasonal)
    seasonal_x = keras.layers.Dense(16, activation='relu')(seasonal_x)
    seasonal_pred = keras.layers.Dense(pred_len)(seasonal_x)

    # Combine predictions
    output = trend_pred + seasonal_pred

    model = keras.Model(inputs, output)
    return model

# Create and use model
model = create_decomposition_forecaster(seq_len=100, pred_len=12, n_features=1)
model.compile(optimizer='adam', loss='mse')

πŸ’‘ Tips & Best Practices

  • Kernel Size Selection: Choose based on the periodicity of your data
  • Odd Kernel Sizes: Use odd sizes (3, 5, 7, ...) for symmetric padding
  • Memory Efficient: Very memory-efficient even for long sequences
  • GPU Friendly: Fully compatible with GPU acceleration
  • Differentiable: Gradients flow properly for training

⚠️ Common Pitfalls

  • Kernel Size Too Large: May over-smooth and remove important details
  • Kernel Size Too Small: May not capture true trend, only high-frequency noise
  • Edge Effects: First and last values are replicated - consider this in interpretation
  • Data Normalization: Input data should be normalized for best results

πŸ“š Further Reading