Add holiday effects

The YAML builder can prepare CSV-driven holiday effects for ordinary MMM models. Choose the representation because it matches the modelling question, rather than because a calendar variable improves in-sample fit.

ModeRepresentation
eventSmooth effects for uniquely named event rows
pooled_controlA binary any-holiday indicator
prophet_componentA frozen target-fitted Prophet component with an MMM coefficient
holidays:
  enabled: true
  mode: prophet_component
  path: holidays.csv
  countries: [UK]
  prefix: holiday

Preparing a new Prophet component requires the holidays extra. Its weights are estimated from the scaled training target, then retained without pickling a Prophet object. Loading and prediction reuse the saved profile. Because the weights were estimated in an earlier stage, their uncertainty is not fully propagated as a joint Bayesian fit. Refit this feature inside each training split; preparing it once from all outcomes leaks holdout information.

Accepted CSV schemas are name,start_date,end_date and ds,holiday,country,year. Catalogue dates are day-first; UK and GB are aliases. Aggregate models default to US when countries are omitted, so specify them explicitly. Geo panels require multiple country codes corresponding to their geo coordinates. pooled_control does not support panel dimensions; FE and CRE reject these holiday effects altogether.

A configured relative path resolves from the YAML directory. Without a path, the builder uses the packaged calendar snapshot. Check its country and year coverage rather than assuming a current universal calendar. Holiday ranges are inclusive and assigned to the model periods they overlap; model dates label period starts. The final period uses the inferred observed interval.

The canonical calendar and fitted profile are persisted, so future predictions do not depend on the original CSV remaining in place. A changed calendar is a new input requiring model review and refitting. A holiday coefficient remains conditional on the model and adjustment assumptions, not a causal effect solely because it is named after a calendar event.

Build from a small calendar

Save this illustrative calendar as sandbox/holidays.csv; its two events fall inside the Python quickstart’s training dates. Event dates are inclusive.

name,start_date,end_date
winter_event,2025-02-10,2025-02-10
spring_event,2025-04-14,2025-04-20

This continuation constructs a fresh ordinary model and attaches an event effect before building. It requires the prepared X and y from the quickstart and uses the calendar file just shown.

from pathlib import Path
from ammm.mmm import MMM, GeometricAdstock, LogisticSaturation
from ammm.mmm.builders.schema import HolidaysConfig
from ammm.mmm.builders.holidays import apply_holidays_from_config, load_holiday_calendar

calendar_path = Path("sandbox/holidays.csv").resolve()
calendar = load_holiday_calendar(calendar_path, countries=("UK",))
print(calendar.data)
print(calendar.provenance.source_sha256)
holiday_model = MMM(
    date_column="date", channel_columns=["tv", "social"],
    adstock=GeometricAdstock(l_max=1), saturation=LogisticSaturation(),
)
apply_holidays_from_config(
    holiday_model,
    HolidaysConfig(mode="event", path=str(calendar_path), countries=("UK",)),
    model_dates=X["date"],
)
holiday_model.build_model(X, y)

For YAML stored in sandbox/model.yml, the matching block is holidays: {enabled: true, mode: event, path: holidays.csv, countries: [UK]}. Check the retained resolved path, source SHA-256, input schema, effective countries and coverage years against the intended calendar; the canonical rows and provenance fields come from src/ammm/mmm/builders/holidays.py:194. No-overlap warnings indicate a zero component for the observations, not successful holiday adjustment (src/ammm/mmm/builders/holidays.py:356).

Implementation reference at 7cb7f20: src/ammm/mmm/builders/holidays.py:194, src/ammm/mmm/holidays.py:27.