foresight

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 30, 2026 License: MIT Imports: 8 Imported by: 0

README

foresight-go

Go Reference CI

Time series forecasting in Go that picks its model by what would have worked.

foresight fits several models to a series, replays the past to see how each would have done, chooses by out-of-sample error and reports intervals taken from the errors actually observed. It uses the standard library only and every result is deterministic.

This is the Go edition of the Rust crate foresight. It is written in Go, not wrapped: no cgo, nothing to link.

Website: https://milkway.github.io/foresight-go/ · Reference: https://pkg.go.dev/github.com/milkway/foresight-go · Português

Install

go get github.com/milkway/foresight-go

Go 1.22 or later.

Use

import foresight "github.com/milkway/foresight-go"

// monthly data whose first observation is in March
y := foresight.Monthly(values, 2)

// replay the last 36 months, 12 months ahead, with every built-in model
report, err := foresight.DefaultBacktest().Run(y, foresight.Defaults())
if err != nil {
	log.Fatal(err)
}

best := report.Best()
fmt.Printf("%s: MAPE %.1f%%\n", best.Name, best.Score)
for _, p := range best.Forecast {
	i, _ := p.Interval(0.80)
	fmt.Printf("%2d %.0f [%.0f, %.0f]\n", p.Horizon, p.Mean, i.Lower, i.Upper)
}

// the total of the next six months, with its own interval
halfYear, _ := best.Cumulative(6)

One model on its own:

// seasonal ARIMA on the log scale
fit, err := foresight.Log(foresight.Airline()).Fit(y)
nextYear := fit.Forecast(12)

// orders chosen from the data, inspected
auto, err := foresight.AutoArima{}.Select(foresight.Monthly(logValues, 2))
fmt.Println(auto.Order())

A trend that bends, with dated events:

model := foresight.Prophet{Events: []foresight.Event{
	{Name: "campaign", Positions: []int{10, 34, 58, 82, 106, 130}}, // future ones included
	{Name: "new_law", Step: true, StepFrom: 80},                     // a lasting change of level
}}
fit, err := model.Estimate(y)
fmt.Println(fit.Changepoints(), fit.Effects())

A model of your own joins the backtest by implementing Model.

What is in it

Piece What it does
Series values + seasonal period; slices keep season and position
Model / Fitted fit once, forecast any horizon, inspect parameters
Models Mean, Naive, Drift, SeasonalNaive, Theta, HoltWinters, LogLinear (optionally deflated by a price index), Arima (seasonal, exact maximum likelihood, optionally with regressors), AutoArima (differences by tests, orders by stepwise search), Ets (the exponential smoothing family in state space form), AutoEts, Prophet (trend with changepoints, Fourier seasonality, dated events and steps)
Transformed any model on the log or another Box-Cox scale (Log, WithBoxCox, WithGuerrero)
Regressors external variables aligned with the data, Fourier terms, SeasonalDummies
Backtest rolling origin (expanding or fixed window) on all cores; MAPE, MAE, RMSE, MASE and bias by horizon; average of the best models; choice by out-of-sample error
Intervals empirical quantiles of the backtest errors, by horizon and for cumulative totals
Measures and tests MAPE, Bias, MAE, RMSE, MASE, Quantile, ACF, KPSS, NDiffs, NSDiffs, SeasonalStrength

The Rust crate has more: TBATS, STL and MSTL, intermittent demand, data cleaning and ensembles.

How it differs from the usual toolkits

Most forecasting libraries choose a model by an in-sample information criterion and derive intervals from distributional assumptions. Here the choice and the intervals both come from forecasts made without seeing the future they are judged against. The interval for a total (say, the rest of a fiscal year) is measured on totals, because adding up monthly limits overstates its uncertainty.

Checked

Against What Agreement
The Rust crate backtest of four models on two public series: errors and bias by horizon, quantiles, forecasts, intervals and the choice the same numbers (relative difference under 10⁻⁹)
The Rust crate Prophet, KPSS, strength of seasonality the same numbers (under 10⁻⁸)
The Rust crate ARIMA, regression with ARIMA errors, ETS: likelihood the same (under 10⁻⁶)
The Rust crate ARIMA and ETS forecasts; automatic orders and automatic ETS on three series forecasts within the precision of the search; the same models chosen
R package forecast 9.0.2 seasonal naive, random walk with drift exact
R package forecast 9.0.2 Theta forecasts within 0.1%

The tests are in rust_test.go, models_test.go and r_test.go. The Rust crate is in turn compared with the R packages forecast and prophet.

Data

  • testdata/piaui_revenue.csv: monthly ICMS and FPE revenue of the state of Piauí, Brazil, March 2017 to June 2026 (Siconfi/STN, RREO Anexo 03), with the chained IPCA price index (BCB/SGS 433). Public data.
  • testdata/air_passengers.csv: the classic monthly airline passengers series (Box & Jenkins, 1976).

Status

Early: the API may change before 1.0.

Development

scripts/dev-check.sh   # gofmt, go vet, go test

License

MIT. See LICENSE.

Documentation

Overview

Package foresight forecasts time series and picks its model by what would have worked.

It fits several models to a series, replays the past to see how each would have done (a rolling-origin backtest), chooses by out-of-sample error and reports intervals taken from the errors actually observed, including intervals for the total of the next k periods. It has no dependencies outside the standard library and every result is deterministic.

y := foresight.Monthly(values, 2) // first observation in March
report, err := foresight.DefaultBacktest().Run(y, foresight.Defaults())
if err != nil {
	log.Fatal(err)
}
best := report.Best()
for _, p := range best.Forecast {
	i, _ := p.Interval(0.80)
	fmt.Println(p.Horizon, p.Mean, i.Lower, i.Upper)
}
total, _ := best.Cumulative(6) // the next six periods, with its own interval

A single model is used through the Model interface:

fit, err := foresight.Theta{}.Fit(foresight.NonSeasonal(values))
next := fit.Forecast(3)

This is the Go edition of the Rust crate of the same name (https://crates.io/crates/foresight). The two are checked against each other and against the R packages forecast and prophet on public data.

Example

Replay the past with every built-in model, take the best and forecast.

package main

import (
	"fmt"

	foresight "github.com/milkway/foresight-go"
)

// Eight years of monthly data with growth, a yearly pattern and some noise.
func sales() []float64 {
	pattern := []float64{1.1, 0.8, 0.9, 1.0, 1.0, 0.9, 1.0, 1.0, 0.9, 1.0, 1.1, 1.3}
	out := make([]float64, 96)
	level := 100.0
	for t := range out {
		noise := float64((t*37)%11-5) * 1.5
		out[t] = level*pattern[t%12] + noise
		level += 0.5
	}
	return out
}

func main() {
	y := foresight.Monthly(sales(), 0) // the first observation is in January
	report, err := foresight.DefaultBacktest().Run(y, foresight.Defaults())
	if err != nil {
		fmt.Println(err)
		return
	}
	best := report.Best()
	fmt.Printf("%s, MAPE %.1f%%\n", best.Name, best.Score)
	for _, p := range best.Forecast[:3] {
		i, _ := p.Interval(0.80)
		fmt.Printf("%d: %.1f [%.1f, %.1f]\n", p.Horizon, p.Mean, i.Lower, i.Upper)
	}
	total, _ := best.Cumulative(6)
	fmt.Printf("next six months: %.0f\n", total.Mean)
}
Output:
log_linear, MAPE 3.1%
1: 164.2 [154.7, 172.1]
2: 120.2 [113.5, 125.3]
3: 136.7 [128.8, 142.6]
next six months: 862

Index

Examples

Constants

View Source
const KPSS5Percent = 0.463

KPSS5Percent is the 5% critical value of the KPSS test for level stationarity.

View Source
const SeasonalStrengthThreshold = 0.64

SeasonalStrengthThreshold is the seasonal strength above which a seasonal difference is taken.

Variables

View Source
var (
	// ErrFewOrigins: the series is too short for at least one pair at the
	// longest horizon.
	ErrFewOrigins = errors.New("foresight: series too short for the backtest")
	// ErrNoCandidate: no candidate could be fitted at every origin.
	ErrNoCandidate = errors.New("foresight: no candidate could be fitted at every origin")
)

Errors of a backtest.

View Source
var (
	// ErrTooShort: the series has too few observations for the model.
	ErrTooShort = errors.New("foresight: series too short for the model")
	// ErrNotFinite: the series has values that are not finite numbers.
	ErrNotFinite = errors.New("foresight: series has values that are not finite")
	// ErrNotPositive: the model needs values greater than zero.
	ErrNotPositive = errors.New("foresight: model needs positive values")
	// ErrNotSeasonal: the model needs a seasonal period of at least 2.
	ErrNotSeasonal = errors.New("foresight: model needs a seasonal series")
	// ErrNoFit: the estimation did not reach a usable result.
	ErrNoFit = errors.New("foresight: model could not be fitted")
)

Errors returned by the models when a series does not suit them. A model never makes up a fit.

View Source
var ErrLength = errors.New("foresight: lengths differ")

ErrLength is returned when two things that must have the same number of observations do not.

View Source
var ErrRegressors = errors.New("foresight: regressors do not cover the series or are collinear")

ErrRegressors is returned when the regressors do not cover the history or the horizon, or cannot be told apart from each other or from the constant.

Functions

func ACF

func ACF(y []float64, maxLag int) []float64

ACF returns the autocorrelations of y at lags 1 to maxLag.

func Bias

func Bias(actual, forecast []float64) float64

Bias is the mean of (forecast − actual) / actual, in percent: positive when the forecasts run above what happened. Pairs whose actual value is zero are left out.

func Difference added in v0.2.0

func Difference(y []float64, lag int) []float64

Difference returns y differenced once at the given lag (1 for the ordinary difference).

func Forecast

func Forecast(m Model, y Series, h int) ([]float64, error)

Forecast fits the model and forecasts the h periods after the last observation. A model with regressors that do not reach the horizon gives an error rather than a forecast that is not a number.

func KPSS added in v0.2.0

func KPSS(y []float64) (float64, bool)

KPSS returns the statistic of the test of Kwiatkowski, Phillips, Schmidt & Shin (1992) for the null hypothesis that the series is stationary around a constant level, with a Bartlett window of ⌊3√n / 13⌋ lags. Large values reject stationarity; the 5% critical value is KPSS5Percent. It reports false for fewer than 3 observations or a statistic that is not finite.

func MAE

func MAE(actual, forecast []float64) float64

MAE is the mean absolute error.

func MAPE

func MAPE(actual, forecast []float64) float64

MAPE is the mean absolute percentage error, in percent. Pairs whose actual value is zero are left out. NaN when there is no pair.

func MASE

func MASE(actual, forecast []float64, scale float64) float64

MASE is the mean absolute scaled error given the scale from MASEScale.

func MASEScale

func MASEScale(train []float64, period int) (float64, bool)

MASEScale is the scale of the mean absolute scaled error: the in-sample mean absolute error of the seasonal naive forecast (naive when period is 1). It reports false when the training data is no longer than one period or the scale is zero.

func NDiffs added in v0.2.0

func NDiffs(y []float64, limit int) int

NDiffs returns the number of ordinary differences needed for the series to pass the KPSS test at 5%, at most limit.

func NSDiffs added in v0.2.0

func NSDiffs(y []float64, period int) int

NSDiffs returns the number of seasonal differences (0 or 1) suggested by the strength of the seasonality.

func Quantile

func Quantile(v []float64, p float64) (float64, bool)

Quantile returns the quantile p of v with linear interpolation (type 7, the default in R). v does not need to be sorted. It reports false for an empty slice.

func RMSE

func RMSE(actual, forecast []float64) float64

RMSE is the root mean squared error.

func SeasonalStrength added in v0.2.0

func SeasonalStrength(y []float64, period int) (float64, bool)

SeasonalStrength returns the strength of seasonality in [0, 1] (Wang, Smith & Hyndman, 2006): 1 − Var(remainder) / Var(seasonal + remainder), from a classical additive decomposition (centred moving average and seasonal means). It reports false for non-seasonal or short series (fewer than two cycles plus one observation).

Types

type Arima added in v0.2.0

type Arima struct {
	P, D, Q int
	// Seasonal orders, ignored for series without seasonality.
	SeasonalP, SeasonalD, SeasonalQ int
	Constant                        Constant
	// Regressors are external variables, with rows for the history and for
	// the periods to forecast. The zero value is none.
	Regressors Regressors
}

Arima is ARIMA(P, D, Q)(SeasonalP, SeasonalD, SeasonalQ) with the seasonal period of the series.

The series is differenced (D ordinary and SeasonalD seasonal differences) and an ARMA model is fitted to the result by exact Gaussian maximum likelihood, computed with the innovations algorithm. The search runs over partial autocorrelations, so the estimates are always stationary and invertible. The likelihood of a mixed model can have more than one peak, so the search starts from three points and keeps the best.

With Regressors, the model is a regression whose errors follow the ARIMA model; the coefficients of the regression and of the model are estimated together.

For series whose swings grow with the level, fit on the log scale with Log.

func Airline added in v0.2.0

func Airline() Arima

Airline returns the "airline model", ARIMA(0,1,1)(0,1,1): a good default for seasonal series, usually on the log scale.

Example

Seasonal ARIMA on the log scale, the model that does best on many monthly series.

package main

import (
	"fmt"

	foresight "github.com/milkway/foresight-go"
)

// Eight years of monthly data with growth, a yearly pattern and some noise.
func sales() []float64 {
	pattern := []float64{1.1, 0.8, 0.9, 1.0, 1.0, 0.9, 1.0, 1.0, 0.9, 1.0, 1.1, 1.3}
	out := make([]float64, 96)
	level := 100.0
	for t := range out {
		noise := float64((t*37)%11-5) * 1.5
		out[t] = level*pattern[t%12] + noise
		level += 0.5
	}
	return out
}

func main() {
	model := foresight.Log(foresight.Airline())
	fit, err := model.Fit(foresight.Monthly(sales(), 0))
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(model.Name())
	for _, v := range fit.Forecast(3) {
		fmt.Printf("%.0f\n", v)
	}
}
Output:
log_arima_011_011
164
121
137

func (Arima) Description added in v0.2.0

func (a Arima) Description() string

func (Arima) Estimate added in v0.2.0

func (a Arima) Estimate(y Series) (*ArimaFit, error)

Estimate fits the model and returns everything that was estimated.

func (Arima) Fit added in v0.2.0

func (a Arima) Fit(y Series) (Fitted, error)

func (Arima) Name added in v0.2.0

func (a Arima) Name() string

type ArimaFit added in v0.2.0

type ArimaFit struct {

	// AR holds φ₁, φ₂, …: X(t) = φ₁X(t−1) + … + Z(t) + θ₁Z(t−1) + …
	AR []float64
	// MA holds θ₁, θ₂, …
	MA []float64
	// SeasonalAR holds Φ₁, Φ₂, … at the seasonal lags.
	SeasonalAR []float64
	// SeasonalMA holds Θ₁, Θ₂, … at the seasonal lags.
	SeasonalMA []float64
	// HasConstant says whether a constant was estimated; Constant is then
	// the mean of the differenced series.
	HasConstant bool
	Constant    float64
	// Regression has the coefficient of each regressor.
	Regression []Param
	// Sigma2 is the innovation variance (maximum likelihood, not corrected
	// for degrees of freedom).
	Sigma2        float64
	LogLikelihood float64
	AIC           float64
	AICc          float64
	BIC           float64
	// Residuals are the one-step prediction errors of the differenced
	// series.
	Residuals []float64
	// contains filtered or unexported fields
}

ArimaFit is an estimated ARIMA model.

func (*ArimaFit) Forecast added in v0.2.0

func (f *ArimaFit) Forecast(h int) []float64

Forecast returns the forecasts for the h periods after the last observation. Where the regressors have no value for a period, the forecast is not a number.

func (*ArimaFit) ForecastVariance added in v0.2.0

func (f *ArimaFit) ForecastVariance(h int) []float64

ForecastVariance returns the variance of the forecast error 1 to h periods ahead, on the scale the model was fitted on.

func (*ArimaFit) IsWellBehaved added in v0.2.0

func (f *ArimaFit) IsWellBehaved(margin float64) bool

IsWellBehaved reports whether every root of the four polynomials is farther from the unit circle than margin (such as 1.01).

func (*ArimaFit) Order added in v0.2.0

func (f *ArimaFit) Order() (p, d, q int)

Order returns the orders (p, d, q).

func (*ArimaFit) Params added in v0.2.0

func (f *ArimaFit) Params() []Param

Params returns the coefficients, the orders and the fit.

func (*ArimaFit) Period added in v0.2.0

func (f *ArimaFit) Period() int

Period returns the seasonal period of the series the model was fitted on.

func (*ArimaFit) SeasonalOrder added in v0.2.0

func (f *ArimaFit) SeasonalOrder() (p, d, q int)

SeasonalOrder returns the seasonal orders (P, D, Q).

type AutoArima added in v0.2.0

type AutoArima struct {
	// Limits of the search. When all six are zero they stand for the
	// defaults: 5, 5, 2, 2, 2 and 1.
	MaxP, MaxQ, MaxSeasonalP, MaxSeasonalQ, MaxD, MaxSeasonalD int
	// FixDifferences uses D and SeasonalD instead of testing.
	FixDifferences bool
	D, SeasonalD   int
	Criterion      Criterion
	// MaxModels is the most models the search will fit (default 94).
	MaxModels int
	// Regressors are external variables of a regression with ARIMA errors;
	// the differences are then decided on what the regression leaves
	// unexplained.
	Regressors Regressors
}

AutoArima is seasonal ARIMA with orders chosen from the data (Hyndman & Khandakar, 2008).

The differences come first: a seasonal difference when the seasonality is strong (NSDiffs), then as many ordinary differences as the KPSS test asks for (NDiffs). The remaining orders are found by a stepwise search: starting from four simple models, the best one is varied — each order up or down by one, alone and in pairs, with and without a constant — for as long as the information criterion improves. Models with roots too close to the unit circle are discarded.

As a Model, the orders are chosen again at every fit, so a backtest judges the whole procedure, not one lucky specification.

The zero value has the usual limits; see DefaultAutoArima.

func DefaultAutoArima added in v0.2.0

func DefaultAutoArima() AutoArima

DefaultAutoArima returns the defaults spelled out.

func (AutoArima) Description added in v0.2.0

func (AutoArima) Description() string

func (AutoArima) Fit added in v0.2.0

func (a AutoArima) Fit(y Series) (Fitted, error)

func (AutoArima) Name added in v0.2.0

func (AutoArima) Name() string

func (AutoArima) Select added in v0.2.0

func (a AutoArima) Select(y Series) (*ArimaFit, error)

Select chooses the orders and returns the estimated model.

type AutoEts added in v0.2.0

type AutoEts struct {
	Criterion Criterion
}

AutoEts is the ETS model with the best information criterion among those that suit the series.

Every combination of error, trend (none, additive, damped) and season (none, additive, multiplicative) is fitted, leaving out the ones with multiplicative parts when the series has values that are not positive, and additive errors with a multiplicative season, whose forecast variance is unbounded.

As a Model, the choice is made again at every fit, so a backtest judges the whole procedure.

Example

The member of the exponential smoothing family that suits the series best.

package main

import (
	"fmt"

	foresight "github.com/milkway/foresight-go"
)

// Eight years of monthly data with growth, a yearly pattern and some noise.
func sales() []float64 {
	pattern := []float64{1.1, 0.8, 0.9, 1.0, 1.0, 0.9, 1.0, 1.0, 0.9, 1.0, 1.1, 1.3}
	out := make([]float64, 96)
	level := 100.0
	for t := range out {
		noise := float64((t*37)%11-5) * 1.5
		out[t] = level*pattern[t%12] + noise
		level += 0.5
	}
	return out
}

func main() {
	fit, err := foresight.AutoEts{}.Select(foresight.Monthly(sales(), 0))
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("ETS(%s)\n", fit.Model().Code())
}
Output:
ETS(MAM)

func (AutoEts) Description added in v0.2.0

func (AutoEts) Description() string

func (AutoEts) Fit added in v0.2.0

func (a AutoEts) Fit(y Series) (Fitted, error)

func (AutoEts) Name added in v0.2.0

func (AutoEts) Name() string

func (AutoEts) Select added in v0.2.0

func (a AutoEts) Select(y Series) (*EtsFit, error)

Select chooses the model and returns it estimated.

type Backtest

type Backtest struct {
	// Origins is how many of the last periods are forecast origins
	// (default 36).
	Origins int
	// Horizon is the longest horizon forecast and evaluated (default 12).
	Horizon int
	// MinTrain is the number of training observations at the first origin
	// (default 48). Shorter series get fewer origins.
	MinTrain int
	// Window trains on the last Window observations only; zero uses
	// everything before the origin.
	Window int
	// Combine also evaluates the simple average of the best Combine models
	// (default 2; 1 disables it).
	Combine int
	// Levels is the coverage of the intervals, such as 0.8 for the 10%–90%
	// quantiles (default 0.80 and 0.95).
	Levels []float64
	// Metric ranks the candidates (default RankByMAPE).
	Metric Metric
	// Sequential fits the origins one at a time instead of on all cores. The
	// result is the same either way.
	Sequential bool
}

Backtest is the configuration of a rolling-origin evaluation.

  1. Rolling origin: for each of the last Origins periods, every candidate is fitted ONLY on the data before the origin and forecasts 1 to Horizon periods ahead; errors are measured against what happened. Horizon h has Origins − h + 1 pairs.
  2. Selection: the candidate with the lowest error averaged over the horizons, the simple average of the best few models included.
  3. Empirical intervals: no normality assumed. For each horizon, the quantiles of the relative error r = actual/forecast − 1 seen in the backtest; the interval is forecast × (1 + q).
  4. Cumulative intervals: the same for the ratio of sums over the first k periods of each origin, because adding up the limits of k intervals overstates the uncertainty of a total.

The zero value of each field stands for its default; DefaultBacktest returns them spelled out.

func DefaultBacktest

func DefaultBacktest() Backtest

DefaultBacktest returns the defaults, which suit monthly data: 36 origins, 12 periods ahead, at least 48 observations to train, the average of the two best models, intervals of 80% and 95%.

func (Backtest) Run

func (b Backtest) Run(y Series, candidates []Candidate) (*Report, error)

Run evaluates the candidates on y, chooses one and forecasts Horizon periods with every candidate that went through the whole backtest.

It returns ErrFewOrigins when the series is too short for at least one pair at the longest horizon, and ErrNoCandidate when no candidate could be fitted at every origin.

type Band

type Band struct {
	Level, Lower, Upper float64
}

Band holds the quantiles of a relative error r (actual/forecast − 1) for one coverage level: the interval is forecast × (1 + Lower) to forecast × (1 + Upper).

type BoxCox added in v0.2.0

type BoxCox struct {
	Lambda float64
}

BoxCox is a transformation of the Box-Cox family: the logarithm for λ = 0, (y^λ − 1)/λ otherwise. It is defined for positive values.

func Guerrero added in v0.2.0

func Guerrero(y Series) (BoxCox, bool)

Guerrero returns the transformation with the λ in [−1, 2] that makes the variability most even across the cycles of the series (Guerrero, 1993): it minimises the coefficient of variation of sd / mean^(1 − λ) over blocks of one period (two observations when there is no seasonality). It reports false without at least two blocks of positive values.

func (BoxCox) Apply added in v0.2.0

func (b BoxCox) Apply(y float64) float64

Apply transforms a value.

func (BoxCox) Invert added in v0.2.0

func (b BoxCox) Invert(z float64) float64

Invert brings a value back to the original scale. This is the median of the forecast distribution, not its mean.

type Candidate

type Candidate struct {
	Name        string
	Description string
	Model       Model
}

Candidate is a model taking part in a backtest, under a name of the caller's choice.

Example

A model of your own joins the backtest by implementing Model.

package main

import (
	"fmt"

	foresight "github.com/milkway/foresight-go"
)

// Eight years of monthly data with growth, a yearly pattern and some noise.
func sales() []float64 {
	pattern := []float64{1.1, 0.8, 0.9, 1.0, 1.0, 0.9, 1.0, 1.0, 0.9, 1.0, 1.1, 1.3}
	out := make([]float64, 96)
	level := 100.0
	for t := range out {
		noise := float64((t*37)%11-5) * 1.5
		out[t] = level*pattern[t%12] + noise
		level += 0.5
	}
	return out
}

func main() {
	candidates := []foresight.Candidate{
		foresight.NewCandidate(foresight.SeasonalNaive{}).Named("same_month", "Same month of last year"),
		foresight.NewCandidate(foresight.LogLinear{Window: 60}),
	}
	report, err := foresight.Backtest{Combine: 1}.Run(foresight.Monthly(sales(), 0), candidates)
	if err != nil {
		fmt.Println(err)
		return
	}
	for _, c := range report.Candidates {
		fmt.Printf("%s: MAPE %.1f%%\n", c.Name, c.Score)
	}
}
Output:
same_month: MAPE 6.6%
log_linear: MAPE 3.3%

func Defaults

func Defaults() []Candidate

Defaults returns a reasonable set of candidates for a backtest: the benchmarks and every model that needs no external data and fits in a moment.

func NewCandidate

func NewCandidate(m Model) Candidate

NewCandidate returns a candidate with the model's own name and description.

func (Candidate) Named

func (c Candidate) Named(name, description string) Candidate

Named returns the candidate under another name and description.

type CandidateReport

type CandidateReport struct {
	Name        string
	Description string
	// Components are the names of the models involved: one, or several for
	// an average.
	Components []string
	// Horizons has the measures by horizon; index 0 is horizon 1.
	Horizons []HorizonStats
	// Score is the ranking metric averaged over the horizons; +Inf when it
	// could not be computed.
	Score float64
	// Params are the parameters fitted on the whole series (none for an
	// average).
	Params []Param
	// Trajectories are the backtest forecasts, [origin][h − 1].
	Trajectories [][]float64
	// Forecast is the forecast from the whole series, with the intervals of
	// the backtest.
	Forecast []Point
}

CandidateReport is the backtest and the forecast of one candidate (a model or an average of models).

func (CandidateReport) Cumulative

func (c CandidateReport) Cumulative(k int) (Point, bool)

Cumulative returns the forecast of the SUM of the first k periods, with intervals from the backtest of sums. It reports false when k is under 1 or beyond the horizon.

type Changepoint added in v0.2.0

type Changepoint struct {
	// Position is relative to the first observation.
	Position int
	// Change is the change in slope, in units of the series per period.
	Change float64
}

Changepoint is a point where the trend did bend.

type Constant added in v0.2.0

type Constant int

Constant says whether an ARIMA model estimates a constant: the mean when the series is not differenced, the drift when it is differenced once. With two or more differences there is never a constant.

const (
	// ConstantByDefault estimates the mean of a series that is not
	// differenced and nothing otherwise.
	ConstantByDefault Constant = iota
	// WithConstant estimates the mean or the drift.
	WithConstant
	// WithoutConstant estimates neither.
	WithoutConstant
)

type Criterion added in v0.2.0

type Criterion int

Criterion is the information criterion that ranks models.

const (
	// ByAICc ranks by the AIC corrected for small samples (the default).
	ByAICc Criterion = iota
	// ByAIC ranks by the AIC.
	ByAIC
	// ByBIC ranks by the BIC.
	ByBIC
)

type Drift

type Drift struct{}

Drift is the random walk with drift: the last observation plus the average change per period over the history.

func (Drift) Description

func (Drift) Description() string

func (Drift) Fit

func (Drift) Fit(y Series) (Fitted, error)

func (Drift) Name

func (Drift) Name() string

type ErrorKind added in v0.2.0

type ErrorKind int

ErrorKind says how the error enters the observation of an ETS model.

const (
	// AdditiveError: y = μ + ε.
	AdditiveError ErrorKind = iota
	// MultiplicativeError: y = μ (1 + ε); needs positive values.
	MultiplicativeError
)

type Ets added in v0.2.0

type Ets struct {
	Error  ErrorKind
	Trend  Trend
	Season Season
}

Ets is one member of the exponential smoothing family in state space form (Hyndman, Koehler, Snyder & Grose, 2002): an error, a trend and a seasonal component.

Simple exponential smoothing is Ets{}, Holt's linear method Ets{Trend: AdditiveTrend}, the multiplicative Holt-Winters method Ets{MultiplicativeError, AdditiveTrend, MultiplicativeSeason}. The smoothing parameters and the initial states are estimated together by maximum likelihood.

func EtsCandidates added in v0.2.0

func EtsCandidates(period int, positive bool) []Ets

EtsCandidates returns the models considered for a series of the given period.

func EtsFromCode added in v0.2.0

func EtsFromCode(code string) (Ets, bool)

EtsFromCode returns the model of the usual three-letter code, error–trend–season, with Ad for a damped trend: "ANN", "AAdN", "MAM"… It reports false for anything else.

func (Ets) Code added in v0.2.0

func (e Ets) Code() string

Code returns the three-letter code of the model.

func (Ets) Description added in v0.2.0

func (e Ets) Description() string

func (Ets) Estimate added in v0.2.0

func (e Ets) Estimate(y Series) (*EtsFit, error)

Estimate fits the model and returns everything that was estimated.

func (Ets) Fit added in v0.2.0

func (e Ets) Fit(y Series) (Fitted, error)

func (Ets) Name added in v0.2.0

func (e Ets) Name() string

type EtsFit added in v0.2.0

type EtsFit struct {

	// Alpha is the smoothing of the level.
	Alpha float64
	// Beta is the smoothing of the trend, Gamma of the seasonal pattern and
	// Phi the damping of the trend; they are NaN when the model has no such
	// part.
	Beta, Gamma, Phi float64

	// Sigma2 is the variance of the errors (relative errors for
	// multiplicative models).
	Sigma2 float64
	// LogLikelihood is the log-likelihood up to the usual constant, as
	// reported by ets in R.
	LogLikelihood float64
	AIC           float64
	AICc          float64
	BIC           float64
	// Residuals are the errors of the one-step forecasts (relative for
	// multiplicative models).
	Residuals []float64
	// Fitted are the one-step forecasts of the history.
	Fitted []float64
	// contains filtered or unexported fields
}

EtsFit is an estimated ETS model.

func (*EtsFit) Forecast added in v0.2.0

func (f *EtsFit) Forecast(h int) []float64

func (*EtsFit) InitialLevelAndTrend added in v0.2.0

func (f *EtsFit) InitialLevelAndTrend() (level, trend float64)

InitialLevelAndTrend returns the level and the trend before the first observation.

func (*EtsFit) InitialSeasonal added in v0.2.0

func (f *EtsFit) InitialSeasonal() []float64

InitialSeasonal returns the seasonal pattern before the first observation, by position in the cycle (position 0 is the first observation).

func (*EtsFit) LevelAndTrend added in v0.2.0

func (f *EtsFit) LevelAndTrend() (level, trend float64)

LevelAndTrend returns the level and the trend after the last observation.

func (*EtsFit) Model added in v0.2.0

func (f *EtsFit) Model() Ets

Model returns the model that was fitted.

func (*EtsFit) Params added in v0.2.0

func (f *EtsFit) Params() []Param

type Event added in v0.2.0

type Event struct {
	Name string
	// Positions are where a recurring event happens, in the ORIGINAL data
	// (see [Series.Index]), past and future: one effect is estimated for all
	// its occurrences. Positions beyond the history are how the forecast
	// knows the event is coming.
	Positions []int
	// Step makes the event a lasting change of level, from StepFrom on, such
	// as a change in the law.
	Step     bool
	StepFrom int
}

Event is something dated that moves the series.

type Fitted

type Fitted interface {
	// Forecast returns point forecasts for the h periods after the last
	// observation.
	Forecast(h int) []float64
	// Params returns the estimated parameters.
	Params() []Param
}

Fitted is a model estimated on a series.

type HoltWinters

type HoltWinters struct{}

HoltWinters is exponential smoothing with level, damped additive trend (φ) and multiplicative seasonal indices.

α, β, γ and φ come from a grid search (3,040 combinations) minimising the sum of squared relative one-step-ahead errors on the training data, the first cycle left out as warm-up. It needs a seasonal period of at least 2, positive values and three full cycles.

func (HoltWinters) Description

func (HoltWinters) Description() string

func (HoltWinters) Fit

func (HoltWinters) Fit(y Series) (Fitted, error)

func (HoltWinters) Name

func (HoltWinters) Name() string

type HorizonStats

type HorizonStats struct {
	// N is the number of forecast/actual pairs evaluated.
	N int
	// MAPE is the mean absolute percentage error, in percent.
	MAPE float64
	// Bias is the mean of (forecast − actual)/actual, in percent; positive
	// when the forecasts ran high.
	Bias float64
	MAE  float64
	RMSE float64
	// MASE is the mean absolute error scaled by the in-sample seasonal naive
	// error of each training set.
	MASE float64
	// Bands are the quantiles of the relative error at this horizon, one per
	// level.
	Bands []Band
	// Cumulative are the quantiles of the relative error of the SUM of the
	// first h periods.
	Cumulative []Band
}

HorizonStats is what the backtest measured at one horizon. Measures that could not be computed are NaN.

type Interval

type Interval struct {
	Level, Lower, Upper float64
}

Interval is an interval around a forecast.

type LogLinear

type LogLinear struct {
	// Window is the number of most recent observations used in the fit.
	// Zero means six cycles, at least 24: enough to catch the recent trend
	// without dragging old breaks along.
	Window int
	// Deflator is a price index aligned with the ORIGINAL data: Deflator[i]
	// deflates the observation whose position is i (see [Series.Index]).
	// Only positions inside the training data are read, so handing the full
	// index to a backtest does not leak the future.
	Deflator []float64
}

LogLinear is a log-linear regression: log(y/I) = a + b·t + seasonal dummies, by least squares over the last Window observations, where I is a price index (1 without a deflator).

The back-transformation is corrected for bias with Duan's smearing factor (the mean of exp(residual)). With a deflator, real forecasts are re-inflated by the index growth over the last cycle, held constant over the horizon. It needs positive values and three full cycles.

func (LogLinear) Description

func (l LogLinear) Description() string

func (LogLinear) Fit

func (l LogLinear) Fit(y Series) (Fitted, error)

func (LogLinear) Name

func (l LogLinear) Name() string

type Mean

type Mean struct{}

Mean forecasts the mean of the history at every horizon.

func (Mean) Description

func (Mean) Description() string

func (Mean) Fit

func (Mean) Fit(y Series) (Fitted, error)

func (Mean) Name

func (Mean) Name() string

type Metric

type Metric int

Metric is the error measure averaged over the horizons to rank the candidates.

const (
	// RankByMAPE ranks by mean absolute percentage error (the default).
	RankByMAPE Metric = iota
	// RankByMAE ranks by mean absolute error.
	RankByMAE
	// RankByRMSE ranks by root mean squared error.
	RankByRMSE
	// RankByMASE ranks by mean absolute scaled error.
	RankByMASE
)

type Model

type Model interface {
	// Name is a short identifier, such as "holt_winters".
	Name() string
	// Description is a one-line description of the method.
	Description() string
	// Fit estimates the model. It returns an error when the series is too
	// short or otherwise unsuitable.
	Fit(y Series) (Fitted, error)
}

Model is a forecasting method, before seeing any data.

Fitting is separate from forecasting so that a fitted model can be inspected and asked for any horizon without being estimated again. Models are plain configuration and safe to share between goroutines.

type Naive

type Naive struct{}

Naive forecasts the last observation at every horizon (a random walk).

func (Naive) Description

func (Naive) Description() string

func (Naive) Fit

func (Naive) Fit(y Series) (Fitted, error)

func (Naive) Name

func (Naive) Name() string

type Param

type Param struct {
	Name  string
	Value float64
}

Param is a named number estimated by a model, for display and audit trails.

type Point

type Point struct {
	// Horizon is the number of periods ahead (for a cumulative forecast, how
	// many were added up).
	Horizon   int
	Mean      float64
	Intervals []Interval
}

Point is a forecast with its intervals.

func (Point) Interval

func (p Point) Interval(level float64) (Interval, bool)

Interval returns the interval with the given coverage, if it was asked for.

type Prophet added in v0.2.0

type Prophet struct {
	// Changepoints is the number of candidate changepoints (default 25).
	// Use NoChangepoints for a straight line.
	Changepoints int
	// NoChangepoints fits a straight line.
	NoChangepoints bool
	// ChangepointRange is the share of the history, from the start, where
	// changepoints may fall (default 0.8).
	ChangepointRange float64
	// ChangepointPriorScale is the flexibility of the trend: larger values
	// let it bend more (default 0.05).
	ChangepointPriorScale float64
	// SeasonalityPriorScale is the flexibility of the seasonal pattern
	// (default 10).
	SeasonalityPriorScale float64
	// FourierOrder is the number of harmonics of the seasonal pattern
	// (default 10, or half the period when that is less, which describes any
	// pattern of that period). Use NoSeasonality for none.
	FourierOrder int
	// NoSeasonality leaves the seasonal pattern out.
	NoSeasonality bool
	// EventPriorScale is the flexibility of the effects of events
	// (default 10).
	EventPriorScale float64
	// Events are recurring events and lasting changes of level.
	Events []Event
}

Prophet is the Prophet model (Taylor & Letham, 2018): a piecewise linear trend that may bend at changepoints, a Fourier seasonal pattern and dated events, fitted by maximum a posteriori.

y(t) = g(t) + s(t) + events(t) + noise, where g is a line whose slope may change at each of a set of candidate changepoints spread over the first part of the history. The changes have a Laplace prior, so most of them come out as exactly zero and the trend bends only where the data insists; the seasonal and event coefficients have normal priors.

What is and is not here, compared with the original: observations are equally spaced and the seasonal period is the one of the series; growth is linear and the components are additive (for multiplicative behaviour, fit on the log scale with Log); events are given by position instead of by calendar. The estimate is the exact optimum of the same posterior, found without Stan.

The zero value has the defaults of Prophet; see DefaultProphet.

Example

A trend that bends, with an event that recurs and is known ahead.

package main

import (
	"fmt"

	foresight "github.com/milkway/foresight-go"
)

// Eight years of monthly data with growth, a yearly pattern and some noise.
func sales() []float64 {
	pattern := []float64{1.1, 0.8, 0.9, 1.0, 1.0, 0.9, 1.0, 1.0, 0.9, 1.0, 1.1, 1.3}
	out := make([]float64, 96)
	level := 100.0
	for t := range out {
		noise := float64((t*37)%11-5) * 1.5
		out[t] = level*pattern[t%12] + noise
		level += 0.5
	}
	return out
}

func main() {
	y := sales()
	for _, t := range []int{5, 29, 53, 77} {
		y[t] += 30 // a campaign every other June
	}
	model := foresight.Prophet{Events: []foresight.Event{
		{Name: "campaign", Positions: []int{5, 29, 53, 77, 101}},
	}}
	fit, err := model.Estimate(foresight.Monthly(y, 0))
	if err != nil {
		fmt.Println(err)
		return
	}
	for _, e := range fit.Effects() {
		fmt.Printf("%s: %+.0f\n", e.Name, e.Value)
	}
}
Output:
campaign: +29

func DefaultProphet added in v0.2.0

func DefaultProphet() Prophet

DefaultProphet returns the defaults of Prophet spelled out: 25 candidate changepoints over the first 80% of the history with prior scale 0.05, seasonal and event prior scales of 10.

func (Prophet) Description added in v0.2.0

func (Prophet) Description() string

func (Prophet) Estimate added in v0.2.0

func (p Prophet) Estimate(y Series) (*ProphetFit, error)

Estimate fits the model and returns everything that was estimated.

func (Prophet) Fit added in v0.2.0

func (p Prophet) Fit(y Series) (Fitted, error)

func (Prophet) Name added in v0.2.0

func (Prophet) Name() string

type ProphetFit added in v0.2.0

type ProphetFit struct {
	// contains filtered or unexported fields
}

ProphetFit is an estimated Prophet model.

func (*ProphetFit) Changepoints added in v0.2.0

func (f *ProphetFit) Changepoints() []Changepoint

Changepoints returns the changepoints where the trend did bend.

func (*ProphetFit) Effects added in v0.2.0

func (f *ProphetFit) Effects() []Param

Effects returns the estimated effect of each event, in units of the series.

func (*ProphetFit) Events added in v0.2.0

func (f *ProphetFit) Events(i int) float64

Events returns the effect of the events at the relative position i.

func (*ProphetFit) FittedValues added in v0.2.0

func (f *ProphetFit) FittedValues() []float64

FittedValues returns the values fitted to the history.

func (*ProphetFit) Forecast added in v0.2.0

func (f *ProphetFit) Forecast(h int) []float64

func (*ProphetFit) Params added in v0.2.0

func (f *ProphetFit) Params() []Param

func (*ProphetFit) Seasonal added in v0.2.0

func (f *ProphetFit) Seasonal(i int) float64

Seasonal returns the seasonal effect at the relative position i.

func (*ProphetFit) Sigma added in v0.2.0

func (f *ProphetFit) Sigma() float64

Sigma returns the standard deviation of the noise, in units of the series.

func (*ProphetFit) Trend added in v0.2.0

func (f *ProphetFit) Trend(i int) float64

Trend returns the trend at the relative position i (0 is the first observation; the length of the history and beyond are the future).

type Regressors added in v0.2.0

type Regressors struct {
	// contains filtered or unexported fields
}

Regressors are named columns of values aligned with the ORIGINAL data: row i belongs to the observation whose position is i (see Series.Index). The rows go on past the end of the history, because a forecast needs the values of the regressors over the horizon.

The zero value has no columns. The methods return a new value and leave the receiver as it was.

func Fourier added in v0.2.0

func Fourier(period float64, order, rows int) Regressors

Fourier returns sines and cosines of the first order harmonics of a cycle of the given period, which need not be a whole number, for rows positions: a smooth seasonal pattern with few coefficients. The sine that is zero everywhere (the harmonic at half a whole period) is left out.

func SeasonalDummies added in v0.2.0

func SeasonalDummies(period, rows int) Regressors

SeasonalDummies returns one indicator for each position of the cycle but the first.

func (Regressors) And added in v0.2.0

func (r Regressors) And(other Regressors) Regressors

And returns the regressors with the columns of another set added.

func (Regressors) Columns added in v0.2.0

func (r Regressors) Columns() [][]float64

Columns returns the columns. They are shared, not copied.

func (Regressors) Covers added in v0.2.0

func (r Regressors) Covers(end int) bool

Covers reports whether there are rows for the positions 0 to end−1 and all of them are finite.

func (Regressors) Names added in v0.2.0

func (r Regressors) Names() []string

Names returns the names of the columns.

func (Regressors) Rows added in v0.2.0

func (r Regressors) Rows() int

Rows returns the number of rows that every column has.

func (Regressors) Width added in v0.2.0

func (r Regressors) Width() int

Width returns the number of columns.

func (Regressors) With added in v0.2.0

func (r Regressors) With(name string, values []float64) Regressors

With returns the regressors with one more column.

type Report

type Report struct {
	// Candidates are the models in the order given, then the average of the
	// best ones.
	Candidates []CandidateReport
	// Chosen is the index of the chosen candidate.
	Chosen int
	// Origins is the number of origins actually used.
	Origins int
	// FirstOrigin is the position in the series of the first period
	// forecast in the backtest.
	FirstOrigin int
	Horizon     int
	Metric      Metric
}

Report is the result of a backtest.

func (*Report) Best

func (r *Report) Best() *CandidateReport

Best returns the chosen candidate.

func (*Report) Candidate

func (r *Report) Candidate(name string) (*CandidateReport, bool)

Candidate looks a candidate up by name.

type Season added in v0.2.0

type Season int

Season is the seasonal component of an ETS model.

const (
	NoSeason Season = iota
	AdditiveSeason
	// MultiplicativeSeason needs positive values.
	MultiplicativeSeason
)

type SeasonalNaive

type SeasonalNaive struct {
	// Growth scales the last cycle by the growth between the last two.
	Growth bool
}

SeasonalNaive forecasts the same season of the last cycle, optionally scaled by recent growth.

With Growth, ŷ(T+k) = y(T+k−m) × g, where g is the sum of the last cycle over the sum of the one before; forecasts beyond one cycle compound g.

func (SeasonalNaive) Description

func (s SeasonalNaive) Description() string

func (SeasonalNaive) Fit

func (s SeasonalNaive) Fit(y Series) (Fitted, error)

func (SeasonalNaive) Name

func (s SeasonalNaive) Name() string

type Series

type Series struct {
	// contains filtered or unexported fields
}

Series is a view over regularly spaced observations, oldest first.

Besides the values, a series knows its seasonal period (12 for monthly data with a yearly cycle, 4 for quarterly, 1 for none) and where it sits in the original data: slicing keeps the absolute position, so the season of each observation and the alignment with external data (a price index, a regressor) survive any Head, Tail or Slice.

The values are not copied: a Series shares them with the slice it was made from.

func Monthly

func Monthly(values []float64, firstMonth int) Series

Monthly returns a series of monthly data with a yearly cycle; firstMonth is the calendar month of the first observation, 0 for January.

func NewSeries

func NewSeries(values []float64, period int) Series

NewSeries returns a series with the given seasonal period; a period under 1 is read as 1 (no seasonality). The first observation is taken to be at season 0; see Series.WithPhase.

func NonSeasonal

func NonSeasonal(values []float64) Series

NonSeasonal returns a series without seasonality.

func Quarterly

func Quarterly(values []float64, firstQuarter int) Series

Quarterly returns a series of quarterly data with a yearly cycle; firstQuarter is 0 for the first quarter.

func (Series) Head

func (s Series) Head(n int) Series

Head returns the first n observations (all of them if n is larger).

func (Series) Index

func (s Series) Index(i int) int

Index returns the position in the original data of observation i, which may be past the end: Len() is the first period to be forecast.

func (Series) IsFinite

func (s Series) IsFinite() bool

IsFinite reports whether every value is a finite number.

func (Series) IsPositive

func (s Series) IsPositive() bool

IsPositive reports whether every value is finite and greater than zero, which is what multiplicative and logarithmic models need.

func (Series) Len

func (s Series) Len() int

Len returns the number of observations.

func (Series) Period

func (s Series) Period() int

Period returns the seasonal period.

func (Series) Season

func (s Series) Season(i int) int

Season returns the season (0 to Period()-1) of observation i, which, like in Index, may be past the end.

func (Series) Slice

func (s Series) Slice(from, to int) Series

Slice returns the observations from position from up to, but not including, position to, clamped to the series.

func (Series) Start

func (s Series) Start() int

Start returns the position, in the original data, of the first observation.

func (Series) Tail

func (s Series) Tail(n int) Series

Tail returns the last n observations (all of them if n is larger).

func (Series) Values

func (s Series) Values() []float64

Values returns the observations. The slice is shared, not copied.

func (Series) WithPhase

func (s Series) WithPhase(phase int) Series

WithPhase sets the season of the first observation of this view.

func (Series) WithValues

func (s Series) WithValues(values []float64) (Series, error)

WithValues returns the same series (period, season and position) over other values, such as a transformation of the original ones.

type Theta

type Theta struct{}

Theta is the Theta method (Assimakopoulos & Nikolopoulos, 2000), in the form shown by Hyndman & Billah (2003) to be simple exponential smoothing with drift: the smoothed level plus half the slope of the linear trend.

Seasonal series are first tested for seasonality (autocorrelation at the seasonal lag, 90% level); when the test passes and the values are positive, the series is seasonally adjusted by classical multiplicative decomposition and the forecasts are re-seasonalised. The smoothing parameter and the initial level minimise the sum of squared one-step errors.

Example

One model on its own.

package main

import (
	"fmt"

	foresight "github.com/milkway/foresight-go"
)

func main() {
	y := []float64{12, 14, 13, 15, 17, 16, 18, 20}
	fit, err := foresight.Theta{}.Fit(foresight.NonSeasonal(y))
	if err != nil {
		fmt.Println(err)
		return
	}
	for _, v := range fit.Forecast(3) {
		fmt.Printf("%.2f\n", v)
	}
}
Output:
20.52
21.04
21.55

func (Theta) Description

func (Theta) Description() string

func (Theta) Fit

func (Theta) Fit(y Series) (Fitted, error)

func (Theta) Name

func (Theta) Name() string

type Transformed added in v0.2.0

type Transformed struct {
	Model Model
	// Transform is the transformation, unless Automatic.
	Transform BoxCox
	// Automatic chooses λ by [Guerrero] at each fit.
	Automatic bool
}

Transformed runs a model on Box-Cox transformed values and brings the forecasts back to the original scale. The series must be positive.

Use Log, WithBoxCox or WithGuerrero to make one.

func Log added in v0.2.0

func Log(m Model) Transformed

Log returns the model on the log scale.

func WithBoxCox added in v0.2.0

func WithBoxCox(m Model, lambda float64) Transformed

WithBoxCox returns the model on the Box-Cox scale with the given λ.

func WithGuerrero added in v0.2.0

func WithGuerrero(m Model) Transformed

WithGuerrero returns the model on the Box-Cox scale with λ chosen by Guerrero at each fit.

func (Transformed) Description added in v0.2.0

func (t Transformed) Description() string

func (Transformed) Fit added in v0.2.0

func (t Transformed) Fit(y Series) (Fitted, error)

func (Transformed) Name added in v0.2.0

func (t Transformed) Name() string

type Trend added in v0.2.0

type Trend int

Trend is the trend component of an ETS model.

const (
	NoTrend Trend = iota
	AdditiveTrend
	// DampedTrend is additive, flattening out over the horizon.
	DampedTrend
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL