Skip to content

Forecast extras : saisonnalité, points de rupture, Monte Carlo et goal-seek

Au-delà du plugin forecast(), le sous-chemin @michi-vz/insights/forecast fournit un ensemble d'aides pures et déterministes qui fonctionnent sur un simple number[] - aucun graphique requis. Chacune est une méthode nommée et classique ; pour l'histoire et les démos, voir le guide Insights.

Import

ts
import {
  decompose, detectPeriod,          // seasonality
  detectChangepoints,               // regime shifts
  monteCarloForecast,               // simulation
  requiredGrowth, requiredRunRate, pacingToGoal, // goal-seek
} from "@michi-vz/insights/forecast";

Saisonnalité : decompose / detectPeriod

La décomposition additive classique divise une série en une tendance lissée, une composante saisonnière répétitive, et le résidu restant (values = trend + seasonal + residual). detectPeriod trouve la longueur du cycle seul, par autocorrélation.

decompose · trend + season + noise
ts
const period = detectPeriod(values);            // e.g. 4 (quarterly), or 1 if not clearly periodic
const { trend, seasonal, residual } = decompose(values, period);
FonctionSignatureRetourne
detectPeriod(values: number[], maxPeriod?: number) => numberPériode dominante (décalage avec l'autocorrélation la plus élevée) ; 1 en l'absence de cycle clair. maxPeriod vaut par défaut min(floor(n/2), 24).
decompose(values: number[], period?: number) => Decomposition{ trend, seasonal, residual, period } - chacun un tableau de la longueur de values. period vaut par défaut detectPeriod(values).

Un faible écart-type résiduel signifie que la tendance et la saison expliquent presque tout le mouvement - autrement dit, comment séparer une vraie croissance de "c'est encore décembre".

Points de rupture : detectChangepoints

Trouve les indices où la pente de la tendance change structurellement (une droite des moindres carrés ordinaires est ajustée de part et d'autre de chaque scission candidate ; les maxima locaux de |slopeAfter - slopeBefore| au-dessus d'un seuil sont conservés).

changepoints · where the trend bends

Averages hide the moment a story changes. detectChangepoints() finds where the slope structurally shifts and the line is coloured by regime - here a clear peak-then-decline. Useful for "when did growth stall?" without eyeballing.

ts
const points = detectChangepoints(values, { minSegment: 3 });
// → [{ index, slopeBefore, slopeAfter, delta }, ...]
OptionTypeDéfautCe que ça fait
minSegmentnumber3Nombre minimum de points de chaque côté d'une scission (aussi l'écart minimum entre deux points de rupture).
thresholdnumberécart-type de la courbe de delta de penteForce de courbure minimale pour qu'une scission compte ; la valeur par défaut s'ajuste à la série.

Chaque Changepoint est { index, slopeBefore, slopeAfter, delta }, où index marque le début du segment "après".

Simulation : monteCarloForecast

Exécute de nombreux futurs simulés autour de la prévision ponctuelle (chaque pas perturbé par un bruit résiduel gaussien qui croît avec sqrt(step)), puis rapporte la trajectoire moyenne, une bande empirique et des probabilités de dépassement. Déterministe via un PRNG à graine - la même seed rejoue toujours le même résultat.

monteCarlo · a fan of futures

A single forecast line hides the risk. monteCarloForecast() runs hundreds of futures - each step nudged by random shocks - and reports the band (the shaded range) plus the odds of clearing a target. Deterministic via a seeded RNG, so the same seed always replays; Re-roll shows how the spread shifts.

ts
const mc = monteCarloForecast(values, { horizon: 4, runs: 500, seed: 1, level: 0.95 });
mc.predictions; // mean path (length = horizon)
mc.lower; mc.upper; // band edges per step
mc.probabilityAbove(target); // fraction of runs finishing strictly above `target`
mc.probabilityBelow(target);
OptionTypeDéfautCe que ça fait
horizonnumber(requis)Pas à simuler dans le futur.
runsnumber500Nombre de futurs simulés.
seednumber1Graine PRNG pour la reproductibilité.
levelnumber0.95Masse de probabilité centrale conservée dans la bande.
methodForecastMethod"holt-winters"Modèle utilisé pour la trajectoire centrale.

Goal-seek : requiredGrowth / requiredRunRate / pacingToGoal

L'inverse de la prévision : étant donné un objectif connu, que faut-il pour l'atteindre ?

goal-seek · what hits the target

Forecasting runs time forward; goal-seek runs it backward from a target. requiredGrowth() and requiredRunRate() are plain arithmetic - the gold dashed line is the pace you'd have to hold. Change the target and watch it move.

ts
requiredGrowth(current, target, periods);   // constant % per period (compounding) to reach target
requiredRunRate(current, target, periods);  // constant additive amount per period to reach target
const pace = pacingToGoal(cumulative, target, periodsElapsed, periodsTotal);
// pace → { projected, attainmentPct, onTrack, requiredRunRate }
FonctionSignatureRetourne
requiredGrowth(current, target, periods) => numberCroissance multiplicative par période g telle que current*(1+g)**periods === target. 0 si current <= 0 ou periods <= 0.
requiredRunRate(current, target, periods) => numberIncrément additif par période (target - current) / periods.
pacingToGoal(cumulative, target, periodsElapsed, periodsTotal) => PacingProjette le rythme actuel sur toute la fenêtre : { projected, attainmentPct, onTrack, requiredRunRate }.

Tout ceci est de l'arithmétique pure - pas de DOM, pas de dépendances, pas de hasard (sauf le Monte Carlo à graine).

guide Insights