Modelling the value of marketing communications

TL;DR

Difficulty: Advanced

Platforms report attributed value, a rule they apply to themselves. Budgets need incremental value, measured against what would have happened anyway.

  • Model the trial rate on the log of contacts per channel: the first touch does most of the work.
  • The decision number is the marginal value of one more contact, the coefficient divided by one plus the contacts already made, times the value of a response.
  • Set it equal to the full cost of a contact, unsubscribe damage included, and you get the optimal frequency. Leave the damage out and the optimiser recommends spam.
  • In the example the platforms claimed 1,180 trials for a quarter with 770. A randomised holdout put retargeting at about a seventh of its claim.
  • Report the interval, not the point: the email optimum is 17 a quarter on the estimate and five to 30 on its interval.

Every platform in your marketing stack reports the conversions it caused, and added up they exceed the conversions you actually had. I show how to model what one more email, one more webinar invitation and one more retargeting impression are really worth, where the optimal contact frequency sits for each segment, why the full cost of an email includes the people it drives away, and how a randomised holdout keeps the whole thing honest, using an invented Galway SaaS. Chapter 15 of 21 in the Marketing Analytics series.

Three dashboards, three winners, one quarter

Picture the quarterly review at Vanlog, the invented Rails SaaS from Galway that plays the software business in this series. It gives plumbers, carpenters and electricians one place for their calendar, their quotes and their invoices. About 8,000 leads sit in the CRM in any quarter: tradespeople who downloaded a quoting template or sat through a demo and have not started a trial yet. Over the quarter they receive emails, webinar invitations and retargeting ads.

Then three people present. The email tool reports 430 trials that started after a click on an email. The webinar platform reports 210 trials from registrants. The retargeting platform reports 540 conversions, most of them "view through", meaning the lead saw an ad, did not click, and later started a trial.1 That adds up to 1,180 trials. The product database says 770 trials started in the quarter, and a fair number of those people would have signed up anyway.

Everyone in that room is right according to their own screen, and the total is still impossible. That is the problem this chapter solves. Not "which channel gets the credit", which is a political question, but "what is one more email, one more webinar invitation, one more retargeting impression actually worth, and when do I stop". Those are modelling questions with honest answers, and the answers usually move budget somewhere the platforms would rather you did not look.

Vanlog and every number here are made up so I can show each step. I simulated the data and ran every query and every line of code below on it, so the numbers in the text are what the code prints.

Where this sits in the series

This is chapter 15 of 21 in my Marketing Analytics series and the first of part four, which is about media and loyalty. It follows Tools of segmentation: k means, latent class analysis and going beyond RFM, and not by accident: the contact plan at the end of this chapter is set per segment, so segments of the kind built there become the rows of the plan here. The next chapter, Media mix modelling: adstock, saturation and where the next euro goes, lifts the same idea from the individual lead to the whole budget and adds memory over time. The basics used here, from the mean to regression, are built from scratch on the Foundations page.

Attributed value and incremental value are different things

Attributed value is what a platform assigns to itself using a rule. Last click is the classic one: the last clicked ad gets all the credit for the conversion.2 View through is more generous still: an ad merely shown to the lead earns credit too. Rules are not measurements. They cannot tell you what would have happened without the touch, and that is the only definition of value that survives contact with a finance director.

Incremental value is the difference between what happened with the communication and what would have happened without it, times what a response is worth. That "without it" is a counterfactual, never observed for a single lead. You estimate it in two ways that should agree. A response model across all leads gives the shape of the relationship between contacts and response. A holdout group, a random set of leads who do not get the communication, gives a clean measurement of the level. I use both, and when they disagree I trust the holdout for the level and the model for the shape. There is evidence for that order: across 15 large advertising experiments at Facebook, the usual observational methods often failed to reproduce what the randomised experiments measured.3 The general logic of proving lift is in Five ways to prove a campaign actually worked.

An example: a lead receives four emails and starts a trial. Last click hands that trial to the fourth email if the lead clicked it. The incremental question is different: of 100 leads like this one, how many would have started a trial with no email at all? If the answer is 6 and the emailed group shows 10, email made 4 of every 100 trials, not 10.

The response model for communications

The core model says that a lead's chance of starting a trial in a quarter depends on how many times each channel touched them, with diminishing returns per channel, plus an interaction between channels, plus what is known about the lead. Written out for Vanlog's three channels:

Ti=β0+βeln⁡(1+Ei)+βwln⁡(1+Wi)+βrln⁡(1+Ri)+βerln⁡(1+Ei)ln⁡(1+Ri)+γ⊤Xi+εiT_i = \beta_0 + \beta_e \ln(1+E_i) + \beta_w \ln(1+W_i) + \beta_r \ln(1+R_i) + \beta_{er} \ln(1+E_i)\ln(1+R_i) + \gamma^\top X_i + \varepsilon_i

Here TiT_i is 1 if lead ii started a trial in the quarter and 0 if not. EiE_i is the number of emails the lead received, WiW_i the number of webinar invitations and RiR_i the retargeting exposure in blocks of ten impressions, because a single impression is too fine a unit to mean anything. ln⁡\ln is the natural logarithm. β0\beta_0 is the intercept, the trial rate of a lead who received nothing. βe\beta_e, βw\beta_w and βr\beta_r are the channel effects, βer\beta_{er} is the interaction between email and retargeting, XiX_i is a vector of lead characteristics (segment, lead age, quarter) with coefficients γ\gamma, and εi\varepsilon_i is the error. Adding one inside each logarithm keeps leads with zero contacts in the data, since the logarithm of zero does not exist. Foundations: regression and Foundations: logarithms

Why the logarithm? Because a first email to a lead who has heard nothing from you in months does far more than the twelfth. The log shape encodes exactly that: each additional contact adds less than the one before, never quite nothing, and never negative. That last property is a limitation, not a feature. Real people do get fed up, and I handle fatigue on the cost side rather than by bending the curve.

Worked example: what the log does to twelve emails

Take an email coefficient of 0.0122. One email moves a lead from ln⁡1=0\ln 1 = 0 to ln⁡2=0.693\ln 2 = 0.693, so the trial probability rises by 0.0122×0.693=0.00850.0122 \times 0.693 = 0.0085, 0.85 percentage points. Twelve emails give ln⁡13=2.565\ln 13 = 2.565, so 0.0122×2.565=0.0310.0122 \times 2.565 = 0.031, 3.1 points. The first email delivered more than a quarter of what all twelve deliver together.

I have written this as a linear probability model, a regression on an outcome that is 0 or 1. Logistic regression is the more respectable choice and I fit both. The linear version keeps the marginal values readable without a calculator. On Vanlog's data the two agree closely: removing email from the last quarter costs 271 trials in the linear model and 267 in the logistic one, which you can compare with the logistic model's marginal effects.4

The number that matters is the marginal value

No coefficient on its own makes a decision. The decision number is the marginal value of one more contact: how much one more email raises the expected trial probability, times what a trial is worth.

MVE=V⋅∂ E[T]∂E=V⋅βe+βerln⁡(1+R)1+EMV_E = V \cdot \frac{\partial\, \mathbb{E}[T]}{\partial E} = V \cdot \frac{\beta_e + \beta_{er}\ln(1+R)}{1+E}

Here MVEMV_E is the money one more email is expected to bring in. VV is the value of a trial, for Vanlog the expected discounted margin a trial turns into. E[T]\mathbb{E}[T] is the expected trial probability and ∂ E[T]/∂E\partial\, \mathbb{E}[T] / \partial E is how much it rises with one more email. EE and RR are the emails and retargeting blocks the lead has already received, and βe\beta_e and βer\beta_{er} are as in the model. The 1+E1+E in the denominator is the log at work: at E=0E = 0 the whole coefficient applies, at E=9E = 9 only a tenth of it. Retargeting makes each email a little more effective, which is what the interaction term in the numerator is for. Foundations: marginal versus average

Set the marginal value equal to the cost of a contact and solve for EE. That gives the optimal frequency:

E∗=V(βe+βerln⁡(1+R))ce−1E^{*} = \frac{V \left(\beta_e + \beta_{er}\ln(1+R)\right)}{c_e} - 1

Here E∗E^{*} is the number of emails per quarter at which the next one would cost more than it brings in, cec_e is the full cost of one email to one lead, and the other symbols are as above. The full cost is defined properly further down, because it is where most frequency optimisers go wrong. Foundations: marginal versus average

Worked example: the value of a Vanlog trial

The example's assumptions: about 22% of Vanlog trials become paying accounts, and a paying account is worth about €1,360 in discounted margin across the plan mix (the survival analysis chapter shows how such a number is built, with Team accounts worth far more and Starter accounts far less). So V=0.22×1,360=€299V = 0.22 \times 1{,}360 = €299, which I round to €300 for everything that follows.

Vanlog's three channels, one curve each

I fitted the model on four quarters, July 2025 to June 2026, with one row per live lead per quarter: 32,000 rows. Four quarters is not a luxury: with a 0 or 1 outcome at a 10% rate, one quarter of 8,000 leads gives standard errors twice as wide. The standard errors are clustered by lead, because the same lead can appear in several quarters.5

TermCoefficientStd. errorWhat it says
Intercept (solo, average lead age, first quarter, no contact)0.0170.011About 1.7% start a trial with no contact at all
ln(1 + emails)0.01220.0042First email adds 0.85 points, twelve add 3.1
ln(1 + webinar invitations)0.03490.0082One invitation adds 2.4 points, the strongest lever
ln(1 + retargeting, per 10 impressions)0.00700.0082Ten impressions add 0.5 points, not distinguishable from zero
ln(1 + emails) x ln(1 + retargeting)0.00250.0032Positive, not distinguishable from zero
Segment: growing firm, 3 to 10 staff0.03720.00393.7 points above solo tradespeople
Segment: established firm0.02230.00462.2 points above solo tradespeople
ln(lead age in months), centredminus 0.00580.0019Older leads respond a little less

Quarter effects are in the model too, so a strong January is not credited to January's emails. None is clearly different from zero, so they are left out of the table.

Read it line by line. The intercept is the baseline: for the last quarter the model puts 284 of the 770 trials at the no contact level, trials nobody in that review should take credit for. The email coefficient looks tiny, as coefficients on log terms always do, and its 95% interval runs from 0.0039 to 0.0204, which matters in a moment. Foundations: confidence intervals Webinar invitations are the strongest lever per contact, no surprise when the webinar shows scheduling software actually working. Retargeting's standard error is larger than its estimate, so the model cannot say whether it works at all. The interaction is in the same position: positive, with an interval that includes zero, so the plan should not lean on it. The segment terms come from a k means segmentation by firm size, and the lead age term says a lead from 2024 is not a lead from last month.

The R squared is under 0.01, and that is fine: the point is not to predict which individual starts a trial but how the rate moves when contacts move. What I do check is that a decile chart of predicted against actual trial rates rises steadily. Here it does, from 6.2% in the lowest predicted tenth to 14.8% in the highest, with the actual rate within about one point of the prediction in every tenth.

Response uplift by contacts per quarterEstimated rise in the trial rate, in percentage points, for 0 to 12 contacts per quarter. Webinar invitations reach 2.42 points at one, 3.83 at two and 8.95 at twelve. Emails reach 0.85 at one, 2.93 at ten and 3.13 at twelve. Retargeting blocks of ten impressions reach 0.49 at one and 1.80 at twelve.Uplift in the trial rate, percentage points02.557.510036912Webinar invitationsEmailsRetargeting, blocks of ten impressionsPercentage pointsContacts per quarter

The horizontal axis is the number of contacts in the quarter, the vertical axis the estimated rise in the trial rate in percentage points. Each curve is the coefficient times ln⁡(1+contacts)\ln(1 + \text{contacts}). The takeaway: every curve is steep at the start and flat by the end. One caution on the webinar line: Vanlog has never sent more than two invitations a quarter, so everything beyond two is the log shape speaking, not the data.

When to stop sending

Now the money. With VV at €300 and βe\beta_e at 0.0122, the marginal value of one more email to a lead who is not being retargeted is 300×0.0122/(1+E)300 \times 0.0122 / (1+E), which is €3.66 divided by one plus the emails already sent. The first email is worth €3.66, the fourth 3.66/4=€0.923.66 / 4 = €0.92, the tenth 3.66/10=€0.373.66 / 10 = €0.37.

The cost side is where the naive version of this analysis falls apart. An email costs perhaps two cent to send, and if that were the whole cost the formula would say 300×0.0122/0.02−1=182300 \times 0.0122 / 0.02 - 1 = 182 emails a quarter, which is a spam filter's definition of a contact strategy. The real cost of one more email includes the chance that the lead unsubscribes and the value lost when they do.

Worked example: the full cost of one email

The example's assumptions: across Vanlog's list, each email makes 0.45% of recipients unsubscribe, and a live lead on the list is worth about €40 in expected future trials (roughly a 13% chance of a future trial worth €300). So each email carries 0.0045×40=€0.180.0045 \times 40 = €0.18 of expected list damage on top of €0.02 to send it, €0.20 in all. That is cec_e for the average lead, and it is what makes the optimum sane:

E∗=300×0.01220.20−1=18.3−1=17.3E^{*} = \frac{300 \times 0.0122}{0.20} - 1 = 18.3 - 1 = 17.3

Here V=300V = 300, βe=0.0122\beta_e = 0.0122 and ce=0.20c_e = 0.20, with no retargeting, so R=0R = 0. Foundations: marginal versus average So about 17 emails a quarter, a little over one a week, against the 12 Vanlog plans now.

The fatigue is not hypothetical. In the distributed lag chapter, Northgate Hampers, the invented Cork hamper shop, sent twice two weeks apart for its Father's Day range, in weeks 22 and 24, and the unsubscribe rate on the second send was roughly double the usual. A fixed unsubscribe rate per email is the gentle version of that.

Marginal value of one more email against its full costThe marginal value of the next email falls from €3.66 with no emails sent to €1.83 after one, €0.92 after three, €0.33 after ten, €0.22 after 16 and €0.17 after 20. The full cost per email is flat at €0.20. The lines cross at about 17 emails.€ per email, average lead with no retargeting€0€1.3€2.5€3.8€50381420Marginal value of the next emailFull cost per email€ per emailEmails already sent this quarter

The horizontal axis is the number of emails a lead has already received this quarter, the vertical axis euro per email. The falling line is the marginal value of the next email, the flat line its full cost. The takeaway: where they cross, at about 17, the next email stops paying for itself.

Now the honest part. The email coefficient's interval runs from 0.0039 to 0.0204. Put those into the same formula and the optimum runs from 300×0.0039/0.20−1=4.9300 \times 0.0039 / 0.20 - 1 = 4.9 to 300×0.0204/0.20−1=29.6300 \times 0.0204 / 0.20 - 1 = 29.6. Somewhere between five and 30 emails a quarter is not a plan, it is a range. The model says "probably more than 12". It does not say "17", and the memo should not either.

The contact plan is set per segment

The email coefficient, the unsubscribe rate and the value of a lead on the list all differ by segment, so the optimum does too. Interact the email term with the segment to get one coefficient per segment.

SegmentShare of leadsEmail coefficient (std. error)Full cost per emailOptimal emails per quarterCurrentDecision
Solo tradespeople45%0.0096 (0.0048)€0.221212Keep
Growing firms35%0.0170 (0.0060)€0.182712Test 18 against 12
Established firms20%0.0085 (0.0079)€0.307 or 812Test fortnightly against 12

Worked example: the three optima

The cost assumptions: solo tradespeople unsubscribe at 0.5% per email, so 0.005×40+0.02=€0.220.005 \times 40 + 0.02 = €0.22. Growing firms unsubscribe at 0.4%, so 0.004×40+0.02=€0.180.004 \times 40 + 0.02 = €0.18. Established firms also unsubscribe at 0.4%, but a lost established firm costs more because one that converts buys more seats, so the example values a live lead there at €70: 0.004×70+0.02=€0.300.004 \times 70 + 0.02 = €0.30. Then:

Esolo∗=300×0.00960.22−1=12.1,Egrowing∗=300×0.01700.18−1=27.3,Eestablished∗=300×0.00850.30−1=7.5E^{*}_{\text{solo}} = \frac{300 \times 0.0096}{0.22} - 1 = 12.1, \quad E^{*}_{\text{growing}} = \frac{300 \times 0.0170}{0.18} - 1 = 27.3, \quad E^{*}_{\text{established}} = \frac{300 \times 0.0085}{0.30} - 1 = 7.5

Each E∗E^{*} uses the segment's own email coefficient in place of βe\beta_e and its own full cost cec_e, with V=300V = 300 throughout. Foundations: marginal versus average

Vanlog had been planning twelve emails a quarter for everyone. On the point estimates, solo tradespeople are about right, growing firms are under contacted and established firms are over contacted. What changes is less how many emails go out than who gets them.

Now look at the standard errors. Each segment coefficient rests on a third of the data, so the intervals are wide: for established firms the interval on the coefficient runs from minus 0.007 to 0.024, which does not even rule out email doing nothing for them. That is why the decision column says "test" twice. The model tells you where to look. A randomised frequency test, half of the growing firms on 18 emails and half on 12 for a quarter, tells you whether to act. My complete guide to marketing testing methods has the design details.

One more thing the formula shows. If the interaction is real, a lead also sitting in the retargeting pool at about 15 blocks of impressions has an effective email coefficient of 0.0122+0.0025×ln⁡16=0.01910.0122 + 0.0025 \times \ln 16 = 0.0191, and the optimum moves from 17 to 300×0.0191/0.20−1=27.7300 \times 0.0191 / 0.20 - 1 = 27.7, about 28. Channels have to be planned together. A frequency cap set in the email tool without looking at the ad platform is guessing. This is Marginal ROI versus average ROI applied one contact at a time.

Webinar invitations and retargeting go through the same optimiser. The example's costs: €2.50 per invitation including the webinar's production, and an effective €0.15 per block of ten impressions, fatigue included. For webinars, 300×0.0349/2.50−1=3.2300 \times 0.0349 / 2.50 - 1 = 3.2, so about three a quarter, where Vanlog has run one or two. Two is well inside the data. Three is one step beyond it and deserves a test. For retargeting with no email, 300×0.0070/0.15−1=13300 \times 0.0070 / 0.15 - 1 = 13 blocks, about 130 impressions a quarter. But the retargeting coefficient is not distinguishable from zero, and when the coefficient's interval includes zero, so does the optimal frequency. The cap stays where it is until a holdout says more.

Incremental value against what the platforms claimed

Here is the table that ended the argument in the quarterly review. Spend includes people's time. The model column is what the fitted model loses when one channel is set to zero for every lead, everything else as it was. The holdout column is measured lift with its 95% interval, from the next section.

ChannelSpendTrials claimed by platformIncremental trials, modelIncremental trials, holdout (95% interval)Incremental value at €300Value per € spent
Email€3,200430271270 (139 to 401)€81,00025.3
Webinars€6,000210162130 (minus 14 to 274)€39,0006.5
Retargeting€7,2005409880 (minus 68 to 228)€24,0003.3
Shared by email and retargetingcounted in bothnot reported45inside both
Baseline, no contact€00284
Total€16,4001,180770

The value columns use the holdout figure:

Value per euro=ΔT×VS\text{Value per euro} = \frac{\Delta T \times V}{S}

Here ΔT\Delta T is the channel's incremental trials, VV the value of a trial and SS the channel's spend in the quarter. For email, 270×300/3,200=25.3270 \times 300 / 3{,}200 = 25.3. Foundations: ratios and rates

The 45 shared trials sit inside both the email and the retargeting figure, so the model column adds up to the trials that happened only when they are counted once: 271+162+98−45+284=770271 + 162 + 98 - 45 + 284 = 770.

The platforms claimed 1,180 trials for a quarter that produced 770, and nothing for the 284 leads who would have signed up regardless. Retargeting is the sharpest lesson: 540 claimed conversions, which at €300 each is €162,000 of value on €7,200 of spend, or 22.5 to one. The holdout says 80 incremental trials, €24,000, 3.3 to one, and its interval reaches below zero. Even the top of that interval, 228, is under half the claim. View through attribution takes credit for showing an ad to people already on their way to the sign up page. Email and webinars over claim too, by roughly 60%, but their claims sit in the right neighbourhood, as tends to happen when a channel needs a real action from the lead. Once you have this table, the budget question becomes the one I set out in where your next marketing euro should go: to the channel whose next euro, not whose average euro, earns most.

The shared row is the one people find odd. Forty five trials belong jointly to email and retargeting in the model and cannot be split without a rule, and I refuse to invent one. I report it as shared value and decide at the level of the pair: cut both, keep both, or shift budget between them. That question the model answers cleanly.

Holdout groups are the gold standard

A holdout group is a random subset of leads who do not receive the communication. Everything else about them is the same in expectation, so the difference in trial rate is the causal effect of the communication. The model cannot substitute for this. It assumes contacts were not aimed at the leads most likely to convert anyway, and in real marketing operations that is always partly false: sales calls the hot leads, and the retargeting algorithm shows more ads to people who behave like buyers.

The email holdout at VanlogThe 8,000 leads of the quarter are split at random within each segment. 7,200 receive the full contact plan and 800 receive no email. The treated group starts trials at 10.0% and the holdout at 6.25%. The lift is the difference, 3.75 points, which times 7,200 treated leads is 270 incremental trials, worth €81,000 at €300 each, compared with the 430 the email tool claimed.8,000 leads in the CRM this quarterRandom split within each segment90%7,200 treated leads receive the fullcontact plan10%800 holdout leads receive no email thisquarterTrial rate: treated 10.0%, holdout6.25%Lift equals treated minus holdout: 3.75pointsIncremental trials: 3.75 points times7,200 equals 270Compare with the platform claim of 430

The diagram runs from the lead list to the comparison with the claim. Randomise within segment, so both groups have the same mix of segments and only the email differs.

The lift and its uncertainty:

L^=yˉT−yˉH,SE(L^)=yˉT(1−yˉT)nT+yˉH(1−yˉH)nH\widehat{L} = \bar{y}_T - \bar{y}_H, \qquad SE(\widehat{L}) = \sqrt{\frac{\bar{y}_T(1-\bar{y}_T)}{n_T} + \frac{\bar{y}_H(1-\bar{y}_H)}{n_H}}

Here L^\widehat{L} is the estimated lift, yˉT\bar{y}_T and yˉH\bar{y}_H are the trial rates in the treated and holdout groups, nTn_T and nHn_H are the group sizes, and SE(L^)SE(\widehat{L}) is the standard error of the lift, the usual one for a difference between two proportions.6 Foundations: confidence intervals

Worked example: reading out the email holdout

The treated group of 7,200 leads started 720 trials, 10.0%. The holdout of 800 started 50, 6.25%. The lift is 0.100−0.0625=0.03750.100 - 0.0625 = 0.0375, 3.75 points, and 0.0375×7,200=2700.0375 \times 7{,}200 = 270 incremental trials. The standard error:

SE=0.100×0.9007,200+0.0625×0.9375800=0.0000125+0.0000732=0.0093SE = \sqrt{\frac{0.100 \times 0.900}{7{,}200} + \frac{0.0625 \times 0.9375}{800}} = \sqrt{0.0000125 + 0.0000732} = 0.0093

So 0.93 points, and the 95% interval is 3.75±1.96×0.933.75 \pm 1.96 \times 0.93, from 1.94 to 5.56 points, or 139 to 401 trials. That is wide, and I say so in the memo. One quarter with a 10% holdout gives the direction and the rough size. A 20% holdout would narrow the interval from about plus or minus 1.8 points to about 1.4. The model's 271 sits inside the interval, which is the agreement I look for.

Each channel needs its own holdout logic. Email is easy: suppress the sends for a random group. Retargeting is harder because the platform decides who sees what. The clean options are a geographic holdout, where a random set of regions gets no retargeting,7 or a ghost ad design, where the platform records which control group users would have seen the ad, so they can be compared with the users who actually saw it.8 Webinars are hardest, because attending is a choice and attendees would have converted at a higher rate anyway. The honest design withholds the invitation from a random 10% and compares invited against not invited, never attended against not attended. At Vanlog that gave 130 incremental trials against the platform's 210, lower precisely because of that self selection.

Running it yourself

You need one table with a row per lead per quarter: contacts per channel, the outcome and whatever you know about the lead. If your touches live in four tools, the first week is getting them into one place with one lead identifier, and that week is usually the project's whole risk. The query assumes four tables: leads, lead_quarters (which leads were live in which quarter, and who was held out), touches and trials.

with contacts as (
  select lead_id,
         date_trunc('quarter', touched_at) as quarter,
         count(*) filter (where channel = 'email')   as emails,
         count(*) filter (where channel = 'webinar') as webinar_invites,
         coalesce(sum(impressions) filter (where channel = 'retargeting'), 0) / 10.0 as retargeting_10
  from touches
  where touched_at >= date '2025-07-01' and touched_at < date '2026-07-01'
  group by lead_id, quarter
)
select q.lead_id, q.quarter, l.segment, l.created_at, q.holdout_email,
       coalesce(c.emails, 0)          as emails,
       coalesce(c.webinar_invites, 0) as webinar_invites,
       coalesce(c.retargeting_10, 0)  as retargeting_10,
       (t.lead_id is not null)::int   as trial
from lead_quarters q
join leads l using (lead_id)
left join contacts c on c.lead_id = q.lead_id and c.quarter = q.quarter
left join trials t on t.lead_id = q.lead_id
  and date_trunc('quarter', t.started_at) = q.quarter;

Then the model and the optimiser, short in Python with statsmodels, where df is the result of the query:

import numpy as np
import pandas as pd
import statsmodels.formula.api as smf

months = (pd.to_datetime(df["quarter"]) - pd.to_datetime(df["created_at"])).dt.days / 30.4
df["log_age"] = np.log(months.round().clip(lower=1))
df["log_age"] -= df["log_age"].mean()
df["lE"] = np.log1p(df["emails"])
df["lW"] = np.log1p(df["webinar_invites"])
df["lR"] = np.log1p(df["retargeting_10"])
m = smf.ols("trial ~ lE + lW + lR + lE:lR + C(segment, Treatment('solo')) + log_age + C(quarter)",
            data=df).fit(cov_type="cluster", cov_kwds={"groups": df["lead_id"]})
print(m.summary())

V, cost_email = 300.0, 0.20
b_e, b_er = m.params["lE"], m.params["lE:lR"]
optimal_emails = lambda blocks: V * (b_e + b_er * np.log1p(blocks)) / cost_email - 1
print({r: round(optimal_emails(r)) for r in (0, 15)})

On Vanlog's data the last line prints {0: 17, 15: 28}, the two optima from the worked examples. Robust standard errors matter because the outcome is 0 or 1 and its variance is not constant, and clustering by lead matters because the same lead appears in several quarters.5 Fit the logistic version alongside and compare the implied effects. If they differ by more than a little, your response rates are outside the range where the linear model is safe.

With clean data, the model, the holdout read out and the contact plan take about two weeks. Before trusting the result I check four things. The coefficients keep their sign and rough size when the model is refitted on a different set of quarters. The holdout lift and the model's estimate for that channel overlap. The decile chart of predicted against actual rises steadily. And the holdout really did receive nothing. I have seen an "unsubscribed" flag quietly used as the holdout, which is not a random group and turns the whole exercise into fiction.

Pitfalls

Treating contacts as if they were randomly assigned. They almost never are. Sales calls the hottest leads, the retargeting algorithm chases people who look like buyers, the webinar attracts the already curious, and the model credits the channel with the lead's existing intent. Controls in XiX_i help, the holdout is the cure, and where you have neither, say so in the memo. When the budget itself follows sales, the simultaneous equations chapter shows what that does to a regression.

Cost defined as the send cost alone. The two cent email is the most expensive mistake in this chapter. Without unsubscribe damage in the cost, the optimiser recommends 182 emails a quarter. Measure the unsubscribe rate per email per segment and the value of a live lead, and the optimum drops to something a human recognises.

Reading the attribution report as a measurement. It is a rule. Last click over credits whatever is nearest the finish line, usually brand search and retargeting, and under credits whatever warmed people up earlier. Never compare one channel's attributed number with another channel's incremental number. They are not on the same scale.

Holdouts that are too small or too short. A 5% holdout on 8,000 leads gives an interval of about plus or minus 2.5 points, wider than most channel effects in this chapter. Size the holdout to the lift you need to detect, and accept that a frequency decision worth a few thousand euro a quarter deserves two quarters of evidence. Foundations: sample size

Letting the log curve promise that more is always a little better. The model never turns negative because it was built so it cannot. Real audiences do, through unsubscribes, spam complaints and the slow erosion of attention. Put fatigue in the cost, watch complaint rates, and treat the optimum as a ceiling rather than a target.

How I do this for clients

The deliverable is a contact plan with a number and a risk attached to every line, not a model file. To build it I need, per lead, the touches by channel with timestamps, the outcome you care about (trial, order or booking) and whatever segment or company data you hold. Exports from the email tool, the ad platforms and the CRM are enough. I join them.

It starts with the free workshop, where we agree what a response is worth in margin, which channels are in scope and which segments the plan will be set for. Then I spend two weeks of real work, on me, building the one table, fitting the model and designing the holdouts you can run next quarter. You get the response curves per channel, the incremental value table against what the platforms claimed, the optimal frequency per segment with the assumptions written next to it, and a one page decision memo that says what to change, what it is worth and how sure I am. If your stack cannot yet suppress a random holdout, I set that up too, because without it we are estimating rather than measuring.

Your raw data stays yours, and the terms for code and models are in the analytics and data science terms. The fees are on the pricing page. The modelling sits under Data Science, the execution across email, webinars and paid channels under Performance Marketing. One accountable person for both means the model and the campaigns cannot blame each other.

Questions for your next quarterly review

  • If we add up the trials each platform claims, is the total larger than the trials we actually had?
  • For each channel, what is the marginal value of one more contact at the frequency we run now, not the channel's average value?
  • What is the full cost of one more email, including expected unsubscribe damage, and who measured it?
  • Which channels have a randomised holdout this quarter, how large is it, and what interval does it give us?
  • How does the optimal frequency differ by segment, and are we sending everyone the same thing?
  • Where are email and paid media planned together, and where in separate tools by separate people?
  • What share of this quarter's conversions would have happened with no marketing at all, and who is taking credit for them?

The series is inspired by Mike Grigsby's Marketing Analytics.9 The explanations, examples and numbers here are my own.

Footnotes

  1. Google, About view through conversions, Google Ads Help, https://support.google.com/google-ads/answer/6270625

  2. Google, About attribution models, Google Ads Help, https://support.google.com/google-ads/answer/6259715

  3. Brett R. Gordon, Florian Zettelmeyer, Neha Bhargava and Dan Chapsky, A Comparison of Approaches to Advertising Measurement: Evidence from Big Field Experiments at Facebook, Kellogg School of Management working paper, April 2018, https://www.kellogg.northwestern.edu/faculty/gordon_b/files/fb_comparison.pdf

  4. statsmodels developers, LogitResults.get_margeff, statsmodels documentation, https://www.statsmodels.org/stable/generated/statsmodels.discrete.discrete_model.LogitResults.get_margeff.html

  5. statsmodels developers, RegressionResults.get_robustcov_results (HC1 and cluster robust covariance), statsmodels documentation, https://www.statsmodels.org/stable/generated/statsmodels.regression.linear_model.RegressionResults.get_robustcov_results.html

  6. Stat Trek, Difference between proportions, https://stattrek.com/estimation/difference-in-proportions

  7. Jon Vaver and Jim Koehler, Measuring Ad Effectiveness Using Geo Experiments, Google Inc., 2011, https://research.google/pubs/measuring-ad-effectiveness-using-geo-experiments/

  8. Garrett A. Johnson, Randall A. Lewis and Elmar I. Nubbemeyer, Ghost Ads: Improving the Economics of Measuring Ad Effectiveness, working paper, February 2016, https://conference.nber.org/conf_papers/f84173.pdf

  9. Mike Grigsby, Marketing Analytics: A Practical Guide to Improving Consumer Insights Using Data Techniques, Kogan Page, 2022, ISBN 9781398608191, https://www.netgalley.co.uk/catalog/book/267990

If your platforms add up to more conversions than you had, bring the exports to the free workshop and we will agree what a response is worth and which channels are in scope. Then the first two weeks of real work are on me: response curves, the marginal value of one more contact per channel and a contact plan per segment, each with its uncertainty written next to it.