Implementation:Online ml River Online Adaptation Pattern
| Knowledge Sources | Domains | Last Updated |
|---|---|---|
| River River Docs | Online Machine Learning, Time Series Forecasting, Concept Drift | 2026-02-08 16:00 GMT |
Overview
Concrete tool documenting how SNARIMAX and HoltWinters continuously adapt their parameters to evolving time series patterns. Pattern Doc.
Description
This pattern doc describes the specific code paths through which River's online forecasters adapt to changing data patterns. Both SNARIMAX and HoltWinters perform adaptation as an integral part of their learn_one method, without requiring external drift detection. The adaptation mechanisms are fundamentally different but serve the same purpose: keeping the model aligned with the most recent behavior of the time series.
Usage
Reference this pattern when you need to understand the internal adaptation mechanics of River forecasters, or when tuning hyperparameters that control adaptation speed.
Code Reference
Source Location
river/time_series/snarimax.py:L336-L350(SNARIMAX adaptation via regressor update)river/time_series/holt_winters.py:L187-L208(HoltWinters adaptation via exponential smoothing)
SNARIMAX Adaptation
The adaptation happens at line 348 of snarimax.py, where the internal regressor learns from the new observation:
def learn_one(self, y, x=None):
if len(self.y_hist) >= self.differencer.n_required_past_values:
x = self._add_lag_features(x=x, Y=self.y_diff, errors=self.errors)
y_diff = self.differencer.diff(y, self.y_hist)
self.y_diff.appendleft(y_diff)
y_pred = self.regressor.predict_one(x)
self.errors.appendleft(y_diff - y_pred)
# KEY ADAPTATION STEP: regressor updates weights via SGD
self.regressor.learn_one(x, y_diff)
self.y_hist.appendleft(y)
Adaptation mechanism: The default regressor is StandardScaler | LinearRegression. The LinearRegression uses SGD (default lr=0.01) which updates weights by:
w_new = w_old - 0.01 * (hat{y} - y) * x
This means the AR/MA weights continuously shift toward values that minimize recent prediction errors.
HoltWinters Adaptation
The adaptation happens through the component update calls at lines 189-193 of holt_winters.py:
def learn_one(self, y, x=None):
if self._initialized:
# KEY ADAPTATION: Each component updates via exponential smoothing
self.level.update(y, self.trend, self.season)
if self.trend is not None:
self.trend.update(y, self.level)
if self.season is not None:
self.season.update(y, self.level, self.trend)
return
# ... initialization phase ...
For additive level, the update (in AdditiveLevel.update) is:
# river/time_series/holt_winters.py:L20-L23
def update(self, y, trend, season):
self.append(
self.alpha * (y - (season[-season.seasonality] if season else 0))
+ (1 - self.alpha) * (self[-1] + (trend[-1] if trend else 0))
)
Adaptation mechanism: Each smoothing parameter (alpha, beta, gamma) controls the blend between the new signal and the previous state. Higher values weight the new observation more, enabling faster adaptation.
Import
from river import time_series
I/O Contract
Inputs
| Context | Parameter | Type | Description |
|---|---|---|---|
| SNARIMAX | regressor optimizer lr | float | Learning rate controlling SGD adaptation speed (default 0.01) |
| HoltWinters | alpha | float | Level smoothing parameter (0 to 1) |
| HoltWinters | beta | float or None | Trend smoothing parameter (0 to 1) |
| HoltWinters | gamma | float or None | Seasonal smoothing parameter (0 to 1) |
Outputs
| Context | Output | Description |
|---|---|---|
| SNARIMAX | Updated regressor weights | Weights shift toward values minimizing recent prediction errors |
| HoltWinters | Updated level, trend, seasonal components | Components adapt based on smoothing parameter settings |
Usage Examples
Observing SNARIMAX adaptation
from river import datasets
from river import time_series
model = time_series.SNARIMAX(p=3, d=1, q=1, m=12, sd=1)
for i, (x, y) in enumerate(datasets.AirlinePassengers()):
model.learn_one(y)
# The regressor's weights are updated at each step via SGD
# As patterns change, weights adapt automatically
Controlling HoltWinters adaptation speed
from river import time_series
# Fast adaptation: high smoothing parameters
fast_model = time_series.HoltWinters(alpha=0.8, beta=0.5, gamma=0.8, seasonality=12)
# Slow adaptation: low smoothing parameters
slow_model = time_series.HoltWinters(alpha=0.1, beta=0.05, gamma=0.1, seasonality=12)
# Both models adapt, but at different rates
for y in [112, 118, 132, 129, 121, 135, 148, 148, 136, 119, 104, 118, 115]:
fast_model.learn_one(y)
slow_model.learn_one(y)