Problems / Cohort Revenue (30-Day LTV) / Editorial
DATE_TRUNC('month', signup_time)
order_time >= signup_time
order_time < signup_time + INTERVAL 30 DAY
COUNT(DISTINCT customer_id)
SUM(amount)
COALESCE
ROUND(..., 2)
The PySpark version splits the same logic into two aggregates (cohort sizes; windowed revenue) joined at the end — often clearer than a fanned-out single pipeline, and it avoids the DISTINCT-under-fan-out subtlety entirely.
"Revenue in the first 30 days" is a per-entity relative window. The classic mistake is filtering with a calendar boundary (orders in the signup month, or before some fixed date), which gives January signups up to 30 more earning days than late-January ones. The second classic mistake is WHERE-ing the window predicate after a LEFT JOIN — the NULL order rows fail the predicate and the join quietly becomes inner, so zero-spend customers vanish from customers. Window in the ON clause; population intact.
customers
Solve Cohort Revenue (30-Day LTV) yourself →