Skip to content

Multi-Touch Attribution Modeling: From Last-Click to Data-Driven

A deep dive into multi-touch attribution using Shapley values, Markov chains, and data-driven approaches — comparing methods, building production systems, and translating attribution insights into media budget optimization.

Multi-Touch Attribution Modeling: From Last-Click to Data-Driven

The question “which marketing channel gets credit for this conversion?” is deceptively simple. The answer — multi-touch attribution (MTA) — is one of the most technically challenging and politically contentious problems in marketing analytics. Every channel manager believes their channel deserves more credit. Every CFO wants a definitive answer. And the reality is that attribution is an inherently unsolvable problem — we can never truly know the counterfactual of what would have happened without a given touchpoint.

What we can do is build increasingly sophisticated models that approximate the truth better than the naive approaches that still dominate most organizations. In this post, I will walk through the evolution from last-click attribution to data-driven models using Shapley values and Markov chains, and share the hard lessons I have learned about building attribution systems that actually change how companies spend their marketing budgets.

The Problem With Last-Click Attribution

Last-click attribution — giving 100% of the credit to the final touchpoint before conversion — remains the default in most organizations because it is simple to implement and understand. But it has severe, well-documented biases:

Overvalues bottom-of-funnel channels: Paid search brand terms, retargeting, and email — channels that intercept customers already in the purchase decision — get disproportionate credit. These channels look great under last-click but often have low incremental impact.

Undervalues top-of-funnel channels: Display advertising, social media awareness campaigns, content marketing, and influencer campaigns — channels that create initial awareness — get zero credit. This leads to systematic underinvestment in demand generation.

Creates a self-fulfilling prophecy: When you cut budget from undervalued top-of-funnel channels, the pipeline of prospects shrinks, which reduces the volume for bottom-of-funnel channels, which eventually shows up as declining performance across all channels.

In one engagement, I found that last-click attribution allocated 45% of credit to branded paid search — a channel that primarily captures demand created by other channels. The true incremental contribution was closer to 10%. The company was over-investing in branded search by 35 percentage points.

Multi-Touch Attribution: The Data

Before discussing models, we need to discuss data. MTA requires user-level journey data — a complete record of every marketing touchpoint a user encountered before converting (or not converting).

Data Requirements

Identity resolution: The hardest data challenge is linking touchpoints across devices and channels to a single user. A customer might see a Facebook ad on their phone, click a Google search ad on their laptop, and receive an email before purchasing in-store. Without a unified customer ID, this journey appears as three separate, single-touchpoint journeys.

I use a combination of deterministic matching (email, phone, login ID) and probabilistic matching (device fingerprinting, IP address, behavioral signals) to build a unified identity graph. The match rate typically ranges from 40–70% depending on the industry and data availability.

Touchpoint data: Every marketing interaction with its timestamp, channel, campaign, creative, placement, and cost. Sources include:

  • Ad platform APIs (Google, Meta, TikTok) for paid media impressions and clicks
  • Web analytics (Google Analytics, Adobe Analytics) for owned media interactions
  • Email platform data for email touchpoints
  • CRM data for sales interactions
  • Offline data (direct mail, events) where available

Conversion data: The outcome variable — purchase, lead, subscription — with its timestamp and value.

Non-converter data: Critically, you also need journey data for users who did NOT convert. Without non-converter data, you cannot distinguish between channels that drive incremental conversions and channels that simply appear in converter journeys because they have high reach.

Data Quality Challenges

Lookback window: How far back do you trace the journey? I typically use a 30-day lookback for e-commerce and 90 days for considered purchases (B2B, automotive). Too short misses early touchpoints; too long introduces noise.

Impression vs. interaction: Should you count view-through impressions (the ad was shown but not clicked)? I include view-through impressions with a decay factor — an impression 7 days ago is worth less than one yesterday. Click-through interactions get full credit.

Cross-device gaps: Even with identity resolution, some cross-device journeys are invisible. This creates a systematic bias toward channels with strong identity signals (logged-in platforms like Google and Facebook) and against channels with weak identity signals (display, CTV).

Shapley Value Attribution

The Shapley value, borrowed from cooperative game theory, provides a principled framework for distributing credit among touchpoints. The idea is: for each channel, calculate its average marginal contribution across all possible subsets of channels.

The Mathematical Foundation

For a set of channels N = {1, 2, …, n} and a value function v(S) that gives the conversion probability for a subset S of channels, the Shapley value of channel i is:

φ_i = Σ_{S ⊆ N\{i}} [|S|!(|N|-|S|-1)! / |N|!] × [v(S ∪ {i}) - v(S)]

This is the average marginal contribution of channel i across all possible orderings of channels.

Practical Implementation

Computing exact Shapley values requires evaluating the value function for all 2^n subsets, which is exponential. For a reasonable number of channels (10–15), this is tractable. For larger channel sets, I use sampling-based approximations.

import numpy as np
from itertools import combinations

def compute_shapley_values(conversion_data, channels):
    """
    Compute Shapley values for marketing channels.
    
    conversion_data: dict mapping frozenset of channels -> conversion count
    channels: list of channel names
    """
    n = len(channels)
    shapley = {ch: 0.0 for ch in channels}
    
    for channel in channels:
        other_channels = [c for c in channels if c != channel]
        
        for subset_size in range(len(other_channels) + 1):
            for subset in combinations(other_channels, subset_size):
                subset_set = frozenset(subset)
                with_channel = subset_set | {channel}
                
                # Marginal contribution
                v_without = conversion_data.get(subset_set, 0)
                v_with = conversion_data.get(with_channel, 0)
                marginal = v_with - v_without
                
                # Weight
                s = len(subset)
                weight = (np.math.factorial(s) * np.math.factorial(n - s - 1)) / np.math.factorial(n)
                
                shapley[channel] += weight * marginal
    
    return shapley

The Value Function Challenge

The key design decision is the value function v(S). What is the conversion probability when a specific subset of channels is present?

I have tried several approaches:

Empirical conversion rate: For each subset S, compute the conversion rate among users whose journey included exactly the channels in S. The problem is data sparsity — with 10 channels, there are 2^10 = 1024 subsets, and many will have very few users.

Model-based conversion rate: Train a logistic regression or gradient boosting model to predict conversion from channel presence features. Then use the model to estimate v(S) for any subset, including those not observed in the data. This is my preferred approach because it handles data sparsity through model generalization.

from sklearn.linear_model import LogisticRegression

# Each row is a user journey, columns are binary channel indicators
# Plus additional features like number of touchpoints, time span, etc.
X = create_channel_features(journey_data)  # binary channel indicators
y = conversion_labels

model = LogisticRegression(C=1.0, max_iter=1000)
model.fit(X, y)

# Value function: predicted conversion probability for any subset
def value_function(channel_subset):
    features = encode_channels(channel_subset)
    return model.predict_proba(features)[:, 1]

Shapley Value Strengths and Limitations

Strengths:

  • Theoretically grounded (the only attribution method with axiomatic guarantees of fairness)
  • Satisfies the efficiency property (credits sum to total conversions)
  • Handles channel interactions naturally

Limitations:

  • Assumes the value function is well-defined and stable
  • Does not account for touchpoint order (a Facebook ad before Google search might have different impact than the reverse)
  • Computationally expensive for many channels
  • Still a correlational, not causal, method

Markov Chain Attribution

Markov chain attribution models the customer journey as a stochastic process, where each touchpoint is a state and transitions between states have probabilities. The contribution of each channel is measured by its removal effect — how much the conversion probability drops when the channel is removed from the journey graph.

Building the Markov Chain

import numpy as np

def build_transition_matrix(journeys):
    """
    Build a transition probability matrix from customer journeys.
    
    journeys: list of lists, each inner list is a sequence of channels
    """
    states = set()
    for journey in journeys:
        states.update(journey)
    states.add('Start')
    states.add('Conversion')
    states.add('Null')  # non-conversion absorbing state
    
    transitions = {}
    for journey in journeys:
        # Add start state
        path = ['Start'] + journey + ['Conversion']
        for i in range(len(path) - 1):
            current = path[i]
            next_state = path[i + 1]
            if current not in transitions:
                transitions[current] = {}
            transitions[current][next_state] = transitions[current].get(next_state, 0) + 1
    
    # Convert counts to probabilities
    transition_matrix = {}
    for state, next_states in transitions.items():
        total = sum(next_states.values())
        transition_matrix[state] = {s: c / total for s, c in next_states.items()}
    
    return transition_matrix

Removal Effect

The removal effect of a channel is computed by:

  1. Calculate the baseline conversion probability from the Markov chain
  2. Remove the channel (merge its incoming and outgoing transitions)
  3. Recalculate the conversion probability
  4. The removal effect is the difference
def removal_effect(transition_matrix, channel):
    """Calculate the removal effect of a channel."""
    # Baseline conversion probability
    baseline = simulate_conversion_probability(transition_matrix)
    
    # Remove channel: merge transitions
    modified_matrix = remove_channel(transition_matrix, channel)
    modified_prob = simulate_conversion_probability(modified_matrix)
    
    return baseline - modified_prob

Markov Chain Strengths and Limitations

Strengths:

  • Captures touchpoint order and transitions
  • Removal effect provides an intuitive interpretation
  • Handles variable journey lengths naturally

Limitations:

  • First-order Markov assumption (each state depends only on the previous state) may be too simplistic
  • Assumes transition probabilities are stationary (do not change over time)
  • Can be unstable with sparse data
  • Does not account for time between touchpoints

Higher-Order Markov Chains

To capture more of the journey context, I sometimes use second-order Markov chains, where the transition probability depends on the previous two states. This doubles the state space but captures important patterns like “Display → Search → Conversion” having different probabilities than “Email → Search → Conversion.”

Data-Driven Attribution: The Production Approach

In production, I combine insights from Shapley values and Markov chains into a unified data-driven attribution model. Here is my production architecture:

Step 1: Journey Assembly

Build user-level journeys from the identity-resolved touchpoint data. Each journey is a sequence of (channel, timestamp) pairs, terminated by either conversion or a timeout (30 days of inactivity).

Step 2: Feature Engineering

For each journey, compute features:

  • Channel sequence (as one-hot encoded presence AND as ordered sequence)
  • Number of touchpoints per channel
  • Time between touchpoints
  • Total journey duration
  • Channel diversity
  • Day-of-week and time-of-day patterns

Step 3: Conversion Propensity Model

Train a model to predict conversion from journey features. I use gradient boosting for its ability to capture complex interactions.

Step 4: Attribution Computation

Use Shapley values (computed via the conversion propensity model as the value function) to distribute credit. This gives a channel-level attribution.

For touchpoint-level attribution (which specific Facebook ad impression gets credit), I use the model to compute marginal contributions at the touchpoint level by simulating the removal of each touchpoint.

Step 5: Reconciliation with Media Mix Model

I reconcile MTA results with Media Mix Model (MMM) results, which use aggregate data and can capture offline channels and long-term effects that MTA misses. Discrepancies between MTA and MMM indicate data quality issues or model limitations that need investigation.

Comparing Attribution Models: What I Have Found

After deploying multiple attribution approaches across several organizations, here are my consistent findings:

Last-click vs. Shapley: Shapley typically shifts 20–40% of credit from bottom-of-funnel (branded search, retargeting) to mid-funnel (social, non-brand search) and top-of-funnel (display, video).

Shapley vs. Markov: Results are usually correlated (r > 0.8) but not identical. Markov tends to give slightly more credit to channels that appear early in the journey, while Shapley is more balanced.

Linear vs. data-driven: Linear attribution (equal credit to all touchpoints) is actually not terrible as an improvement over last-click. Data-driven models provide additional value of 10–15% in terms of optimizing media allocation.

The real value is not the model but the process: The most valuable outcome of building an attribution system is not the attribution numbers themselves but the organizational shift toward data-driven media allocation and the discipline of testing incrementality.

Translating Attribution to Media Budget Optimization

Attribution numbers are useless unless they change how you allocate budget. Here is my framework:

Step 1: Compute Channel Efficiency

For each channel, compute: Efficiency = Attributed Conversions / Channel Cost

Under last-click, branded search looks efficient. Under Shapley attribution, the picture changes dramatically — display and social often look much more efficient when properly credited.

Step 2: Identify Optimization Opportunities

Compare each channel’s attributed efficiency against its marginal efficiency (what happens when you increase or decrease spend by 10%). Channels with high attributed efficiency but low marginal efficiency are saturated. Channels with low attributed efficiency but high marginal efficiency are under-invested.

Step 3: Reallocate Budget

Shift budget from saturated channels to under-invested channels. The expected improvement is: ΔRevenue = Σ(ΔSpend_i × Marginal_ROAS_i)

Step 4: Test and Iterate

Implement the reallocation as an A/B test. Measure the actual impact on total conversions and revenue. Update the attribution model with the new data. Repeat.

Lessons Learned

Lesson 1: Perfect attribution is impossible. Embrace the uncertainty. Use multiple models and triangulate.

Lesson 2: Identity resolution is the bottleneck. The best attribution model in the world is useless without good user-level journey data. Invest in identity resolution first.

Lesson 3: Incrementality testing is the ultimate validation. Attribution models give correlational estimates. Only randomized experiments (geo-lift tests, holdout tests) can establish causality. I run incrementality tests for each major channel quarterly.

Lesson 4: Organizational alignment is harder than the math. Getting channel managers to accept attribution results that reduce their channel’s credit is a political challenge. Frame it as “optimizing the portfolio” rather than “cutting your channel.”

Lesson 5: Start simple, add complexity incrementally. Moving from last-click to linear attribution is a huge improvement. Moving from linear to Shapley is a moderate improvement. Moving from Shapley to a complex deep learning attribution model provides marginal additional value. Match the complexity to the decision stakes.

Conclusion

Multi-touch attribution is one of the most impactful applications of data science in marketing. The evolution from last-click to Shapley value or Markov chain-based attribution typically reveals 20–40% misallocation in media budgets — misallocation that costs real money.

The technical approaches — Shapley values for principled credit allocation, Markov chains for capturing journey dynamics, gradient boosting for modeling conversion propensity — are well-established. The real challenges are data quality (identity resolution), organizational alignment (accepting that your channel is over-credited), and the discipline of continuous testing and optimization.

Build the system, validate with incrementality tests, and use attribution as a compass — not a GPS. It points you in the right direction, but you still need to test each step.