Multi-Touch Attribution with Shapley Values in Snowflake and Snowpark

Quick answer: Shapley values share each conversion fairly among the channels that contributed, using a method from game theory instead of arbitrary position weights. Bring every ad platform's data into Snowflake, map it to one touchpoint model, build journeys, and compute each channel's credit with a short Snowpark job that runs per client. Credits always add up to the actual number of conversions, which fixes the over-counting you get from adding up each platform's own reports. For a marketing agency, this enabled an 18% improvement in client ROAS by shifting budget between channels with total spend unchanged.

Last updated: October 2026

Every ad platform takes full credit for every conversion it touched. Add up the conversions reported by Google Ads, Meta and LinkedIn for the same campaign period and you get more conversions than actually happened, and last-click attribution, the usual fallback, gives all the credit to whichever channel happened to come last.


This article describes the attribution model we built for a digital marketing agency, covered in our marketing attribution case study. The agency managed $2.8 million a month of paid media for 23 clients across five ad platforms, and four analysts spent 16 hours every Monday copying platform exports into spreadsheets. Project details come from that engagement. The Python is a tested illustration written for this article.


Why the platforms' numbers never add up


Each platform has its own attribution window, its own definition of a conversion and its own way of counting the same person. A prospect might click a LinkedIn ad on Tuesday, a Google search ad on Thursday and convert through a Meta retargeting ad on Saturday. Under last-click attribution, Meta gets all the credit and LinkedIn and Google get none, while each platform's own report claims the conversion for itself.


Clients kept asking which channel was actually driving revenue, and at $2.8 million a month in combined spend, not knowing was an expensive answer.


One touchpoint model across five platforms


We used Hevo's pre-built connectors for Google Ads, Meta Ads, LinkedIn Ads, TikTok Ads and Microsoft Advertising (Bing Ads until April 2019), plus Google Analytics 4. Hevo handles the API pagination, rate limits and schema changes that make ad platform integrations hard to maintain, and all six sources were syncing into Snowflake within two days, every six hours.


The real work was the normalisation layer. Every click, impression and conversion maps to a common schema: client_id, platform, campaign_name, touchpoint_timestamp, event_type, cost and revenue. The platforms name their levels differently, so the model has to decide which level of each hierarchy counts as the same grain:


PlatformHierarchy (top to bottom)
Google AdsAccount, campaign, ad group
MetaAd account, campaign, ad set, ad
LinkedInSponsored account, campaign group, campaign, creative
TikTokCampaign, ad group, ad
Microsoft AdvertisingAccount, campaign, ad group

LinkedIn's own documentation maps its campaign group to a Google or Meta campaign, and its campaign to an ad group or ad set, which is a useful starting point. One prerequisite is worth stating plainly: Shapley attribution needs journeys, meaning touchpoints that can be tied to the same person or account, and platform reporting on its own provides aggregates. In practice, journeys usually come from event-level data, such as web analytics sessions tagged with campaign parameters.


What a Shapley value is


Lloyd Shapley introduced the value in 1953 as a way to share a payoff fairly among players in a cooperative game. Applied to attribution, the players are channels and the payoff is conversions. A channel's credit is its marginal contribution, the conversions gained by adding it to a group of other channels, averaged over every possible order in which the channels could be added. The credits always sum to the total number of conversions, so nothing is counted twice.


The exact calculation looks at every subset of channels, 2n of them for n channels, which is trivial for five or six channels and impractical for hundreds of touchpoint types. For larger problems, marketing researchers use simplified formulations, such as the one by Zhao, Mahboobi and Bagheri (2018), and Google's Ads Data Hub documents a Simplified Shapley Value Method based on it, capped at 150 touchpoints. Google Analytics 4 and Google Ads data-driven attribution, by contrast, describe their own machine-learning, counterfactual approach and do not say they use Shapley values.


Computing it in Snowpark


The model runs nightly as a Python job in Snowpark. The function below takes one client's journeys, each with the set of channels it touched and whether it converted. The worth of a group of channels is the number of conversions from journeys that touched only channels in that group, which is the coalition value used in the simplified formulation above. We tested this code against a brute-force calculation over every ordering of channels, and the credits always sum to the client's total conversions.


Exact Shapley credit per channel for one client (Python, tested)
from itertools import combinations
from math import factorial
import pandas as pd

def shapley_for_client(df: pd.DataFrame) -> pd.DataFrame:
    """One row per journey: CLIENT_ID, CHANNELS (comma-separated), CONVERTED (0 or 1)."""
    sets = df["CHANNELS"].str.split(",").apply(frozenset)
    conversions = df.assign(CHANNEL_SET=sets).groupby("CHANNEL_SET")["CONVERTED"].sum().to_dict()
    channels = sorted(set().union(*sets))
    n = len(channels)

    def worth(coalition):
        # Conversions from journeys that touched only channels in the coalition.
        return sum(c for touched, c in conversions.items() if touched <= coalition)

    rows = []
    for ch in channels:
        others = [c for c in channels if c != ch]
        credit = 0.0
        for size in range(len(others) + 1):
            weight = factorial(size) * factorial(n - size - 1) / factorial(n)
            for subset in combinations(others, size):
                s = frozenset(subset)
                credit += weight * (worth(s | {ch}) - worth(s))
        rows.append((df["CLIENT_ID"].iloc[0], ch, credit))
    return pd.DataFrame(rows, columns=["CLIENT_ID", "CHANNEL", "ATTRIBUTED_CONVERSIONS"])

Snowpark runs the function once per client with a group-by apply, which executes it as a vectorised UDTF inside Snowflake, so journey data never leaves the platform. The pandas package must be available to the job.


Run it for every client and save the results (Snowpark Python)
from snowflake.snowpark.types import StructType, StructField, StringType, FloatType

journeys = session.table("attribution.client_journeys")   # CLIENT_ID, CHANNELS, CONVERTED

credits = journeys.group_by("CLIENT_ID").apply_in_pandas(
    shapley_for_client,
    output_schema=StructType([
        StructField("CLIENT_ID", StringType()),
        StructField("CHANNEL", StringType()),
        StructField("ATTRIBUTED_CONVERSIONS", FloatType()),
    ]),
)

credits.write.save_as_table("attribution.channel_credit", mode="overwrite")

Explaining the result to clients


The mathematics is sound, but most marketing managers have never heard of a Shapley value. On the client dashboards, the result appears as a simple contribution score, each channel's share of conversions as a percentage, such as Google driving 42% of a client's conversions. That is what stakeholders need in order to act.


Each client has a dashboard built from one template with parameterised data sources, showing attributed revenue by channel, cost per attributed conversion and ROAS by platform. The tool the agency used, Looker Studio, was renamed Data Studio in April 2026. Its parameters can be set through the report URL when the report owner enables it, and scheduled delivery emails a PDF of a report to up to 50 recipients; without the Pro edition, a report can have one schedule at most daily frequency. The client reports are generated on Sunday night and arrive before Monday morning, so the Monday spreadsheet routine is gone.


Where the improvement came from


The 18% improvement in client ROAS came from shifting budget. Once the agency could see that TikTok was getting last-click credit for conversions that LinkedIn had initiated, it moved 15 to 20% of budget from over-attributed channels to under-attributed ones. Total spend stayed the same and conversions went up.


Client retention rose from 72% to 91%, because quarterly reviews could finally show which channels contributed what and where the next dollar should go. The 16 hours a week of manual reporting fell to zero, and all five platforms had multi-touch attribution for the first time.


Pranay Vatsal, Founder & CEO

Pranay Vatsal is the Founder & CEO of CelestInfo with deep expertise in Snowflake, data architecture, and building production-grade data systems for global enterprises.

Related Articles

Frequently Asked Questions

Q: What is Shapley value attribution?

It shares each conversion among the channels that contributed, giving each channel its average marginal contribution across every possible order in which the channels could be added. The method comes from Lloyd Shapley's 1953 work in game theory, and the credits always add up to the total number of conversions.

Q: Is Google Analytics data-driven attribution based on Shapley values?

Google does not describe it that way. GA4 and Google Ads data-driven attribution use machine learning and a counterfactual approach. The Google product that documents Shapley values is Ads Data Hub, which uses a Simplified Shapley Value Method capped at 150 touchpoints.

Q: How expensive is it to compute Shapley values?

The exact calculation considers every subset of channels, 2 to the power n for n channels. That is fast for a handful of channels and impractical for hundreds, where simplified formulations or sampling are used.

Q: How do I run Python per group in Snowflake?

Use Snowpark's group_by(...).apply_in_pandas(func, output_schema=...), which passes each group to your function as a pandas DataFrame and runs it inside Snowflake as a vectorised UDTF.

Q: What happened to Looker Studio?

Google renamed Looker Studio as Data Studio in April 2026, and the Pro edition became Data Studio Pro. Reports, parameters and scheduled delivery continue under the new name.