A funnel is an ordered sequence of steps — visit → view product → add to cart → checkout → purchase. Because overall conversion is the product of stage conversions, small stage improvements compound, and one bad stage caps everything downstream. The job of funnel analysis is finding the stage where fixing the leak buys the most.
| Metric | Definition | Watch for |
|---|---|---|
| Stage conversion | users(stage) / users(previous) | The bottleneck — lowest rate first |
| Overall conversion | users(last) / users(first) | Topline; hides where the leak is |
| Drop-off | 1 − stage conversion | Multiply by traffic to rank by impact |
| Time-to-convert | Median time between stages | Slow stages often precede abandonment |
pip install pandas numpy
# ────────────────────────────────────────
import numpy as np
import pandas as pd
rng = np.random.default_rng(11)
stages = ['visit', 'view_product', 'add_to_cart', 'checkout', 'purchase']
rates = [1.0, .55, .30, .70, .60]
events = []
for uid in range(10_000):
for stage, rate in zip(stages, rates):
if rng.random() > rate:
break
events.append((uid, stage))
ev = pd.DataFrame(events, columns=['user_id', 'event'])
funnel = ev.groupby('event')['user_id'].nunique().reindex(stages)
print((funnel / funnel.iloc[0]).round(3))
print("fix first:", (funnel / funnel.shift(1)).idxmin())
A funnel converting 40%, 50% and 20% at its three steps: 4.0% end to end. Ten more points on the weakest step (20 → 30%) lift it to 6.0%; ten more points on the 50% step only to 4.8%. Steps multiply, so the same absolute gain is worth most where the rate is lowest — it is the biggest relative change.
- Steps are not independent: pushing more people through one step often lowers the quality of those reaching the next.
- Funnels built from sessions double-count users who return; decide whether you are counting people or visits.
- A funnel shows where people leave, not why; pair it with experiments.
stages = np.array([0.40, 0.50, 0.20]) # visit -> signup -> trial -> paid overall = stages.prod() fix_small = np.array([0.40, 0.50, 0.30]).prod() # +10 points on the weakest step fix_large = np.array([0.40, 0.60, 0.20]).prod() # +10 points on a stronger one
Assumes import numpy as np and import pandas as pd (and SciPy where imported). Each snippet simulates or makes up its own data, as the comments say.
The same idea with a library
# Python — funnel from an event log import pandas as pd stages = ['visit', 'view_product', 'add_to_cart', 'checkout', 'purchase'] # events: user_id, event — count users reaching each stage reached = {s: events.loc[events.event == s, 'user_id'].nunique() for s in stages} funnel = pd.DataFrame({'users': reached.values()}, index=stages) funnel['stage_conv'] = funnel['users'] / funnel['users'].shift(1) funnel['overall'] = funnel['users'] / funnel['users'].iloc[0] print(funnel.round(3)) # Biggest leak = stage with lowest stage_conv print("Fix first:", funnel['stage_conv'].idxmin())
- Trustworthy Online Controlled Experiments, R. Kohavi, D. Tang & Y. Xu, Cambridge University Press, 2020
- Lean Analytics, A. Croll & B. Yoskovitz, O’Reilly, 2013
Last reviewed October 2026 · How this site checks its content · Report a mistake