// Case study / Marketing analytics
The identity layer that made the budget argument defensible.
Stanley Steemer's channels sat in disconnected systems with no consistent way to compare cost-efficiency, and address churn was corrupting any single customer view. Before a media mix model could mean anything, the business needed one resolved customer identity to hang channel-level cost and response data on. This case is the identity layer — and the regression model that sat on top of it.
What happened
The problem was defined before the model was built — and the problem was identity.
Stanley Steemer's marketing ran across GA4, Google Ads, Facebook Ads, email via Bronto, direct mail, and Valpak shared mail — each in its own system, with no consistent way to compare cost-efficiency across them. Worse, address churn — moves, marriages, household changes — meant the same customer appeared as several different people, so any channel comparison built on raw contact records would have been an argument about noise.
I built the identity-resolution layer first, then the model on top of it: a regression base for addressable channels and a deliberate, stakeholder-agreed weighted-allocation layer for passive channels that genuinely cannot be measured with response-level attribution. The result changed real budget decisions.
Context
Six channels, six systems, no common customer.
Each channel reported its own performance in its own currency — clicks, opens, response cards, coupon redemptions. Comparing a click to a piece of mail requires both to resolve to the same customer and the same household. Address churn was also driving real mail waste: without reliable householding, the business was paying to mail people who had moved, remarried, or already existed elsewhere in the file under a different record.
Task
One resolved identity, then one model both regimes could share.
Build the customer-identity layer the model would depend on, integrate household-penetration context, and deliver a media mix model that let leadership compare every channel's spend on the same economics — while being explicit about which channels could carry full response attribution and which could not. The model was built bottom-up and response-level rather than as an aggregate time-series MMM — a design choice: the resolved identity joins gave individual-level response data that aggregate MMMs exist to approximate, so modelling at that level used the stronger data directly.
Automated multi-attribute MD5 hashing per customer record, including gender-aware and nickname-normalised fallbacks, with data-quality validation at the ingestion boundary.
Connected components resolved via union-find merged every contact sharing any candidate hash into one master identity per customer.
US Census household data joined via a custom ZIP-to-ZCTA mapping, measuring household penetration by ZCTA.
Up to 13 distinct hash types per customer record — name, email, phone, and address combinations, gender-aware fallbacks for surname changes, and nickname normalisation so "Robert", "Bob", and "Bobby" resolve as one person. Email, phone, and name validation ran at the same ingestion boundary.
Only hashes shared by two or more contacts entered the graph as candidate matches. Union-find then resolved the connected components, pulling every transitively linked contact into one cluster, each assigned a single master identity. USPS change-of-address records and household changes folded in as matching signals rather than items for manual review.
A custom ZIP-to-ZCTA mapping joined US Census household data to the resolved identities — Knime profiled and cleaned the Census dataset before it entered the pipeline — so the model could estimate what share of an area's households each channel had already reached.
A linear regression base model, prototyped in R against the SQL Server estate the warehouse already ran on, attributed conversion across addressable channels. Passive channels — Valpak shared mail, radio — got a separate, stakeholder-agreed weighted allocation instead: with no click, open, or individual exposure signal, forcing them into response attribution would have been the less rigorous choice.
Action
The model replaced a hand-built attribution pipeline.
Before GA4's BigQuery export existed, channel attribution here ran on Python scripts querying the Universal Analytics Reporting API, with custom GA dimensions — session ID, user ID, zip code, Google and Bing click IDs — injected as cross-system join keys into a daily warehouse extract. Because those joins already existed in the warehouse, the identity layer was engineered to sit on them rather than bought as a black box. Every pipeline run was registered under a batch-audit framework that tracked source, inserted, and unprocessed record counts per workflow run — including failure paths — so a bad run was visible before it reached the model.
Outcome
Personalisation had no measurable effect on response. Changing the offer did.
- Mail personalisation — image and content choices — showed no measurable effect on response. Changing the offer itself did. A falsifiable negative result from the direct-mail testing programme the model ran alongside — it redirected testing effort from creative variants to offers. Direct mail briefs changed from that point forward.
- Budget reallocation followed: Valpak shared mail outperformed paid social, and email outperformed direct mail, on cost-per-piece against tracked conversions measured from resolved identities rather than derived from the allocation weights.
- Householding cut mail waste at the source — resolved identities meant the file stopped paying to reach the same household twice.
- The two-regime design held up in the room: being explicit about which channels carried full response attribution and which sat in the agreed allocation layer is what kept the budget conversation from being contested.
Design lesson
The upstream problem definition, not the regression, is what made the model trustworthy.
The regression was the fast part. The decision that made the model trustworthy was upstream: resolving identity before modelling, and refusing to force passive channels into a response-attribution frame they could not support. A lighter-weight method, agreed openly with stakeholders, was more defensible than false precision. The model's most useful output was a specific negative finding.