Approach: treat signup→activate→add_payment→purchase as an ordered funnel. Compute counts and step conversion rates (step-to-step) with 95% CIs, then compute absolute and relative drops between consecutive steps. Use normal approximation for CI (or Wilson) for proportions.Step 1 — counts & rates per stepsql
-- counts per step (one row per user with max step reached)
WITH user_step AS (
SELECT user_id,
MAX(CASE WHEN event='signup' THEN 1 ELSE 0 END) * 1 AS has_signup,
MAX(CASE WHEN event='activate' THEN 1 ELSE 0 END) * 1 AS has_activate,
MAX(CASE WHEN event='add_payment' THEN 1 ELSE 0 END) * 1 AS has_payment,
MAX(CASE WHEN event='purchase' THEN 1 ELSE 0 END) * 1 AS has_purchase
FROM events
WHERE event IN ('signup','activate','add_payment','purchase')
GROUP BY user_id
)
SELECT
SUM(has_signup) AS n_signup,
SUM(has_activate) AS n_activate,
SUM(has_payment) AS n_payment,
SUM(has_purchase) AS n_purchase
FROM user_step;
Step 2 — compute step conversion rates and 95% CI (normal approx)Rate = n_next / n_prev. CI: p ± 1.96*sqrt(p*(1-p)/n_prev)sql
WITH counts AS (
-- paste results from previous query or compute inline
)
SELECT
'signup->activate' AS step,
n_activate::float / n_signup AS rate,
(n_activate::float / n_signup) - 1.96*sqrt((n_activate::float/n_signup)*(1 - n_activate::float/n_signup)/GREATEST(n_signup,1)) AS lower,
(n_activate::float / n_signup) + 1.96*sqrt((n_activate::float/n_signup)*(1 - n_activate::float/n_signup)/GREATEST(n_signup,1)) AS upper
FROM counts
UNION ALL
-- repeat for activate->add_payment using n_activate as denominator, etc.
Step 3 — compute absolute and relative dropsAbsolute drop = rate_prev - rate_next. Relative drop = (rate_prev - rate_next) / rate_prev.sql
WITH rates AS (
-- compute r_signup = n_activate/n_signup, r_activate = n_payment/n_activate, r_payment = n_purchase/n_payment
)
SELECT
'signup->activate' AS transition,
r_signup AS rate_prev,
r_activate AS rate_next,
(r_signup - r_activate) AS absolute_drop,
CASE WHEN r_signup>0 THEN (r_signup - r_activate)/r_signup ELSE NULL END AS relative_drop
FROM rates;
Notes & next steps:- Use Wilson CI or prop.test for small n or extreme p.- Segment by cohort, channel, device to locate where drops concentrate.- Use bootstrapping in Python/R if events per user >1 or dependency exists.- Validate event hygiene (duplicates, time windows) and visualize funnel with error bars.