{"schemaVersion":2,"courseId":"forecast","module":{"id":"transformations","title":"1 · Targets, transformations, and stationarity","summary":"Define the forecasting target before choosing a model.","stages":[{"type":"read","id":"forecast-transformations-guide","skillIds":["forecast.series-transformations","forecast.univariate-stationarity"],"estimatedMinutes":7,"difficulty":"core","title":"Targets, transformations, and stationarity: research guide","html":"<h2>Targets, transformations, and stationarity</h2><p class=\"lead\">Define the forecasting target before choosing a model.</p><p>Logs, differences, seasonal adjustments, and levels change the estimand; every forecast must be transformed back consistently.</p><div class=\"callout\"><b>Workflow.</b> Choose levels, logs, differences, or seasonal adjustments for the forecasting target.</div><p><b>Diagnostic:</b> Inspect units, trend, seasonality, and invertibility. The main failure to avoid is a forecast evaluated on a scale different from the decision target.</p>","prerequisiteStageIds":[]},{"type":"interactive","id":"forecast-transformations-lab","skillIds":["forecast.series-transformations"],"estimatedMinutes":6,"difficulty":"core","title":"Series transformations intuition lab","note":"Drag the Box-Cox power λ. Watch the variance of the first vs second half of the series equalise as λ→0 (the log) — the transform picks the scale on which the target is well-behaved.","params":[{"name":"lam","label":"Box-Cox power λ","min":-0.5,"max":1.5,"step":0.1,"value":0}],"template":"\nimport numpy as np, matplotlib.pyplot as plt\nrng = np.random.default_rng(0); n = 240\nt = np.arange(n)\nlevel = np.exp(0.012 * t)                       # smooth exponential trend\ny = level * np.exp(0.35 * rng.normal(0, 1, n))  # multiplicative error grows with level\nlam = {{lam}}\nz = (np.power(y, lam) - 1) / lam if abs(lam) > 1e-6 else np.log(y)\nhalf = n // 2\nv1, v2 = z[:half].var(), z[half:].var()          # variance homogeneity check\nplt.figure(figsize=(6, 3.7))\nplt.plot(t, z, color=\"#da9100\", lw=1.2)\nplt.axvline(half, color=\"#c9c6ac\", ls=\"--\", lw=1)\nplt.xlabel(\"time\"); plt.ylabel(f\"Box-Cox(y, lambda={lam:g})\")\nplt.title(f\"var(early)={v1:.3f}   var(late)={v2:.3f}   ratio={v2/v1:.2f}\")\nprint(f\"lambda={lam:g}: variance ratio (late/early) = {v2/v1:.2f}  (near 1 = stabilised)\")\nprint(\"lambda=0 (log) turns multiplicative errors additive; forecasts must be back-transformed on this scale\")\n","prerequisiteStageIds":["forecast-transformations-guide"]},{"type":"code","id":"forecast-transformations-worked","skillIds":["forecast.series-transformations"],"estimatedMinutes":10,"difficulty":"core","title":"Targets, transformations, and stationarity: worked Python model","note":"Run the reproducible example, then change one assumption and compare the output.","code":"import numpy as np\nrng = np.random.default_rng(7)\nphi = 0.72\ny = np.zeros(160)\nfor t in range(1, len(y)):\n    y[t] = phi*y[t-1] + rng.normal(scale=0.8)\nX, target = y[:-1, None], y[1:]\nphi_hat = np.linalg.lstsq(X, target, rcond=None)[0][0]\nforecast = phi_hat*y[-1]\nprint(f\"Estimated persistence: {phi_hat:.3f}\")\nprint(f\"One-step forecast: {forecast:.3f}\")","prerequisiteStageIds":["forecast-transformations-lab"]},{"type":"codechallenge","id":"forecast-transformations-challenge","skillIds":["forecast.series-transformations"],"estimatedMinutes":12,"difficulty":"core","title":"Series transformations code challenge","note":"Estimate an AR(1) coefficient by least squares and store the final one-step forecast.","starter":"import numpy as np\ny = np.array([0.2, 0.4, 0.1, 0.5, 0.7, 0.6])\nphi_hat = ...\nforecast = ...","tests":"expected = np.linalg.lstsq(y[:-1, None], y[1:], rcond=None)[0][0]\nassert np.isclose(phi_hat, expected)\nassert np.isclose(forecast, expected*y[-1])\nprint(\"grader: AR estimate and forecast are correct\")","hints":["Regress y[1:] on y[:-1] without an intercept.","The one-step forecast is phi_hat times the last observation."],"success":"The numerical result and the series transformations workflow both pass the grader.","prerequisiteStageIds":["forecast-transformations-worked"],"variantId":"forecast-transformations-challenge-core"},{"type":"case","id":"forecast-transformations-case","skillIds":["forecast.univariate-stationarity"],"estimatedMinutes":8,"difficulty":"core","title":"Univariate stationarity: model decision","note":"A team is about to report results without checking rolling moments, roots, and unit-root evidence.","steps":[{"prompt":"What is the best next action?","choices":["Distinguish deterministic trend, unit-root persistence, and stationary dynamics.","Increase decimal precision and report immediately.","Drop observations until the preferred coefficient appears."],"answer":0,"explain":"The defensible workflow is to distinguish deterministic trend, unit-root persistence, and stationary dynamics."},{"prompt":"Which risk should the final report address explicitly?","choices":["Only whether the sample mean is positive.","spurious autocorrelation and unreliable long-horizon forecasts","Whether every coefficient has the same sign."],"answer":1,"explain":"The relevant failure mode is spurious autocorrelation and unreliable long-horizon forecasts."}],"prerequisiteStageIds":["forecast-transformations-challenge"],"variantId":"forecast-transformations-case-core"},{"type":"match","id":"forecast-transformations-match","skillIds":["forecast.univariate-stationarity"],"estimatedMinutes":7,"difficulty":"core","title":"Univariate stationarity: evidence map","note":"Match each research role to the course-specific evidence.","pairs":[{"left":"Good practice","right":"Distinguish deterministic trend, unit-root persistence, and stationary dynamics."},{"left":"Diagnostic evidence","right":"rolling moments, roots, and unit-root evidence"},{"left":"Failure to guard against","right":"spurious autocorrelation and unreliable long-horizon forecasts"}],"prerequisiteStageIds":["forecast-transformations-case"],"variantId":"forecast-transformations-match-core"}]}}
