The config is a .yaml file carrying the channel- and platform-level mappings, the transformation parameters with their search grids, and the bounds used from channel down to campaign level. It is the single steering file for a cell's model runs - module 1.5 covered why the parameters mean what they mean; this module is the practitioner's "how do I actually build one".
Per the Config Creation sheet, each brand/cell generally gets four config files: ST unconstrained, ST constrained, LT unconstrained, LT constrained - kept separate so a run can be revisited or re-iterated cleanly when something goes wrong. In practice you rarely write one from scratch: you copy the most recent config and modify it. That habit is efficient and dangerous in equal measure - the pitfalls list below is mostly a catalogue of what copied configs silently carry over.
The top-level keys cluster into six working groups:
| Group | Keys | Controls |
|---|---|---|
Scope & grain | year1, year2, date_col, period, group_cols, segment_col, train_split | Which two modelling years, weekly grain (period 52) vs monthly (12), and the composite key defining one-model-per-combination (e.g. Company + Brand + Model) |
Variable rosters | variables, distribution, promo, price, base_internal, base_external, base_list_lt_l2, exclude_Scaling, include_Scaling_LT | Which exact columns belong to each variable group, the base regressor set, and scaling opt-outs. Media columns are resolved via platform keywords instead of a list |
Targets | target_col, sales_col, target_col_l2, target_col_l3 | The dependent variable per level - the L1 volume column, the value column for conversions, and the contribution-chained L2/L3 targets |
Transforms & grids | transform_mapping, platform_cat_parameter_ranges, lambda_range | Which groups receive which derived transforms, and the per-platform adstock/saturation candidate grids |
Constraints & special handling | neg_col, halo_column_keyword | Sign constraints and halo-column identification |
Model selection | priority_order | The ranked diagnostics that decide which candidate model wins |
Some configs in the family also carry platform_categories_no_keywords (keyword exclusions per category), no_l2_list, use_spends, spend_keywords / impression_keywords, and the bounds dictionaries (media_bounds, Platform_bounds, Campaign_bounds, plus the LT variants with per-pillar bounds). Their absence from a given file is valid - do not "fix" a config by adding keys it never had.
Three derived transforms are mapped to variable groups - never to individual columns:
salience→ applied tomediaanddistribution(a share-of-presence style transform)meaningful→ applied tomediaonlydifferentiation→ applied tomediaandprice(relative-to-competition, e.g. price vs comp average)
The mapping resolves through the group-to-column lists (distribution:, price:, media via platform keywords). Consequence worth tattooing somewhere: adding a column to a group implicitly enrols it in every transform that names that group. Drop a new column into price: and it silently starts receiving the differentiation transform.
The production config carries 19 platform categories, each with an alpha_range (adstock decay) and a beta_range (Hill saturation shape): radio, digital_audio, digital_av, digital_display, digital_native, digital_video, digital_youtube, a brand-specific youtube split-out, digital_partnerships, a campaign-specific partnership split-out, ecomm_display, ecomm_search, ooh, paid_social, a brand-specific social split-out, print, search, tv, and the brand's halo platform. A header comment in the file states that the alpha/beta chosen at L1 are retained in L2 and L3 modelling - the grid search happens once.
Two structural facts that correct an older framing you may have met in module 1.5's early curriculum draft:
- Despite the name, these are NOT [low, high] bounds. Each "range" is a discrete candidate grid - the tool only ever tries the listed values. A value between two grid points is never searched.
- The beta grid is identical across every platform in this config (~14 candidates spanning below 1 to well above 1, so concave and S-shaped saturation are both searchable). All cross-platform differentiation is carried by alpha: TV gets the highest, widest, finest decay grid; video/AV/OOH sit in an upper-middle band; display/native/print/radio in the middle; search, ecomm and paid social get coarse fast-decay grids. Broadcast brand-building media decay slowly, performance/intent media decay fast.
Different config blocks interpret their strings differently, and mixing the two semantics is the classic silent failure:
- Keyword-matched blocks: the platform-category names and
halo_column_keywordare matched as substrings against ADS column names. - Exact-name blocks:
neg_col,exclude_Scaling,base_internal, and the group lists must match column names verbatim - every character, including whitespace, casing, and underscores.
A keyword fragment placed in an exact-name list (or vice versa) simply matches nothing - no error, no warning, no constraint applied.
neg_col holds the variables constrained to negative coefficients: own price, promo price index, inflation, and competitor media spends. The competitor columns follow the source__platform__metric double-underscore convention - e.g. competitor_media_spends_data__tv__spend. Economically the block reads as: own price up, sales down; inflation up, sales down; competitor spend up, own sales down. There is no pos_col - media positivity is enforced by the tool by default.
halo_column_keyword identifies halo media columns (a keyword of the form digital_<brand>_halo) for dropping from the focal model's decomposition. Note the double-entry: the same halo token also appears as a platform category with its own parameter grids - the halo media is parameterised in the pipeline even though it is excluded here. Removing one entry without the other changes behaviour in non-obvious ways.
priority_order is the ranked list of diagnostics the tool uses to sort and select among candidate models - remember from module 2.1 that each level fits many candidates across the alpha/beta grid. The production sequence, exactly:
The list mixes lower-is-better and higher-is-better metrics; the tool encodes each metric's direction internally. What you must internalize: reordering this list changes which model gets selected. It is not documentation - it is a control.
The multi-level chain is wired through the target keys, and it is off by one on purpose: target_col names the L1 dependent variable (a sales-volume column); target_col_l2 points at Contribution_L1; target_col_l3 points at Contribution_L2. The dependent variable of level N is the contribution output of level N-1 - exactly the decomposition chain from module 2.1. Analysts routinely mis-set these by matching the level numbers (target_col_l2: Contribution_L2 looks right and is wrong).
The skeleton below shows the structure with a representative subset of keys. Every value is fabricated for teaching - grids, years, splits and column names alike. Never copy any of it into a real config; the real exhibit is referenced at the end of this module.
# ALL VALUES FABRICATED - structure illustration only
transform_mapping:
salience: [media, distribution]
meaningful: [media]
differentiation: [media, price]
year1: 2021
year2: 2022
halo_column_keyword: ['digital_brandx_halo']
neg_col:
- PRICE_PER_SALES_VOLUME
- PROMO_PRICE_INDEX
- inflation
- competitor_media_spends_data__tv__spend
- competitor_media_spends_data__ooh__spend
variables: [media, distribution, promo, price]
date_col: 'Date'
group_cols: [Company, Brand, Model]
segment_col: 'Segment'
distribution: [TDP_ABS, TDP_PROMO_ABS]
promo: [] # declared but empty - the empty list must stay
price: [PRICE_PER_SALES_VOLUME, PROMO_PRICE_INDEX]
base_internal: [Inflation, CONSUMER_CONFIDENCE_INDEX,
PRICE_PER_SALES_VOLUME, TDP_ABS,
competitor_media_spends_data__tv__spend]
base_external: [seasonality_stl]
base_list_lt_l2: [TDP_ABS, TDP_PROMO_ABS,
PRICE_PER_SALES_VOLUME, PROMO_PRICE_INDEX]
period: 52
train_split: 0.9
target_col: sales_volume_kgs
sales_col: sales_value
priority_order: [mape, mape_test, adj_r_squared, r_squared,
dw_stat, shapiro_stat, shapiro_p_value,
high_vif_count, low_t_stat_count, bp_stat, bp_pval]
target_col_l2: Contribution_L1 # level N models level N-1's output
target_col_l3: Contribution_L2
platform_cat_parameter_ranges:
tv: # slowest decay, finest grid
alpha_range: [0.55, 0.60, 0.65, 0.68, 0.70, 0.72, 0.75, 0.80]
beta_range: [0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.8]
digital_video: # upper-middle band
alpha_range: [0.45, 0.48, 0.50, 0.52, 0.55]
beta_range: [0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.8]
search: # fast decay, coarse grid
alpha_range: [0.10, 0.20, 0.30]
beta_range: [0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.8]
Note in passing how the fabricated grids still teach the real pattern: alpha bands differ by platform (tv > digital_video > search), beta is one shared grid, and the promo group is declared with an empty list rather than removed.
- "range" is not a range. Grids are discrete; a value between grid points is never searched. The real TV grid contains several many-decimal values - previously fitted optima pinned back into the grid. Copying the config to a new market silently carries those stale pins.
- Key casing is inconsistent and exact-match.
exclude_Scalingandinclude_Scaling_LTcarry a capital S while everything else is lower snake_case. Writingexclude_scalingis silently ignored - YAML enforces no schema here. - Keyword shadowing. Generic and brand-specific categories coexist (
digital_youtubevs the brand-specific youtube category;paid_socialvs the brand-specific social one). A column containing the brand token also contains the generic token as a substring, so matching order/specificity decides which parameter grid it receives. The Config Creation sheet's own worked case is a retailer whose name is a prefix of two sibling retailers' names - the fix applied there was renaming the ADS columns to collision-free abbreviations before configuring. Longest-match or careful naming is not optional. - Verbatim column-name fragility. Exact-name lists must match names containing spaces, commas and currency symbols character-for-character; near-duplicate variants of the same driver (differently prefixed income columns,
inflationlowercase in one list and capitalised in another) must each be matched verbatim. One wrong character = silent no-op. - Cross-list consistency is manual.
base_list_lt_l2duplicates the distribution + price lists by hand; adding a column toprice:without updating it (orexclude_Scaling) creates silent divergence between the L1 and LT models. - Comments are load-bearing. Commented-out blocks record tool defaults and previous settings (an older lambda shape, an alternative train split) - read them, never trust them as active config, and remember the Config Creation sheet's final check: save the file.
The real exhibit
The production artifact this module describes is Onboarding Docs\mmx_tool_config_UK_knorr_new_iteration-latest May12.yaml, paired 1:1 with the methodology workbook's Config Creation sheet. Open it alongside this module - reference only: it contains real client parameter values and brand tokens, so study the structure and never lift values into your own configs or notes.
digital_youtube). A brand-tokened ADS column arrives. What is the risk?priority_order actually control?- Course build notes
yaml_config.md- structural documentation of the real production config (key inventory, transform_mapping, the 19 platform categories, alpha band structure, shared beta grid, neg_col convention, priority_order sequence, target chain, pitfalls list); all numeric values in that source are described directionally only, and every number in this module's YAML block is fabricated MathCo Methodology Understanding_UL.xlsxsheetConfig Creation(four-configs-per-cell practice, platform_categories keyword mapping, the column-name collision case and its rename fix, parameter dictionary, Quick Checks)- Real exhibit (reference only, no values lifted):
Onboarding Docs\mmx_tool_config_UK_knorr_new_iteration-latest May12.yaml