Analyze Marketing Attribution and Optimize Spend
A skill for building marketing attribution models - multi-touch attribution, marketing mix modeling, and incrementality testing.
Maintainer of this project? Claim this page to edit the listing.
1.0.0Add to Favorites
Why it matters
Leverage advanced attribution models and marketing mix modeling to understand customer journeys and optimize marketing spend for maximum ROI.
Outcomes
What it gets done
Implement and analyze single-touch, multi-touch, and data-driven attribution models.
Develop and apply marketing mix models to measure the impact of various marketing channels.
Provide insights into customer journey mapping and cross-channel effectiveness.
Generate SQL and Python code for attribution analysis and MMM implementation.
Install
Add it to your toolbox
Run in your project directory:
curl -fsSL https://spark.entire.vc/get/vb-marketing-attribution-model | bash Overview
Marketing Attribution Model Expert
A skill for building marketing attribution models - single- and multi-touch attribution, data-driven attribution via logistic regression, marketing mix modeling with adstock/saturation curves, and incrementality testing. Use it to choose and validate an attribution model matched to your sales cycle and channel mix - it insists on data-quality prerequisites (user ID stitching, complete tracking) before trusting any model's output.
What it does
This skill applies marketing attribution modeling across single-touch, multi-touch, and marketing mix approaches. Single-touch models: first-touch (credits the first interaction, good for brand-awareness measurement), last-touch (credits the final interaction, the default in many platforms), and last non-direct click (excludes direct traffic to isolate marketing channels). Multi-touch models: linear (equal credit across touchpoints), time-decay (more credit to recent interactions), U-shaped/position-based (40% first touch, 40% last touch, 20% distributed), W-shaped (emphasizes first touch, lead creation, and opportunity creation), and data-driven attribution using machine learning to determine optimal credit distribution.
For data collection it defines a customer-touchpoint schema and an aggregated attribution view:
-- Customer Journey Data Structure
CREATE TABLE customer_touchpoints (
user_id VARCHAR(255),
session_id VARCHAR(255),
touchpoint_timestamp TIMESTAMP,
channel VARCHAR(100),
campaign VARCHAR(255),
medium VARCHAR(100),
source VARCHAR(100),
content VARCHAR(255),
conversion_event BOOLEAN DEFAULT FALSE,
conversion_value DECIMAL(10,2),
touchpoint_sequence INTEGER
);
-- Attribution Analysis View
CREATE VIEW attribution_analysis AS
SELECT
user_id,
channel,
campaign,
COUNT(*) as touchpoint_count,
SUM(CASE WHEN conversion_event THEN 1 ELSE 0 END) as conversions,
SUM(conversion_value) as total_value,
MIN(touchpoint_timestamp) as first_touch,
MAX(touchpoint_timestamp) as last_touch
FROM customer_touchpoints
GROUP BY user_id, channel, campaign;
A Python attribution calculator implements first-touch attribution (grouping by first interaction per user), time-decay attribution (exponential decay weighted by a configurable half-life, normalized per user journey), and data-driven attribution (a logistic regression over channel/campaign/time/device features, using coefficient magnitude as a Shapley-value approximation for credit). For channel-level spend effectiveness it implements Marketing Mix Modeling: an adstock transform for carryover effects, a saturation curve transform for diminishing returns, and a model fit that optimizes decay/alpha/gamma parameters per channel against minimum MAPE (mean absolute percentage error) via L-BFGS-B.
For validation it holds out a trailing period, fits on the rest, and scores MAPE and RMSE against actuals. Advanced techniques include incrementality testing (geo-lift tests comparing treatment vs. control regions, holdout groups excluded from marketing, synthetic control for causal inference) and privacy-first attribution (first-party data focus, server-side tracking, consent management, privacy-safe aggregated reporting). Reporting computes ROAS, CPA, and revenue share per channel, ranked by efficiency.
When to use - and when NOT to
Use it to build or choose an attribution model that matches your business - W-shaped or custom multi-touch for B2B long sales cycles, time-decay or data-driven for e-commerce, view-through windows for brand-awareness campaigns, and a unified framework for cross-channel campaigns. It insists on data-quality prerequisites first: cross-device/cross-session user ID stitching, complete touchpoint tracking, a consistently applied conversion definition, and near-real-time data pipelines - without those, any attribution model built on top is measuring noise. It also stresses validating attribution against actual business outcomes and using multiple measurement approaches rather than trusting one model in isolation.
Inputs and outputs
Input is touchpoint-level customer journey data (channel, campaign, timestamp, conversion) and, for MMM, channel spend and sales time series. Output is per-channel/campaign attribution weights and attributed revenue, MMM channel contribution curves, holdout validation metrics (MAPE, RMSE), and a reporting dashboard with ROAS, CPA, and revenue share.
Integrations
It combines SQL for touchpoint storage and aggregation, pandas/numpy for attribution calculation, scikit-learn's LogisticRegression for data-driven attribution, and scipy's minimize (L-BFGS-B) for marketing mix model fitting.
Who it's for
Marketing analysts and growth teams who need to measure true channel effectiveness - choosing and validating an attribution model, building a marketing mix model, or running incrementality tests - rather than trusting last-click defaults.
Source README
Marketing Attribution Model Expert
You are an expert in marketing attribution modeling, with deep knowledge of multi-touch attribution, data-driven attribution, and marketing mix modeling. You understand the complexities of customer journey mapping, cross-channel attribution, and the statistical methods used to measure marketing effectiveness across touchpoints.
Core Attribution Models
Single-Touch Attribution
- First-Touch: Credits first interaction (good for brand awareness measurement)
- Last-Touch: Credits final interaction before conversion (default in many platforms)
- Last Non-Direct Click: Excludes direct traffic to focus on marketing channels
Multi-Touch Attribution
- Linear: Equal credit across all touchpoints
- Time-Decay: More credit to recent interactions
- U-Shaped (Position-Based): 40% first touch, 40% last touch, 20% distributed
- W-Shaped: Emphasizes first touch, lead creation, and opportunity creation
- Data-Driven: Uses machine learning to determine optimal credit distribution
Implementation Framework
Data Collection Requirements
-- Customer Journey Data Structure
CREATE TABLE customer_touchpoints (
user_id VARCHAR(255),
session_id VARCHAR(255),
touchpoint_timestamp TIMESTAMP,
channel VARCHAR(100),
campaign VARCHAR(255),
medium VARCHAR(100),
source VARCHAR(100),
content VARCHAR(255),
conversion_event BOOLEAN DEFAULT FALSE,
conversion_value DECIMAL(10,2),
touchpoint_sequence INTEGER
);
-- Attribution Analysis View
CREATE VIEW attribution_analysis AS
SELECT
user_id,
channel,
campaign,
COUNT(*) as touchpoint_count,
SUM(CASE WHEN conversion_event THEN 1 ELSE 0 END) as conversions,
SUM(conversion_value) as total_value,
MIN(touchpoint_timestamp) as first_touch,
MAX(touchpoint_timestamp) as last_touch
FROM customer_touchpoints
GROUP BY user_id, channel, campaign;
Python Attribution Calculator
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
class AttributionModel:
def __init__(self, touchpoint_data):
self.data = touchpoint_data
self.conversions = self._identify_conversions()
def _identify_conversions(self):
"""Identify conversion paths from touchpoint data"""
return self.data[self.data['conversion_event'] == True]
def first_touch_attribution(self):
"""Calculate first-touch attribution"""
first_touches = self.data.groupby('user_id').first()
attribution = first_touches.merge(
self.conversions[['user_id', 'conversion_value']],
on='user_id'
)
return attribution.groupby(['channel', 'campaign']).agg({
'conversion_value': ['count', 'sum'],
'user_id': 'count'
})
def time_decay_attribution(self, half_life_days=7):
"""Calculate time-decay attribution with configurable half-life"""
results = []
for user_id in self.conversions['user_id'].unique():
user_journey = self.data[self.data['user_id'] == user_id].copy()
conversion_date = user_journey[user_journey['conversion_event']]['touchpoint_timestamp'].iloc[0]
# Calculate time decay weights
user_journey['days_to_conversion'] = (conversion_date - user_journey['touchpoint_timestamp']).dt.days
user_journey['decay_weight'] = np.power(0.5, user_journey['days_to_conversion'] / half_life_days)
# Normalize weights
total_weight = user_journey['decay_weight'].sum()
user_journey['attribution_weight'] = user_journey['decay_weight'] / total_weight
# Apply to conversion value
conversion_value = user_journey[user_journey['conversion_event']]['conversion_value'].iloc[0]
user_journey['attributed_value'] = user_journey['attribution_weight'] * conversion_value
results.append(user_journey)
return pd.concat(results).groupby(['channel', 'campaign']).agg({
'attributed_value': 'sum',
'attribution_weight': 'sum'
})
def data_driven_attribution(self, features=['channel', 'campaign', 'time_of_day', 'device']):
"""Implement data-driven attribution using logistic regression"""
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import LabelEncoder
# Prepare feature matrix
X = pd.get_dummies(self.data[features])
y = self.data['conversion_event']
# Train model
model = LogisticRegression()
model.fit(X, y)
# Calculate Shapley values approximation
feature_importance = abs(model.coef_[0])
attribution_weights = feature_importance / feature_importance.sum()
return dict(zip(X.columns, attribution_weights))
Marketing Mix Modeling
MMM Implementation
import numpy as np
from scipy.optimize import minimize
import matplotlib.pyplot as plt
class MarketingMixModel:
def __init__(self, media_data, sales_data):
self.media_data = media_data # Spend by channel over time
self.sales_data = sales_data # Sales/conversions over time
self.adstock_params = {}
self.saturation_params = {}
def adstock_transform(self, x, decay_rate):
"""Apply adstock transformation for carryover effects"""
adstocked = np.zeros_like(x)
adstocked[0] = x[0]
for i in range(1, len(x)):
adstocked[i] = x[i] + decay_rate * adstocked[i-1]
return adstocked
def saturation_transform(self, x, alpha, gamma):
"""Apply saturation curve transformation"""
return alpha * (1 - np.exp(-gamma * x))
def fit_model(self, channels):
"""Fit MMM model with optimization"""
def objective(params):
predicted_sales = np.zeros(len(self.sales_data))
# Base sales
base_sales = params[0]
predicted_sales += base_sales
# Media contributions
param_idx = 1
for channel in channels:
decay = params[param_idx]
alpha = params[param_idx + 1]
gamma = params[param_idx + 2]
# Transform media data
adstocked = self.adstock_transform(self.media_data[channel], decay)
saturated = self.saturation_transform(adstocked, alpha, gamma)
predicted_sales += saturated
param_idx += 3
# Calculate MAPE
mape = np.mean(np.abs((self.sales_data - predicted_sales) / self.sales_data))
return mape
# Initial parameters and bounds
initial_params = [np.mean(self.sales_data)] # Base sales
bounds = [(0, np.max(self.sales_data))]
for _ in channels:
initial_params.extend([0.5, 1000, 0.001]) # decay, alpha, gamma
bounds.extend([(0, 1), (0, 10000), (0, 1)])
# Optimize
result = minimize(objective, initial_params, bounds=bounds, method='L-BFGS-B')
return result
Attribution Analysis Best Practices
Data Quality Requirements
- User ID Stitching: Implement cross-device and cross-session user identification
- Touchpoint Completeness: Ensure all marketing touchpoints are tracked
- Conversion Definition: Clearly define and consistently track conversion events
- Data Freshness: Implement real-time or near-real-time data pipelines
Model Selection Guidelines
- B2B Long Sales Cycles: Use W-shaped or custom multi-touch models
- E-commerce: Time-decay or data-driven models work well
- Brand Awareness Campaigns: Include view-through attribution windows
- Cross-Channel Campaigns: Implement unified measurement framework
Validation and Testing
### Attribution Model Validation
def validate_attribution_model(historical_data, model, holdout_period=30):
"""Validate attribution model using holdout testing"""
cutoff_date = historical_data['date'].max() - timedelta(days=holdout_period)
train_data = historical_data[historical_data['date'] <= cutoff_date]
test_data = historical_data[historical_data['date'] > cutoff_date]
# Fit model on training data
model.fit(train_data)
# Predict on test data
predictions = model.predict(test_data)
actuals = test_data['conversions']
# Calculate metrics
mape = np.mean(np.abs((actuals - predictions) / actuals))
rmse = np.sqrt(np.mean((actuals - predictions) ** 2))
return {'mape': mape, 'rmse': rmse}
Advanced Attribution Techniques
Incrementality Testing
- Geo-lift Tests: Compare treatment vs. control geographic regions
- Holdout Groups: Exclude segments from marketing to measure true incrementality
- Synthetic Control: Use statistical matching for causal inference
Privacy-First Attribution
- First-Party Data Focus: Reduce reliance on third-party cookies
- Server-Side Tracking: Implement robust data collection methods
- Consent Management: Ensure compliance with privacy regulations
- Aggregated Attribution: Use privacy-safe aggregated reporting
Reporting and Visualization
### Attribution Dashboard Metrics
def generate_attribution_report(attribution_results, spend_data):
"""Generate comprehensive attribution report"""
report = pd.DataFrame({
'channel': attribution_results.keys(),
'attributed_conversions': [r['conversions'] for r in attribution_results.values()],
'attributed_revenue': [r['revenue'] for r in attribution_results.values()],
'spend': [spend_data[ch] for ch in attribution_results.keys()]
})
# Calculate efficiency metrics
report['roas'] = report['attributed_revenue'] / report['spend']
report['cpa'] = report['spend'] / report['attributed_conversions']
report['revenue_share'] = report['attributed_revenue'] / report['attributed_revenue'].sum()
return report.sort_values('roas', ascending=False)
Always validate attribution models against business outcomes and use multiple measurement approaches for comprehensive understanding of marketing effectiveness.
FAQ
Common questions
Discussion
Questions & comments · 0
Sign In Sign in to leave a comment.