Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 28 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
# Tipping Point
# dxpoint
**Author:** [Ryan Duecker](ryanduecker@google.com)

[![PyPI Downloads](https://img.shields.io/pypi/dm/tippingpt.svg?label=PyPI%20downloads)](https://pypi.org/project/tippingpt/)
[![PyPI Downloads](https://img.shields.io/pypi/dm/dxpoint.svg?label=PyPI%20downloads)](https://pypi.org/project/dxpoint/)

A lightweight, marketing intelligence module that assists in identifying media response curves and determining the inflection points.
A lightweight, differential marketing intelligence module that models media response curves, calculates marginal derivatives, and determines strategic inflection points.

Growth marketers and media buyers ask two fundamental questions:
1) *"When are we out of the inefficient learning phase?"*
2) *"When should we stop scaling spend?"*

By fitting performance data to continuous saturation curves, Tipping Point identifies the **Minimal Marginal Cost Point** (the inflection point where acquisition cost is lowest) and the **Point of Diminishing Returns** (where marginal ROAS hits your profitability hurdle rate), defining your exact **Optimal Scaling Zone**.
By fitting performance data to continuous saturation curves and evaluating their derivatives ($f'(x)$ and $f''(x)$), **dxpoint** identifies the **Minimal Marginal Cost Point** (the inflection point where acquisition cost is lowest) and the **Point of Diminishing Returns** (where marginal ROAS hits your profitability hurdle rate), defining your exact **Optimal Scaling Zone**.

Tipping Point focuses primarily on **single-channel curve fitting** and **cross-channel portfolio planning**, keeping single-channel workflows fast, lightweight, and accessible without requiring heavy econometric setup.
**dxpoint** focuses primarily on **single-channel curve fitting** and **cross-channel portfolio planning**, keeping single-channel workflows fast, lightweight, and accessible without requiring heavy econometric setup.

---

## Core Methodology

Tipping Point leverages the mathematical foundations of modern response modeling—specifically the Hill saturation and adstock formulations popularized by [Google’s Meridian](https://github.com/google/meridian).
**dxpoint** leverages the mathematical foundations of modern response modeling—specifically the Hill saturation and adstock formulations popularized by [Google’s Meridian](https://github.com/google/meridian).

### 1. Media Saturation (The Hill Function)
Instead of basic linear or logarithmic approximations, this module natively models media saturation using the **Hill Function**.
Expand All @@ -30,7 +30,7 @@ $$Return = \beta_0 + \frac{\beta \cdot Spend_{adstocked}^\alpha}{K^\alpha + Spen
* **$\beta_0$ (Baseline Demand):** Optional organic, non-media baseline return.

### 2. Adstock (Lagged Effects & Memory)
Advertising impacts persist beyond the day of exposure. Tipping Point supports multiple memory decay models:
Advertising impacts persist beyond the day of exposure. **dxpoint** supports multiple memory decay models:

* **Geometric Adstock:** Exponential memory decay parameterized by retention rate $\theta \in [0, 1)$:
$$S_{t\_adstocked} = S_t + \theta \cdot S_{t-1\_adstocked}$$
Expand All @@ -39,7 +39,7 @@ Advertising impacts persist beyond the day of exposure. Tipping Point supports m
During single-channel training, adstock can be set to `none`, `fixed` (explicit half-life), `bounded` (constrained half-life window), or `free` (unconstrained optimization).

### 3. Margin-Focused Calculus & Tipping Points
Using marginal rates of change rather than historical blended averages, the module calculates:
Using marginal rates of change ($dy/dx$) rather than historical blended averages, the module calculates:
* **Marginal ROAS ($f'(x)$):** The efficiency of the *next* dollar spent.
* **Peak Efficiency Point ($f''(x) = 0$):** The inflection point. Spend at least this much to exit the warm-up phase.
* **Stop Scaling Point ($f'(x) = \text{Target mROAS}$):** The exact spend level where marginal return drops below your baseline unit economics.
Expand All @@ -50,7 +50,7 @@ Using marginal rates of change rather than historical blended averages, the modu
## Installation

```bash
pip install tippingpt
pip install dxpoint
```

This module uses **tinygrad** for ultra-lightweight GPU-accelerated gradient descent, **scipy** for portfolio optimization, and **plotly/streamlit** for interactive visualization. Bayesian MCMC estimation is built-in with adaptive burn-in tuning.
Expand All @@ -64,7 +64,7 @@ Pass raw `Spend` and `Return` arrays directly into the module. You can fit using

```python
import numpy as np
from tippingpoint import MarketingReturnCurve
from dxpoint import MarketingReturnCurve

spends = np.array([1200, 5000, 15000, 25000, 40000])
returns = np.array([200, 1500, 12000, 22000, 28000])
Expand Down Expand Up @@ -151,19 +151,19 @@ print(f"Expected Incremental Return: {pred:,.1f} (95% CI: [{low:,.1f}, {high:,.1
```

### 5. Incrementality Experiment Calibration & Parallel Association
Associate causal lift test results (e.g. geo-experiments, conversion lift studies) in parallel across individual channels, joint MMMs, or portfolio allocators:
Associate causal lift test results (e.g. geo-experiments, conversion lift studies) in parallel across individual channels, joint multi-channel models, or portfolio allocators:

```python
# 5a. Associate experiments directly on a single-channel model
model_youtube.add_experiment(spend=15000, lift=11500, se=800, name="YT_Lift_Q1")
model_youtube.validate_experiments(verbose=True)

# 5b. Associate experiments in parallel across multiple channels in an MMM
mmm.attach_experiments({
# 5b. Associate experiments in parallel across multiple channels in a joint model
mc_model.attach_experiments({
"YouTube": [{"name": "YT_Q1", "spend": 15000, "lift": 11500, "se": 800}],
"Paid Search": {"name": "Search_Q1", "spend": 25000, "lift": 38000, "se": 1200}
})
mmm_validation = mmm.validate_experiments(verbose=True)
mc_validation = mc_model.validate_experiments(verbose=True)

# 5c. Audit calibration across portfolio allocator channels before optimization
allocator = PortfolioAllocator([model_search, model_youtube, model_social])
Expand All @@ -174,7 +174,7 @@ calibration_audit = allocator.get_calibration_summary()
Once you have fitted single-channel curves, the `PortfolioAllocator` calculates the budget distribution that maximizes total portfolio return:

```python
from tippingpoint import PortfolioAllocator
from dxpoint import PortfolioAllocator

# Initialize the Allocator with fitted channel models
allocator = PortfolioAllocator([model_search, model_youtube, model_social])
Expand All @@ -192,26 +192,28 @@ print(f"Expected Portfolio Return: ${scenario['expected_total_return']:,.2f}")
### 7. Interactive Dashboard & Example Notebooks
* **Web App Dashboard:** Launch the built-in Streamlit app to explore single-channel curves, adstock carryover timelines, and cross-channel allocation simulations:
```bash
tipp dashboard
dxpoint dashboard
# or using the short alias:
dxpt dashboard
```
* **Practitioner's Single-Channel Starter Template:** See [`examples/practitioner_single_channel_template.ipynb`](examples/practitioner_single_channel_template.ipynb) for a production-ready boilerplate template for media science practitioners, featuring automated diagnostic fit callouts, causal experiment validation, and customized Matplotlib visualizations.
* **Single-Channel YouTube Saturation Example:** See [`examples/single_channel_youtube_branded_search.ipynb`](examples/single_channel_youtube_branded_search.ipynb) for a concise, step-by-step engineering tutorial on fitting daily YouTube video spend to Attributed Branded Search volume, calculating the geometric carryover half-life, locating Peak Efficiency ($f''(x) = 0$), and identifying the Stop Scaling Point against a $16.00 target CPA.
* **Causal Experiment Calibration Example:** See [`examples/single_channel_incrementality_calibration.ipynb`](examples/single_channel_incrementality_calibration.ipynb) for an engineering case study integrating holdout conversion lift studies via Bayesian MCMC to decouple organic baseline demand from paid media lift.
* **Multi-Channel Stacked Walkthrough:** See [`examples/tippingpoint_walkthrough.ipynb`](examples/tippingpoint_walkthrough.ipynb) for an end-to-end tutorial on multi-channel budget allocation and visualizing how brand consideration campaigns shift response curves upward.
* **Multi-Channel Stacked Walkthrough:** See [`examples/dxpoint_walkthrough.ipynb`](examples/dxpoint_walkthrough.ipynb) for an end-to-end tutorial on multi-channel budget allocation and visualizing how brand consideration campaigns shift response curves upward.

---

## Exploring Multi-Channel Dynamics: The Lightweight MMM Framework
## Exploring Multi-Channel Dynamics: Joint Response Curves (`MultiChannelModel`)

While Tipping Point is built around lightweight single-channel curve fitting and portfolio allocation, it also provides a multi-channel modeling class (`MultiChannelMMM`) that allows practitioners to explore how individual channels interact under a unified framework.
While **dxpoint** is built around lightweight single-channel curve fitting and portfolio allocation, it also provides a multi-channel modeling class (`MultiChannelModel`) that allows practitioners to explore how individual channels interact under a unified framework.

> [!IMPORTANT]
> **Not a Substitute for Full MMM:**
> `MultiChannelMMM` is a lightweight, exploratory tool designed to help users examine joint adstock carryover, saturation, and preliminary historical attribution across channels. **It does not provide a full, production-grade Marketing Mix Model.**
> `MultiChannelModel` is a lightweight tool designed to help users examine joint adstock carryover and saturation across channels. **It does not provide a full, production-grade Marketing Mix Model.**
>
> A full MMM—such as **[Google's Meridian](https://github.com/google/meridian)**—incorporates rich macroeconomic controls, pricing/promotions, non-media baseline variables, reach and frequency transformations, and comprehensive prior elicitation. For enterprise budget decisions, causal attribution, and complete cross-media measurement, **Google Meridian should always be used to produce robust results.**

### What `MultiChannelMMM` Provides
### What `MultiChannelModel` Provides
When you need to analyze multiple spend series simultaneously:
* **Joint Parameter Estimation:** Simultaneously estimates adstock decay ($\theta_m$), Hill saturation ($\alpha_m, K_m$), baseline ($\beta_0$), and channel scale ($\beta_m$) via MCMC.
* **Hierarchical Partial Pooling:** Stabilizes estimates for smaller or noisy channels by pooling across channel distributions.
Expand All @@ -220,12 +222,12 @@ When you need to analyze multiple spend series simultaneously:

```python
import pandas as pd
from tippingpoint import MultiChannelMMM
from dxpoint import MultiChannelModel

df = pd.read_csv("weekly_marketing_data.csv")

mmm = MultiChannelMMM(channel_names=["Search", "YouTube", "Social"])
mmm.fit(
mc_model = MultiChannelModel(channel_names=["Search", "YouTube", "Social"])
mc_model.fit(
spend_data=df[["Search", "YouTube", "Social"]],
return_array=df["Revenue"],
fit_baseline=True,
Expand All @@ -234,7 +236,7 @@ mmm.fit(
)

# Decompose historical contributions and channel ROIs
decomp = mmm.decompose_historical_contributions(
decomp = mc_model.decompose_historical_contributions(
spend_data=df[["Search", "YouTube", "Social"]],
return_array=df["Revenue"]
)
Expand All @@ -243,7 +245,7 @@ print(decomp["summary_table"])

---

## Integrating with Existing MMMs (Google Meridian)
## Integrating with External MMMs (Google Meridian)

If you already run Google Meridian or PyMC-Marketing, you can extract your posterior mean parameters and initialize `MarketingReturnCurve` directly without refitting:

Expand Down
2 changes: 1 addition & 1 deletion docs/_build/html/.buildinfo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Sphinx build info version 1
# This file records the configuration used when building these files. When it is not found, a full rebuild will be done.
config: e3f99bd6e7885fe2e4c58ae42481fecc
config: 3a2f5a0392ed9e4e3c7c594b1a06505c
tags: 645f666f9bcd5a90fca523b33c5a78b7
Binary file modified docs/_build/html/.doctrees/api.doctree
Binary file not shown.
Binary file modified docs/_build/html/.doctrees/environment.pickle
Binary file not shown.
Binary file modified docs/_build/html/.doctrees/index.doctree
Binary file not shown.
Loading
Loading