Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

SEO & Digital Marketing  Module 11 – Analytics and measurement

Attribution, incrementality and marketing mix

Compare GA4's data-driven and last-click attribution with ad platforms' counts, run a holdout or geo test, and see what marketing mix models can tell you.

  • Advanced
  • 25 minutes
  • Examples run with Node.js 24.21.0, Python 3.14.8 and Pyodide 314.0.7
  • By MySmartCoPilot
Free lesson, Pro answers: 25 minutes
Free: the whole lesson. With a pass: the model answers to its 2 interview questions.

What you will learn

  • Compare GA4's data-driven and last-click attribution, and the credit each gives to the channels of a path
  • Explain why Google Ads, other ad platforms and GA4 report different numbers for the same week
  • Design a holdout or geo test that estimates what a campaign added, and report it as a range against break-even
  • Decide when a marketing mix model such as Meridian or Robyn is worth building for a business

Before you start

What you’ll learn · Before you start

What you will learn

  • Compare GA4's data-driven and last-click attribution, and the credit each gives to the channels of a path
  • Explain why Google Ads, other ad platforms and GA4 report different numbers for the same week
  • Design a holdout or geo test that estimates what a campaign added, and report it as a range against break-even
  • Decide when a marketing mix model such as Meridian or Robyn is worth building for a business

Before you start

On this page

Pune Bakes spent 30,000 rupees on Instagram ads in Pune for four weeks, and the ad platform reported 61 orders from them, almost exactly the 62 the ads needed to pay for themselves at a margin of 486 an order. A test that kept the ads out of Nashik says they added about 52 orders, somewhere between 35 and 69, so whether they paid is still open.

The platform’s number is attribution, a share of the credit for orders that happened; the test’s number is incrementality, an estimate of the orders that would not have happened without the ads. By the end of this page you can read GA4’s attribution models and what each gives a channel, explain why every platform’s total differs, design a holdout or geo test and report it as a range against break-even, and decide whether a marketing mix model is worth building.

Attribution shares the credit for an order among the visits before it

Most orders come at the end of a path of visits from several channels, and an attribution model: A rule or a model that decides how the credit for a conversion is shared among the channels on the path before it, such as last non-direct click or data-driven attribution. decides how much of each order each channel gets. GA4 offers three: data-driven attribution, the subject of the next section; paid and organic last click, which gives everything to the last channel that is not Direct; and Google paid channels last click, which gives everything to the last Google Ads channel and falls back to the paid and organic rule when there is none. Direct gets credit only when the whole path is direct. First click, linear, time decay and position-based were removed in 2023 (Get started with attribution). Step through one of the bakery’s orders:

Interactive · step by step One order, four channels, three rules Step through Asha's path to her order, then see how much of the order each rule gives each channel.
One order, four channels, three rules, step 5 of 5. Showing Instagram ad (position-based: 0.4), WhatsApp chat (position-based: 0.2), Google search (position-based: 0.4), Typed the address (Direct: no credit), Order.Instagram adposition-based: 0.4WhatsApp chatposition-based: 0.2Google searchposition-based: 0.4Typed the addressDirect: no creditOrderone eggless cake0, 0.33 or 0.4 of the order

The drawing shows the whole scene; its 5 steps are listed below.

Every step as text
  1. The path. Asha clicked an Instagram ad, asked a question on WhatsApp, searched for the bakery on Google a day later and finally typed the address to pay. One order, four visits.
  2. Last non-direct click. GA4's paid and organic last click gives the whole order to the last channel that is not Direct. Typing the address does not count, so Google search gets 1 order and the rest 0.
  3. Linear. A linear rule, which GA4 removed in 2023, splits the order evenly over the channels that are not Direct, a third each for Instagram, WhatsApp and Search.
  4. Position-based. A position-based rule, also removed, gives 40 % to the first channel and 40 % to the last, and shares 20 % between those in the middle.
  5. Same order, three answers. The order is the same and nothing about Asha changed, yet Instagram's share runs from 0 to 0.4. A rule does not discover credit; it decides it.

The program draws 500 synthetic paths of the bakery’s customers and shares their orders out under last non-direct click and under two of the removed rules, shown only to see how a rule moves credit. Run it, then press Edit and raise the chance that a path ends with a Google search from 0.6 to 0.9:

500 orders shared out by three rules Python · paths/paths.py
"""Share the credit for 500 of the bakery's orders among the channels on each order's path, under three rules.

The paths are synthetic, drawn from a seeded generator. "Last non-direct click" is GA4's paid and organic last click:
all the credit goes to the last channel that is not Direct, and an all-direct path keeps it for Direct ("Get started
with attribution", Analytics Help). Linear and position-based are rule-based models that GA4 removed in 2023; they are
shown only to see how a rule moves credit, never as what GA4 reports. The bakery is fictional.
"""
import random

rng = random.Random(42)
CHANNELS = ["Instagram ads", "WhatsApp", "Organic Search", "Email", "Direct"]


def path():
    """One order's path: Instagram ads often open it, WhatsApp and Email help, Search and Direct often close it."""
    steps = []
    if rng.random() < 0.55:
        steps.append("Instagram ads")
    for channel, chance in (("WhatsApp", 0.45), ("Email", 0.2)):
        if rng.random() < chance:
            steps.append(channel)
    if rng.random() < 0.6 or not steps:
        steps.append("Organic Search")
    if rng.random() < 0.35:
        steps.append("Direct")
    return steps


def last_non_direct(p):
    others = [c for c in p if c != "Direct"]
    return {others[-1] if others else "Direct": 1.0}


def linear(p):
    others = [c for c in p if c != "Direct"] or ["Direct"]
    return {c: others.count(c) / len(others) for c in dict.fromkeys(others)}


def position_based(p):
    others = [c for c in p if c != "Direct"] or ["Direct"]
    if len(others) <= 2:
        return linear(others)
    credit = {c: 0.0 for c in others}
    credit[others[0]] += 0.4
    credit[others[-1]] += 0.4
    for c in others[1:-1]:
        credit[c] += 0.2 / (len(others) - 2)
    return credit


paths = [path() for _ in range(500)]
models = {"last non-direct": last_non_direct, "linear*": linear, "position-based*": position_based}
totals = {name: {c: 0.0 for c in CHANNELS} for name in models}
for p in paths:
    for name, model in models.items():
        for channel, share in model(p).items():
            totals[name][channel] += share

print(f"{'orders credited':16}" + "".join(f"{name:>17}" for name in models))
for c in CHANNELS:
    print(f"{c:16}" + "".join(f"{totals[name][c]:17.0f}" for name in models))
touched = sum("Instagram ads" in p for p in paths)
print(f"\nInstagram ads appear on {touched} of the 500 paths; * = removed from GA4 in 2023, shown for teaching only")

Output

orders credited   last non-direct          linear*  position-based*
Instagram ads                  39              128              136
WhatsApp                       67              109               99
Organic Search                359              219              227
Email                          35               44               38
Direct                          0                0                0

Instagram ads appear on 253 of the 500 paths; * = removed from GA4 in 2023, shown for teaching only

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 paths.py

Instagram ads appear on 253 of the 500 paths. Last non-direct click gives them 39 orders, because Search usually comes last and takes 359. Spread the credit evenly and Instagram gets 128; weight the first and last visits and it gets 136. The orders are the same in every column. A rule decides the credit; it never discovers it, and none of the three says whether those 253 customers would have ordered without the ads.

Checkpoint

Check: Attribution models and the credit they give

  1. Question 1 of 2 A path runs Instagram ad, WhatsApp chat, Google search, then a typed address before the order. Which channel does GA4's paid and organic last click credit with the whole order?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Organic Search, the last channel that is not Direct

    Last non-direct click is why Search took 359 of the program's 500 orders: it so often comes last.

  2. Question 2 of 2 When does Direct receive credit in GA4's attribution models?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Only when every visit on the path to the key event was direct

    GA4 treats a typed address as the end of a journey that started somewhere else, unless nothing else is on the path.

Data-driven attribution learns from the paths that did not convert

data-driven attribution: GA4's attribution model that shares credit by each touchpoint's estimated contribution, learned from paths that converted and paths that did not. It can revise recent conversions for up to 7 days. does not apply a fixed rule. Google says it uses the account’s own data, and its algorithms evaluate the paths of people who converted and of people who did not, with a counterfactual approach that “contrasts what happened with what could have occurred”. For Google ad exposures, the gains come from “training on data from randomized controlled trials”, and where an account has little data the models may draw on aggregate data from its data sharing settings (Get started with attribution). Follow the steps Google describes:

Interactive · stage by stage What data-driven attribution does with the paths Follow the steps Google describes for its data-driven model, from the paths it reads to the credit it gives.
What data-driven attribution does with the paths, step 5 of 5. Showing Read the paths, Compare them, Estimate each touch, Share the credit, Revise for 7 days. In focus: Revise for 7 days.Read the pathswith and without key eventsCompare themhappened against could haveEstimate each touchlearned from trialsShare the creditby each touch's estimateRevise for 7 daysas new data arrives

The drawing shows the whole scene; its 5 steps are listed below.

Every step as text
  1. Both kinds of path. The model reads the paths that ended in a key event and the paths that did not, from the account's own data. Last click reads only the first kind.
  2. A counterfactual. It contrasts what happened with what could have occurred, to find the touchpoints most likely to lead to a key event.
  3. Learned from trials. For Google ad exposures, Google says the gains are computed by training on data from randomised controlled trials, and that where an account has little data, aggregate data from its data sharing settings may be used.
  4. Credit by contribution. Each touchpoint gets a share of the key event in line with its estimated contribution, so an opening channel such as an Instagram ad can get credit that last click never gives it.
  5. Numbers that move. Conversions can be reattributed for up to 7 days after the conversion, so last week's figures may still change. Read them once they have settled.

Two practical points follow from that description. Recent numbers move, because conversions “can be reattributed for up to 7 days after the conversion”: the bakery’s Instagram ads had 31 of last week’s orders on Monday and 34 on Friday. And the model is still attribution, run inside your reports on the paths GA4 could see. It gives an opening channel such as an Instagram ad credit that last click never does, but for one business’s ads it runs no experiment, and the bakery cannot see inside it. The reporting model is a setting, under Admin, Data display, Events, Attribution settings, beside the key event lookback window that decides how far back a touchpoint can still earn credit.

Comparing two models and calling the winner true

Switching the report from last click to data-driven and seeing Instagram’s share rise proves only that the models differ. Neither number says how many orders the ads caused; only a test can.

Checkpoint

Check: Data-driven attribution

  1. Question 1 of 2 On Monday the data-driven report gave Instagram ads 31 of last week's orders, and on Friday 34. What happened?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Data-driven attribution reattributed some conversions, which it may do for up to 7 days

    Read last week's data-driven numbers once they have settled, after the 7-day window.

  2. Question 2 of 2 Switching the report from last click to data-driven doubles Instagram's share. What can you conclude?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: That the two models share credit differently; neither says how many orders the ads caused

    Changing the model changes the credit, not the facts. Measure what the ads add with a holdout or a geo test.

Every ad platform counts with its own rules, so the totals never agree

Each ad platform measures from its own side, with its own windows. Google Ads counts a conversion that follows a click within 30 days by default, adjustable from 1 to 90; after an engaged view of a video ad within 3 days; and after an ad that was only seen within 1 day (About conversion windows). View-through conversions, where someone “saw your ad but did not click it”, are kept out of the main Conversions column (About conversion measurement). Other platforms set their own windows and count views their own way, so read each one’s attribution settings before you compare. Above all, every platform counts the orders that follow its own ads, while GA4 gives each order to one channel by one rule, so the platforms’ totals added together can exceed the orders the business took. The bakery’s reports for one week:

Source Orders it gives Instagram’s ads Why it differs
The social ad platform 61 Every order after its own ad, 15 of them after a view without a click
GA4, data-driven, on Friday 34 A share of each path, still being revised for 7 days
GA4, last non-direct click 22 Only the orders whose last channel was Paid Social
The geo test, over 4 weeks 52, range 35 to 69 Orders that would not have happened without the ads

Pick one source of truth for each question and say which: the bakery’s own order records for how many orders there were, GA4 for how they spread across channels, each platform’s own numbers for tuning the campaigns inside it, and a test for what the ads added. Consent widens every gap, since GA4 measures less of the visitors who refused, while a platform may still count its own clicks.

ROAS Calculator Work out the ROAS a platform's number implies and the ROAS the test's range implies, and compare both with your break-even ROAS.

Checkpoint

Check: Why platforms and GA4 disagree

  1. Question 1 of 2 The platform counts 15 orders from people who saw an ad and never clicked it. Why are they absent from GA4's attribution?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: GA4 sees visits and their sources; a view without a click never brings a visit with the ad as its source

    View-through counts are the platform's own view of its influence. Google Ads keeps them out of its main Conversions column; read each platform's settings.

  2. Question 2 of 2 The bakery's ad platforms together claim more orders than its order system took. Is something broken?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Not necessarily; each platform counts the orders after its own ads, so one order can be claimed twice

    Count orders in your own records, split them across channels in GA4, and use each platform's numbers only inside that platform.

Incrementality asks what would have happened without the ads

incrementality: The difference between what happened with the marketing and what would have happened without it, measured against a group or a region kept from the ads. is the difference between what happened with the ads and what would have happened without them, and the only way to see the second is to keep some customers from the ads. An audience holdout keeps a holdout group: The people, or the region, deliberately kept from a campaign during a test, so that their results show what would have happened without the ads. of people out of the campaign and compares their conversion rate with the rest, a comparison of two proportions with a standard test (NIST/SEMATECH e-Handbook). A geo test runs the ads in one region and not in a similar one. A staggered start switches regions on one after another. A comparison of before and after with no control proves nothing on its own, because the season moves both.

The bakery ran its ads in Pune and held them back in Nashik for four weeks, after four ordinary weeks. The program computes a difference in differences, Pune’s change minus Nashik’s, so that whatever moved both cities cancels out, and a range by redrawing the weeks 2,000 times. Run it, then press Edit and add two more weeks to each list of Pune and Nashik, copying the last value:

What the ads added in Pune, against Nashik, and whether it paid Python · geo/geo_holdout.py
"""Estimate what the bakery's Instagram ads added, from a test that ran them in Pune and held Nashik back.

A difference in differences: the change in Pune's weekly orders minus the change in Nashik's over the same weeks, so
whatever moved both cities (the season, a holiday) cancels out. The range comes from a bootstrap, which redraws the
weeks of each period with replacement 2,000 times from a fixed seed. The orders are synthetic; the margin of 486 per
order is the goals lesson's. The bakery is fictional.
"""
import random
from math import erf, sqrt
from statistics import mean, stdev

PUNE = {"before": [52, 48, 55, 50], "during": [66, 70, 63, 69]}  # ads on in Pune during the test
NASHIK = {"before": [30, 28, 33, 29], "during": [33, 31, 35, 32]}  # ads held back
SPEND, MARGIN, WEEKS = 30_000, 486, 4


def did(test, control):
    return (mean(test["during"]) - mean(test["before"])) - (mean(control["during"]) - mean(control["before"]))


rng = random.Random(7)
draws = sorted(did({k: rng.choices(v, k=len(v)) for k, v in PUNE.items()},
                   {k: rng.choices(v, k=len(v)) for k, v in NASHIK.items()}) for _ in range(2000))
low, high = draws[49], draws[1949]
estimate, spread = did(PUNE, NASHIK), stdev(draws)
break_even = SPEND / MARGIN / WEEKS  # extra orders a week the ads must bring to pay for themselves
paid = sum(d >= break_even for d in draws)


def normal_between(a, b, mu, sigma):
    """P(a <= X <= b) for a normal curve with mean mu and spread sigma, the model the page's figure draws."""
    def cdf(x):
        return 0.5 * (1 + erf((x - mu) / (sigma * sqrt(2))))
    return cdf(b) - cdf(a)


print(f"Pune: {mean(PUNE['before']):.2f} -> {mean(PUNE['during']):.2f} orders a week")
print(f"Nashik: {mean(NASHIK['before']):.2f} -> {mean(NASHIK['during']):.2f} orders a week")
print(f"Added by the ads: {estimate:.2f} orders a week, 95 % range {low:.2f} to {high:.2f} (bootstrap spread {spread:.2f})")
print(f"\nOver {WEEKS} weeks: {WEEKS * estimate:.0f} extra orders, range {WEEKS * low:.0f} to {WEEKS * high:.0f}")
print(f"Their margin: {MARGIN * WEEKS * estimate:,.0f}, range {MARGIN * WEEKS * low:,.0f} to {MARGIN * WEEKS * high:,.0f}, "
      f"against {SPEND:,} of ad spend")
print(f"To pay for the ads: {SPEND / MARGIN:.1f} extra orders, {break_even:.2f} a week")
print(f"Redrawn tests in which the ads paid for themselves: {paid} of {len(draws)} ({100 * paid / len(draws):.1f} %)")
print(f"Normal curve with mean 13, spread 2.2: P(15.4 <= X <= 30) = {normal_between(15.4, 30, 13, 2.2):.4f}")

Output

Pune: 51.25 -> 67.00 orders a week
Nashik: 30.00 -> 32.75 orders a week
Added by the ads: 13.00 orders a week, 95 % range 8.75 to 17.25 (bootstrap spread 2.21)

Over 4 weeks: 52 extra orders, range 35 to 69
Their margin: 25,272, range 17,010 to 33,534, against 30,000 of ad spend
To pay for the ads: 61.7 extra orders, 15.43 a week
Redrawn tests in which the ads paid for themselves: 298 of 2000 (14.9 %)
Normal curve with mean 13, spread 2.2: P(15.4 <= X <= 30) = 0.1377

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 geo_holdout.py

The ads clearly added orders: 13 a week, with a 95 % range from 8.75 to 17.25 that stays far from zero. Over four weeks that is 52 extra orders, between 35 and 69. Paying for 30,000 of spend at 486 an order needs 61.7, or 15.43 a week, and the range runs on both sides of it. Only 298 of the 2,000 redrawn tests, 14.9 %, paid. The normal curve below has the estimate’s mean, 13, and its spread, 2.2, and its lower end starts at 15.4, the break-even: read the share of the curve beyond it.

Interactive · change it and see Weekly extra orders: the share above break-even Set the mean and the standard deviation, then the two ends of the range. The shaded area is the probability that a value falls between them.
The bell curve with the range shaded: density against x.510152000.050.10.150.2xdensitydensityμ

For a normal distribution with mean 13 and standard deviation 2.2, the probability of a value between 15.4 and 30 is 0.1377 (13.8 %), from z = 1.09 to z = 7.73.

P(15.4 ≤ X ≤ 30) 0.1377
z-score of a 1.09
z-score of b 7.73
The numbers as a table
Probability within k standard deviations of the mean
RangeProbability
μ ± 1σ0.6827
μ ± 2σ0.9545
μ ± 3σ0.9973

The shaded share is 0.1377, close to the 14.9 % of the redrawn tests. Move the lower end down to 13, the break-even of a cheaper campaign, and half the curve lies beyond it. The honest report gives the range, the method and what would change the decision: “The ads added 35 to 69 orders over four weeks in Pune against Nashik, measured by a difference in differences; at 30,000 of spend they paid for themselves in about one test in seven; a longer test, more cities or a lower spend would settle it.”

Key idea

Attribution shares the credit for orders that happened; only a test with a group kept from the ads shows what they added, and its answer is a range to set against break-even.

A/B Test Significance Calculator For an audience holdout, enter the conversions and the visitors of the exposed and the held-out groups and see whether the difference could be chance.

Checkpoint

Check: Holdouts, geo tests and ranges

  1. Question 1 of 2 A test region goes from 40 to 55 orders a week and its control region from 30 to 33. What is the difference in differences, in orders a week?

    Type a number.

    Show the answer: Question 1 of 2

    Answer: 12 orders a week

    (55 − 40) − (33 − 30) = 15 − 3 = 12. The control's rise of 3 is what would have happened anyway, so it is taken away, as Nashik's 2.75 was in the bakery's test.

  2. Question 2 of 2 The bakery's test found 35 to 69 extra orders over four weeks, and 61.7 were needed to pay for the ads. What is the honest conclusion?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: The ads added orders, but whether they paid cannot be told yet; test longer, in more cities or at a lower spend

    Report the range, the method and what would change the decision. Here the effect is real and the profit uncertain.

Marketing mix models need years of varied spend

A marketing mix model: A statistical model that explains a business's sales over time by its spend on each channel, with carry-over and saturation, without tracking individual people. works from the other end: it explains weekly sales by the spend on each channel, with carry-over and saturation, and estimates what each channel adds without tracking anyone. Two open-source ones are widely known. Google’s Meridian, Apache-2.0, is a Bayesian framework that “enables advertisers to set up and run their own in-house models” (Meridian); its guide asks for weekly data, “a minimum of two years’ worth of weekly data for geo-level models” and three years for national ones (collect and organize your data). Meta’s Robyn, MIT-licensed, uses ridge regression to explore carry-over rates and saturation curves and is “built for granular datasets with many independent variables” (Robyn).

Both need what a small business rarely has: years of weekly sales and spend, with spend that varied enough for the model to see what each change did. The bakery has under a year of steady Instagram spend, which no model can split into causes. Answer the table’s questions for a business you know:

Interactive · answer and compare Which way of judging your marketing fits? Answer four questions about your business. The table rules out the methods that cannot answer, or that your data cannot feed, and says why.
What do you need to know?
How much weekly history of sales and ad spend do you have?
Can you switch the ads off for a region or an audience for some weeks?

Answer the questions above: the table then marks what fits your case and says why the rest does not.

Five methods against what they answer, what they need, how fast and at what effort
Method Says what ads added Data it needs Time to an answer Effort for a small shop
Last non-direct click Reads: the last channel before each order Poor shares credit for orders that happened; says nothing of the ones that would anyway Good tagged visits in GA4 Good in the reports today Good none beyond clean tags
Data-driven attribution Reads: paths with and without key events Mixed estimates each touch from paths; still credit, not a test of the ads Mixed GA4 paths, with aggregate data where an account has little Good in the reports, revised for up to 7 days Good none beyond clean tags and key events
Audience holdout Reads: a group kept from the ads Good compares people who saw the ads with people kept from them Mixed an ad platform that can keep an audience out Mixed weeks of running the test Mixed planning, then waiting; less spend reaches the held group
Geo test Reads: a region with ads, one without Good the bakery's test: 52 extra orders, range 35 to 69 Good weekly orders by city from your own records Mixed 4 weeks before and 4 during, and longer for a tighter range Mixed a region without ads for the test's weeks
Marketing mix model Reads: years of weekly sales and spend Mixed estimates each channel's effect from how sales moved with spend Poor Meridian asks for 2 years of weekly data per region, or 3 nationally Poor months to gather the history and fit the model Poor a statistician's work, and a spend history that varied
How the answers decide
  • “What do you need to know?” Whether the ads add orders at all: rules out Last non-direct click and adds a caveat to Data-driven attribution. Attribution shares the credit for orders that happened; only a test shows the orders that would not have happened without the ads.
  • “What do you need to know?” Which channel to credit in the reports: adds a caveat to Audience holdout, Geo test and Marketing mix model. Tests and models answer a different question, what the ads added; for crediting channels in reports, attribution is the tool.
  • “How much weekly history of sales and ad spend do you have?” Less than two years: rules out Marketing mix model. A marketing mix model learns from how sales moved with spend over time; Meridian's guide asks for at least two years of weekly data.
  • “Can you switch the ads off for a region or an audience for some weeks?” No: rules out Audience holdout and Geo test. A test needs a group or a region kept from the ads; without one, nothing shows what would have happened anyway.
  • “What do you need to know?” How to split a large budget across many channels and “How much weekly history of sales and ad spend do you have?” Two years or more, with spend that varied: adds a caveat to Geo test. With many channels and years of varied spend, a mix model can weigh them all; tests then check its estimates one channel at a time.

For most small businesses the order is attribution in the reports from day one, a test whenever a channel’s budget is big enough to matter, and a mix model only once there are years of varied spend across many channels.

Chart Maker (Graph from Data) Chart weekly orders of a test region and a control region side by side, before and during a test, to see the gap the estimate measures.

Checkpoint

Check: Marketing mix models

  1. Question 1 of 2 How much history does Meridian's guide ask for in a geo-level marketing mix model?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: At least two years of weekly data, and three years for a national model

    A mix model learns from how sales moved with spend over time, which takes years and spend that varied.

  2. Question 2 of 2 A bakery with under a year of steady Instagram spend wants to know whether the ads work. What fits first?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: A geo test or an audience holdout, with attribution in the reports meanwhile

    Attribution from day one, tests when a budget matters, a mix model only with years of varied spend across many channels.

Practise: explain the reports, then measure the ads

The lab works through the bakery’s numbers in two steps: first say why three reports disagree, then write the functions that estimate what the ads added and whether they paid:

Code lab · 2 steps

From three reports to one verdict

Explain why the bakery's reports disagree, then estimate what the Instagram ads added and whether they paid for themselves.

  1. Explain three gaps between two reports To do
  2. Estimate what the ads added, and whether it paid To do

Exercise · Easy · Python

Explain three gaps between two reports

The bakery compared its reports for one week and wrote down three gaps, in GAPS in gaps.py. Each has one main cause among those in CAUSES:

  • own-credit: each ad platform counts every order that follows its own ad, while GA4 gives each order to one channel;
  • views: a platform counts orders from people who saw an ad without clicking it, which GA4 never sees;
  • reattribution: data-driven attribution can reattribute conversions for up to 7 days, so recent numbers move;
  • consent: visitors who refused consent are measured less or not at all in GA4.

Fill in ANSWERS with the cause of each gap, and write one sentence for each in WHY that a colleague could check against the reports. The sample tests check the causes and that every gap has its sentence.

Starter code · gaps.py

"""Three gaps between the bakery's reports for one week, and their causes. The bakery is fictional."""
CAUSES = {"own-credit", "views", "reattribution", "consent"}

GAPS = {
    "g1": "The social ad platform reports 61 orders for its campaign; GA4's last non-direct click gives Paid Social 22, "
          "and most of the other 39 orders ended with a Google search.",
    "g2": "Of the platform's 61 orders, 15 came from people who saw an ad in their feed and never clicked it.",
    "g3": "On Monday GA4's data-driven report gave Instagram ads 31 of last week's orders; on Friday it gave them 34.",
}

ANSWERS = {"g1": "consent", "g2": "consent", "g3": "consent"}

WHY = {}
The sample tests · test_gaps.py
from gaps import ANSWERS, CAUSES, GAPS, WHY


def test_every_gap_has_a_known_cause():
    """gives each of the three gaps one cause from CAUSES"""
    assert set(ANSWERS) == set(GAPS)
    assert all(cause in CAUSES for cause in ANSWERS.values()), ANSWERS


def test_platform_counts_its_own_orders():
    """sees that the platform claims every order after its own ad"""
    assert ANSWERS["g1"] == "own-credit"


def test_views_are_invisible_to_ga4():
    """sees that orders after a view without a click exist only in the platform"""
    assert ANSWERS["g2"] == "views"


def test_recent_numbers_still_move():
    """sees that data-driven attribution revises recent conversions"""
    assert ANSWERS["g3"] == "reattribution"


def test_every_gap_has_a_sentence():
    """explains each gap in a sentence a colleague could check"""
    assert set(WHY) == set(GAPS)
    for gap, sentence in WHY.items():
        assert len(sentence.split()) >= 8, f"{gap}: write a full sentence"
A hint

Ask, for each gap, which tool can see something the other cannot. A platform sees its own ad views; GA4 sees every channel of a path but only the visitors it may measure; and a number that changes after the week ended has been recalculated, not recounted.

The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Exercise · Medium · Python

Estimate what the ads added, and whether it paid

The bakery ran its Instagram ads in Pune for 4 weeks and held them back in Nashik. lift.py holds the weekly orders of both cities, 4 weeks before the test and 4 weeks during it. Write three functions:

  1. did(test_before, test_during, control_before, control_during): the difference in differences, the change in the test city's average weekly orders minus the change in the control city's, in orders a week;
  2. profit(extra_orders, margin, spend): what the extra orders leave after paying for the ads, at a margin per order;
  3. verdict(low, high, needed): from the range of extra orders a test found and the extra orders needed to pay for the ads, "pays" when even the low end is enough, "does not pay" when even the high end falls short, and "cannot tell yet" otherwise.

The sample tests use the bakery's numbers and two other cases.

Starter code · lift.py

"""What the bakery's Instagram ads added in Pune, measured against Nashik, and whether it paid. The bakery is fictional."""
from statistics import mean

PUNE_BEFORE, PUNE_DURING = [52, 48, 55, 50], [66, 70, 63, 69]
NASHIK_BEFORE, NASHIK_DURING = [30, 28, 33, 29], [33, 31, 35, 32]


def did(test_before, test_during, control_before, control_during):
    """The difference in differences of average weekly orders."""
    return mean(test_during) - mean(test_before)


def profit(extra_orders, margin, spend):
    """What the extra orders leave after the ad spend."""
    return extra_orders * margin


def verdict(low, high, needed):
    """'pays', 'does not pay' or 'cannot tell yet'."""
    return "pays"
The sample tests · test_lift.py
from lift import NASHIK_BEFORE, NASHIK_DURING, PUNE_BEFORE, PUNE_DURING, did, profit, verdict


def test_did_on_the_bakerys_test():
    """finds 13 extra orders a week in Pune, after Nashik's own rise of 2.75"""
    assert abs(did(PUNE_BEFORE, PUNE_DURING, NASHIK_BEFORE, NASHIK_DURING) - 13.0) < 1e-9


def test_did_subtracts_the_control():
    """takes away what moved the control city too"""
    assert abs(did([10, 10], [15, 15], [20, 20], [22, 22]) - 3.0) < 1e-9
    assert abs(did([40, 42], [41, 43], [30, 30], [25, 25]) - 6.0) < 1e-9


def test_profit_pays_for_the_ads_first():
    """leaves a loss of 4,728 for 52 extra orders at 486 against 30,000 of spend"""
    assert profit(52, 486, 30000) == -4728
    assert profit(70, 486, 30000) == 4020


def test_verdict_reads_the_whole_range():
    """says pays, does not pay or cannot tell yet from both ends of the range"""
    assert verdict(35, 69, 61.7) == "cannot tell yet"
    assert verdict(65, 80, 61.7) == "pays"
    assert verdict(10, 40, 61.7) == "does not pay"
    assert verdict(61.7, 90, 61.7) == "pays"
A hint

statistics.mean gives the average of a list. The difference in differences is (mean(test_during) - mean(test_before)) - (mean(control_during) - mean(control_before)). For verdict, compare the low end with what is needed first, then the high end.

The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Finally, test yourself on the five ideas, from shared credit to mix models; the questions lead with your shakiest one:

Practice that adapts to you

Practise this lesson

10 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 10 Which attribution models does GA4 offer? Choose every one.

    Choose every answer that is right.

    Show the answer: Question 1 of 10

    Answer:

    • Paid and organic last click
    • Google paid channels last click
    • Data-driven

    Three models remain; first click, linear, time decay and position-based were removed. The lesson shows two of them only to see how a rule moves credit.

  2. Question 2 of 10 In the program's 500 orders, how many more orders does position-based give Instagram ads than last non-direct click does?

    Type a number.

    Show the answer: Question 2 of 10

    Answer: 97 orders

    136 − 39 = 97. The same orders, shared by another rule, more than triple Instagram's credit, which is why a rule decides credit rather than discovering it.

  3. Question 3 of 10 What does GA4's data-driven attribution study that last click ignores?

    Choose one answer.

    Show the answer: Question 3 of 10

    Answer: The paths of people who did not convert, as well as those who did

    Data-driven attribution contrasts what happened with what could have occurred; for Google ad exposures, its gains come from training on data from randomised controlled trials.

  4. Question 4 of 10 Where is the attribution model of GA4's key event reports chosen?

    Choose one answer.

    Show the answer: Question 4 of 10

    Answer: In Admin, under Data display, Events, then Attribution settings, beside the key event lookback window

    The setting changes how key event reports share credit, not what was collected.

  5. Question 5 of 10 What is Google Ads' default click-through conversion window, in days?

    Type a number.

    Show the answer: Question 5 of 10

    Answer: 30 days

    30 days by default, adjustable from 1 to 90; engaged views default to 3 days and views to 1 day.

  6. Question 6 of 10 Which source should answer "How many orders did we take last month?"

    Choose one answer.

    Show the answer: Question 6 of 10

    Answer: The business's own order records

    One source of truth per question: own records for totals, GA4 for the channel split, platforms inside themselves, tests for what ads added.

  7. Question 7 of 10 Why does a geo test need a control region?

    Choose one answer.

    Show the answer: Question 7 of 10

    Answer: The control shows what would have happened without the ads, so the season and other changes cancel out

    Without a control, a before-and-after comparison mixes the ads with everything else that changed.

  8. Question 8 of 10 A four-week test costs 30,000 of ads, and each order leaves a margin of 486. How many extra orders a week must the ads bring to pay for themselves? Give two decimals.

    Type a number.

    Show the answer: Question 8 of 10

    Answer: 15.43 orders a week (anything from 15.42 to 15.44 counts)

    30,000 ÷ 486 = 61.7 orders over four weeks, 61.7 ÷ 4 = 15.43 a week. The bakery's estimate of 13 a week, with a range up to 17.25, straddles it.

  9. Question 9 of 10 What does a marketing mix model do?

    Choose one answer.

    Show the answer: Question 9 of 10

    Answer: It explains weekly sales by the spend on each channel, with carry-over and saturation, without tracking people

    Meridian and Robyn are open-source mix models; both need years of weekly data with spend that varied.

  10. Question 10 of 10 For which business is a marketing mix model such as Robyn most likely to be worth building?

    Choose one answer.

    Show the answer: Question 10 of 10

    Answer: A retailer with years of weekly sales and varied spend across many channels

    The more channels, years and variation in spend, the more a mix model can tell apart; small businesses usually start with tests.

Interview questions

Warm-up (fresher to 2–5 years): attribution or incrementality, and the question each one answers

Attribution shares the credit for conversions that happened among the channels on their paths, by a rule such as last non-direct click or by a model such as GA4’s data-driven attribution; it answers which channels to credit in the reports. Incrementality asks how many conversions would not have happened without the marketing, and it needs a comparison with a group kept from the ads, such as an audience holdout or a geo test with a control region. The two can differ widely: a channel that closes most paths gets much credit from last click, while the test may show it added little. Attribution is in the reports every day; incrementality needs a test, and gives a range rather than one number.

Two more questions follow, about a platform that claims more orders than it caused and about designing a test. Choose your first move for each, then open it:

Checkpoint

Your first decision

  1. Question 1 of 2 Interview brief: a platform claims 61 orders from a month's 30,000 of spend, GA4 credits it with 22, and the owner asks which is true. What do you say first?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Neither is what the ads added; they count credit by different rules, and a test is how to measure the increment

    Separate the questions: credit in reports, and what the ads added. Only a comparison with a group kept from the ads answers the second.

  2. Question 2 of 2 Interview brief: the owner wants to know whether Instagram ads bring orders she would not have had anyway, and she sells in two cities. What do you propose?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: A geo test, ads in one city and none in the other, with weeks before and during, read as a difference in differences with a range

    Keep a comparable group from the ads, decide the length and the break-even first, and report a range.

Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.

  1. Mid to senior

    A bakery spent 30,000 rupees on social ads in a month. The ad platform claims 61 orders, GA4's last click credits it with 22, and each order leaves a margin of 486. The owner asks which number is true and whether the ads paid. How do you answer?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Mid to senior

    A bakery sells in Pune and Nashik and wants to know whether its Instagram ads bring orders it would not have had anyway. Design the test: what you compare, for how long, how you read it and what could spoil it.

    Model answer, Pro. First step and follow-ups

Key takeaways

  • An attribution model shares the credit for an order among the channels on its path; GA4 offers data-driven, paid and organic last click and Google paid channels last click, gives Direct credit only for all-direct paths, and removed first click, linear, time decay and position-based in 2023.
  • Data-driven attribution learns from paths that converted and paths that did not, revises conversions for up to 7 days, and is still attribution, not a test of your ads.
  • Every ad platform counts the orders after its own ads with its own windows and views, so totals disagree by design; choose one source of truth for each question.
  • Incrementality needs a group kept from the ads: a holdout or a geo test, read as a difference in differences with a range, and judged against the extra orders needed to break even.
  • Marketing mix models such as Meridian and Robyn need years of weekly, varied spend; most small businesses learn more from simple tests first.

Terms in this lesson

attribution model
A rule or a model that decides how the credit for a conversion is shared among the channels on the path before it, such as last non-direct click or data-driven attribution.
data-driven attribution
GA4's attribution model that shares credit by each touchpoint's estimated contribution, learned from paths that converted and paths that did not. It can revise recent conversions for up to 7 days.
holdout group
The people, or the region, deliberately kept from a campaign during a test, so that their results show what would have happened without the ads.
incrementality
The difference between what happened with the marketing and what would have happened without it, measured against a group or a region kept from the ads.
marketing mix model
A statistical model that explains a business's sales over time by its spend on each channel, with carry-over and saturation, without tracking individual people.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.