lección 4
Día 3 -- Métricas de producto: DAU, retention, engagement
Calculas las métricas que Marta necesita para inversores: DAU/MAU con tendencia, retención por cohorte D1/D7/D30 y engagement score por usuario.
⏱ 45 min
### Miércoles, 8:00 AM -- Los datos están listos
Hoy calculas métricas sobre los datos ya cargados. Si lo reproduces en local, parte de `datos/reproducciones_eventos.parquet` para DAU/MAU, retención y engagement (usa `user_id`, `content_id`, `timestamp` y `properties`) cruzado con `datos/usuarios.csv` (`signup_date` o `created_at` para definir las cohortes de registro). Para la conversión free a premium y el churn usa `datos/suscripciones.csv` (`plan_type, status, started_at, ended_at, monthly_amount_eur, cancel_reason`) y, si necesitas ingresos reales, `datos/pagos.csv` (`payment_id, subscription_id, user_id, amount_eur, currency, status, payment_date, payment_method, failure_reason`). Las queries SQL de esta lección usan el esquema PostgreSQL ilustrativo (`playbacks`, `watch_seconds`, `completed`, `registered_at`); trabaja con las columnas reales de los ficheros cuando ejecutes en tu máquina.
El pipeline corrió anoche a las 03:00. Los 3 millones de reproducciones históricas están en S3 como Parquet particionado por fecha. DuckDB tiene vistas configuradas. Es hora de transformar bytes en narrativa. Porque eso es lo que Marta necesita para el viernes: no un dashboard con números, sino una HISTORIA que un inversor pueda seguir y que le haga sacar la chequera.
La historia que los inversores de streaming quieren escuchar tiene tres actos: "La gente viene" (crecimiento DAU/MAU), "La gente se queda" (retención por cohorte) y "La gente se engancha" (engagement creciente). Si las tres curvas van hacía arriba, la conclusión es inevitable: este producto tiene product-market fit y merece inversión para escalar.
### Acto 1: "La gente viene" -- DAU, WAU, MAU
Las métricas de actividad son el primer indicador de salud. Un inversor mira DAU/MAU como un médico mira el pulso: si hay pulso constante y creciente, el paciente está vivo y mejorando. El ratio DAU/MAU (stickiness) indica si el producto es un hábito diario o una visita esporádica.
1-- Cálculo de DAU, WAU, MAU con tendencia2-- Ejecutar en DuckDB contra las vistas del lake34-- DAU: usuarios únicos que reprodujeron algo cada día5CREATE OR REPLACE VIEW metric_dau AS6SELECT7 event_date,8 COUNT(DISTINCT user_id) AS dau,9 -- Media móvil 7 días para suavizar variación diaria10 AVG(COUNT(DISTINCT user_id)) OVER (11 ORDER BY event_date12 ROWS BETWEEN 6 PRECEDING AND CURRENT ROW13 ) AS dau_7d_avg14FROM v_playbacks15WHERE watch_seconds > 30 -- Solo sesiones significativas (> 30s)16GROUP BY event_date17ORDER BY event_date;1819-- MAU: usuarios únicos en ventana de 30 días rolling20CREATE OR REPLACE VIEW metric_mau AS21SELECT22 event_date,23 COUNT(DISTINCT user_id) OVER (24 ORDER BY event_date25 RANGE BETWEEN INTERVAL '29 days' PRECEDING AND CURRENT ROW26 ) AS mau_rolling27FROM (28 SELECT DISTINCT event_date, user_id29 FROM v_playbacks30 WHERE watch_seconds > 3031)32GROUP BY event_date33ORDER BY event_date;3435-- Stickiness: DAU / MAU36SELECT37 d.event_date,38 d.dau,39 m.mau_rolling AS mau,40 ROUND(d.dau * 100.0 / NULLIF(m.mau_rolling, 0), 1) AS stickiness_pct41FROM metric_dau d42JOIN metric_mau m ON d.event_date = m.event_date43WHERE d.event_date >= '2024-01-01'44ORDER BY d.event_date;
DAU con media móvil 7 días + stickiness. Solo sesiones > 30s para no inflar con bounces.
La decisión de filtrar por watch_seconds > 30 es deliberada. Un usuario que abre un vídeo y lo cierra en 5 segundos no es "activo" -- es alguien que hizo clic por error o que no le gustó la primera escena. En TaskFlow, Pablo (el PM) te enseñó está lección: "Un usuario activo es alguien que USÓ el producto, no alguien que lo abrió y se fue." Aquí, "usar el producto" significa ver contenido durante al menos 30 segundos.
### Acto 2: "La gente se queda" -- Retención por cohorte
La retención es LA métrica que separa startups con product-market fit de las que no lo tienen. Un inversor sofisticado como Meridian Capital sabe que el crecimiento de MAU puede ser artificial (publicidad pagada). Pero la retención no miente: si los usuarios vuelven después de 7 días sin que les pagues por hacerlo, el producto tiene algo que funciona.
1-- Retención por cohorte semanal: D1, D7, D14, D302-- Cohorte = semana de primer playback del usuario34WITH user_first_play AS (5 -- Fecha del primer playback significativo de cada usuario6 SELECT7 user_id,8 MIN(event_date) AS cohort_date,9 DATE_TRUNC('week', MIN(event_date))::DATE AS cohort_week10 FROM v_playbacks11 WHERE watch_seconds > 3012 GROUP BY user_id13),14user_activity AS (15 -- Todas las fechas en que cada usuario fue activo16 SELECT DISTINCT user_id, event_date17 FROM v_playbacks18 WHERE watch_seconds > 3019),20retention_calc AS (21 SELECT22 ufp.cohort_week,23 COUNT(DISTINCT ufp.user_id) AS cohort_size,24 COUNT(DISTINCT CASE25 WHEN ua.event_date = ufp.cohort_date + INTERVAL '1 day'26 THEN ufp.user_id END) AS retained_d1,27 COUNT(DISTINCT CASE28 WHEN ua.event_date BETWEEN ufp.cohort_date + INTERVAL '6 days'29 AND ufp.cohort_date + INTERVAL '8 days'30 THEN ufp.user_id END) AS retained_d7,31 COUNT(DISTINCT CASE32 WHEN ua.event_date BETWEEN ufp.cohort_date + INTERVAL '13 days'33 AND ufp.cohort_date + INTERVAL '15 days'34 THEN ufp.user_id END) AS retained_d14,35 COUNT(DISTINCT CASE36 WHEN ua.event_date BETWEEN ufp.cohort_date + INTERVAL '28 days'37 AND ufp.cohort_date + INTERVAL '32 days'38 THEN ufp.user_id END) AS retained_d3039 FROM user_first_play ufp40 LEFT JOIN user_activity ua ON ufp.user_id = ua.user_id41 GROUP BY ufp.cohort_week42)43SELECT44 cohort_week,45 cohort_size,46 ROUND(retained_d1 * 100.0 / cohort_size, 1) AS retention_d1_pct,47 ROUND(retained_d7 * 100.0 / cohort_size, 1) AS retention_d7_pct,48 ROUND(retained_d14 * 100.0 / cohort_size, 1) AS retention_d14_pct,49 ROUND(retained_d30 * 100.0 / cohort_size, 1) AS retention_d30_pct50FROM retention_calc51WHERE cohort_week >= '2023-10-01' -- Últimos 5 meses (cohortes con D30 completo)52ORDER BY cohort_week;
Retención por cohorte semanal con ventanas flexibles (+-1 día). El dato que cierra rondas.
La ventana de +-1 día en la retención no es un truco -- es realismo. Un usuario "D7" no siempre vuelve exactamente el día 7. Si vuelve el día 6 u 8, sigue siendo retención a la semana. Netflix usa ventanas de +-2 días para sus cohortes internas. Para la presentación a inversores, esto da números más estables sin ser deshonesto.
### Acto 3: "La gente se engancha" -- Engagement Score
El engagement es más difícil de capturar en un solo número. No es solo "cuánto tiempo ven" -- es una combinación de frecuencia (cuántos días a la semana), profundidad (cuánto contenido completan) e intensidad (cuánto tiempo por sesión). Diseñas un engagement score compuesto que pondera estos tres factores:
1-- Engagement Score por usuario (últimos 30 días)2-- Composición: frecuencia (40%) + profundidad (30%) + intensidad (30%)34WITH user_metrics_30d AS (5 SELECT6 user_id,7 COUNT(DISTINCT event_date) AS active_days,8 COUNT(*) AS total_plays,9 SUM(watch_seconds) / 3600.0 AS total_hours,10 AVG(watch_seconds) / 60.0 AS avg_session_minutes,11 AVG(CASE WHEN completion_ratio > 0 THEN completion_ratio END) AS avg_completion,12 COUNT(CASE WHEN completed THEN 1 END) AS titles_completed13 FROM v_playbacks14 WHERE event_date >= CURRENT_DATE - INTERVAL '30 days'15 AND watch_seconds > 3016 GROUP BY user_id17),18scored AS (19 SELECT20 user_id,21 active_days,22 total_plays,23 total_hours,24 avg_session_minutes,25 avg_completion,26 titles_completed,27 -- Frecuencia: días activos / 30 (max 1.0)28 LEAST(active_days / 30.0, 1.0) AS freq_score,29 -- Profundidad: completion rate promedio (ya está entre 0 y 1)30 COALESCE(avg_completion, 0) AS depth_score,31 -- Intensidad: minutos por sesión normalizado (30 min = 1.0)32 LEAST(avg_session_minutes / 30.0, 1.0) AS intensity_score33 FROM user_metrics_30d34)35SELECT36 user_id,37 active_days,38 total_hours,39 avg_completion,40 -- Engagement Score: ponderado41 ROUND(42 (freq_score * 0.4 + depth_score * 0.3 + intensity_score * 0.3) * 100,43 144 ) AS engagement_score,45 -- Segmento basado en score46 CASE47 WHEN (freq_score * 0.4 + depth_score * 0.3 + intensity_score * 0.3) >= 0.748 THEN 'power_user'49 WHEN (freq_score * 0.4 + depth_score * 0.3 + intensity_score * 0.3) >= 0.450 THEN 'regular'51 WHEN (freq_score * 0.4 + depth_score * 0.3 + intensity_score * 0.3) >= 0.1552 THEN 'casual'53 ELSE 'at_risk'54 END AS user_segment55FROM scored56ORDER BY engagement_score DESC;
Engagement score compuesto: frecuencia (40%) + profundidad (30%) + intensidad (30%).
El engagement score te permite segmentar usuarios en 4 grupos: power users (>70, los evangelistas), regulars (40-70, el core), casuals (15-40, oportunidad de activación) y at-risk (<15, candidatos a churn). Para Marta, la distribución de estos segmentos es una historia poderosa: "El 20% de nuestros usuarios son power users que ven más de 10 horas al mes y completan el 75% del contenido que empiezan."
La segmentación también es accionable: los power users son tus evangelistas (lánzales un programa de referidos). Los regulares son tu core (no los molestes, solo mejora el contenido). Los casuals son tu oportunidad (emails de contenido personalizado para reactivarlos). Los at-risk son tu alerta temprana (si crecen, tienes un problema de producto).
1-- Resumen ejecutivo de segmentos para el dashboard2SELECT3 segment,4 COUNT(*) AS users,5 ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER (), 1) AS pct,6 ROUND(AVG(engagement_score), 1) AS avg_score,7 ROUND(AVG(total_hours), 1) AS avg_hours_monthly,8 ROUND(AVG(active_days), 1) AS avg_days_active9FROM (10 -- (subquery del engagement score anterior)11 SELECT user_id, engagement_score, total_hours, active_days,12 CASE13 WHEN engagement_score >= 70 THEN 'Power User'14 WHEN engagement_score >= 40 THEN 'Regular'15 WHEN engagement_score >= 15 THEN 'Casual'16 ELSE 'At Risk'17 END AS segment18 FROM user_engagement_scores19)20GROUP BY segment21ORDER BY avg_score DESC;2223-- Resultado esperado:24-- Power User | 2.940 | 19.6% | 81.2 | 12.4h | 21.3 días25-- Regular | 5.250 | 35.0% | 53.7 | 5.8h | 10.1 días26-- Casual | 4.560 | 30.4% | 27.3 | 2.1h | 3.8 días27-- At Risk | 2.250 | 15.0% | 8.4 | 0.4h | 1.2 días
Distribución de segmentos: 55% engaged (power+regular) es una señal fuerte para inversores.
### Crecimiento MoM -- la tendencia que importa
1-- Crecimiento Month-over-Month de MAU2WITH monthly_mau AS (3 SELECT4 DATE_TRUNC('month', event_date)::DATE AS month,5 COUNT(DISTINCT user_id) AS mau6 FROM v_playbacks7 WHERE watch_seconds > 308 GROUP BY DATE_TRUNC('month', event_date)9)10SELECT11 month,12 mau,13 LAG(mau) OVER (ORDER BY month) AS prev_month_mau,14 ROUND(15 (mau - LAG(mau) OVER (ORDER BY month)) * 100.016 / NULLIF(LAG(mau) OVER (ORDER BY month), 0),17 118 ) AS growth_mom_pct19FROM monthly_mau20WHERE month >= '2023-06-01'21ORDER BY month;2223-- Resultado esperado (últimos meses):24-- 2024-01: 12.400 MAU (+6.8% MoM)25-- 2024-02: 13.600 MAU (+9.7% MoM)26-- 2024-03: 15.100 MAU (+11.0% MoM) <- tendencia acelerando
Crecimiento MoM acelerando: la señal que todo VC quiere ver.
Un crecimiento MoM del 8-11% sin gasto significativo en marketing es una señal fuerte de producto orgánico. Marta te confirma que el 80% del crecimiento viene de referidos y búsqueda orgánica. Eso es exactamente lo que Meridian Capital quiere escuchar: "el producto crece solo porque es bueno".
### Resumen del Día 3 (mañana)
- Calculaste DAU/MAU con media móvil 7 días y stickiness ratio (~14%)
- Implementaste retención por cohorte semanal: D1, D7, D14, D30 con ventanas flexibles
- Diseñaste engagement score compuesto (frecuencia + profundidad + intensidad)
- Segmentaste usuarios: power (20%), regular (35%), casual (30%), at-risk (15%)
- Verificaste crecimiento MoM acelerando: 6.8% -> 9.7% -> 11.0%
- Tienes los tres actos de la narrativa listos: "vienen, se quedan, se enganchan"
## ejercicios
Calcular DAU/MAU con stickiness y tendencia
Implementa las queries que calculan DAU diario, MAU rolling de 30 días y el ratio de stickiness. Incluye media móvil de 7 días para suavizar la variación y calcula el crecimiento WoW.
Retención por cohorte con ventana flexible
Implementa el cálculo de retención D1/D7/D30 por cohorte mensual de registro. Usa ventanas flexibles de +-1 día para D7 y +-2 días para D30. El resultado debe ser la tabla de cohortes que Marta mostrará a Meridian Capital.
Métricas de monetización: conversión y churn
Los inversores quieren ver que el modelo freemium funciona. Calcula la tasa de conversión free->premium mensual y el churn rate mensual de suscriptores premium usando las tablas de subscriptions y users.
Engagement score y segmentación de usuarios
Implementa en Python el cálculo del engagement score compuesto y la segmentación de usuarios en power/regular/casual/at_risk. Genera también las estadísticas agregadas por segmento para el dashboard.
Regístrate para guardar tu progreso.
## comentarios
Reporta erratas, ayuda a otros o comparte tu opinión. Sé constructivo.
Inicia sesión para comentar y responder.
cargando comentarios...