lección 8
Proyecto: análisis de cohortes y retención de clientes
Un caso real completo: calcula retención mes a mes con window functions, CTEs encadenadas y visualización de la matriz de cohortes.
⏱ 60 min
Bienvenido al proyecto final de esta skill. Todo lo que has aprendido — window functions, rankings, LAG, frames, CTEs — converge aquí en un análisis que impresionaría en cualquier entrevista técnica y que es directamente aplicable en el mundo real: el análisis de cohortes para medir retención de clientes.
El contexto: el VP de Producto de tu empresa te pide una cosa aparentemente simple: "¿Estamos reteniendo clientes o los perdemos después de la primera compra?" La respuesta no es un número — es una MATRIZ. Y construirla requiere el arsenal completo de SQL avanzado.
### ¿Qué es un análisis de cohortes?
Una cohorte es un grupo de clientes que comparten un evento en común en el tiempo. La cohorte de "enero 2024" son todos los clientes que hicieron su primera compra en enero de 2024. La pregunta de retención es: de esos clientes, ícuántos volvieron a comprar en febrero? ¿Y en marzo? ¿Y en abril?
La analogía: imagina una universidad que admite 200 alumnos cada año. La cohorte de 2020 son los 200 que entraron ese año. La retención es: ícuántos llegaron a segundo? ¿A tercero? ¿Cuántos se graduaron? Si la cohorte de 2020 tiene 80% de retención al segundo año pero la de 2021 solo tiene 60%, algo cambió — quizás el plan de estudios empeoró, o la competencia mejoró.
### El resultado que vamos a construir
Vamos a construir una matriz de retención como esta:
1-- Resultado final: Matriz de retencion2-- cohorte | mes_0 | mes_1 | mes_2 | mes_3 | mes_4 | mes_53-- 2023-01 | 100% | 42% | 31% | 28% | 25% | 22%4-- 2023-02 | 100% | 45% | 33% | 29% | 26% |5-- 2023-03 | 100% | 38% | 29% | 25% | |6-- 2023-04 | 100% | 41% | 30% | | |7-- ...89-- Lectura: "De los clientes que compraron por primera vez en enero 2023,10-- el 42% volvio a comprar en el mes 1, el 31% en el mes 2, etc."
La matriz de cohortes: cada fila es un grupo de clientes, cada columna un mes de vida.
### Paso 1: Identificar la cohorte de cada cliente
El primer paso es determinar cuándo cada cliente hizo su PRIMERA compra. Eso define su cohorte. Usamos MIN() — puede ser como agregación o como window function, depende de lo que necesites:
1-- Paso 1: Fecha de primera compra de cada cliente (su cohorte)2CREATE OR REPLACE TABLE cohortes_base AS3SELECT4 cliente_id,5 DATE_TRUNC('month', MIN(fecha)) AS cohorte,6 MIN(fecha) AS primera_compra7FROM pedidos8WHERE estado = 'completado'9GROUP BY cliente_id;1011-- Verificar12SELECT cohorte, COUNT(*) AS clientes13FROM cohortes_base14GROUP BY cohorte15ORDER BY cohorte;
Cada cliente pertenece a una sola cohorte: el mes de su primera compra.
### Paso 2: Calcular la actividad mensual de cada cliente
Ahora necesitamos saber en qué meses compró cada cliente. Unimos los pedidos con la información de cohorte y calculamos cuántos meses han pasado desde la primera compra:
1-- Paso 2: Actividad mensual + distancia desde la cohorte2CREATE OR REPLACE TABLE actividad_mensual AS3SELECT DISTINCT4 cb.cliente_id,5 cb.cohorte,6 DATE_TRUNC('month', p.fecha) AS mes_actividad,7 DATEDIFF('month', cb.cohorte, DATE_TRUNC('month', p.fecha)) AS meses_desde_inicio8FROM cohortes_base cb9JOIN pedidos p ON p.cliente_id = cb.cliente_id10WHERE p.estado = 'completado'11 AND DATEDIFF('month', cb.cohorte, DATE_TRUNC('month', p.fecha)) >= 0;1213-- meses_desde_inicio = 0 significa "el mes de su primera compra"14-- meses_desde_inicio = 1 significa "el mes siguiente", etc.
DATEDIFF calcula cuántos meses han pasado entre la cohorte y cada compra.
### Paso 3: Construir la matriz de retención
El paso final: para cada cohorte y cada "mes de vida" (0, 1, 2, 3...), contamos cuántos clientes fueron activos y lo dividimos entre el tamaño inicial de la cohorte:
1-- Paso 3: Matriz de retencion completa2WITH tamano_cohorte AS (3 SELECT cohorte, COUNT(DISTINCT cliente_id) AS clientes_iniciales4 FROM cohortes_base5 GROUP BY cohorte6),7retencion AS (8 SELECT9 am.cohorte,10 am.meses_desde_inicio,11 COUNT(DISTINCT am.cliente_id) AS clientes_activos12 FROM actividad_mensual am13 GROUP BY am.cohorte, am.meses_desde_inicio14)15SELECT16 r.cohorte,17 tc.clientes_iniciales,18 r.meses_desde_inicio,19 r.clientes_activos,20 ROUND(100.0 * r.clientes_activos / tc.clientes_iniciales, 1) AS pct_retencion21FROM retencion r22JOIN tamano_cohorte tc ON tc.cohorte = r.cohorte23WHERE r.meses_desde_inicio <= 6 -- Primeros 6 meses24ORDER BY r.cohorte, r.meses_desde_inicio;
La query final: cohorte — meses — porcentaje de retención.
### Paso 4: Enriquecer con window functions
Ahora añadimos inteligencia extra con window functions: ¿la retención de este mes es mejor o peor que la del mes anterior? ¿Cuál es la tendencia?
1-- Enriquecer: tendencia de retencion con LAG2WITH tamano_cohorte AS (3 SELECT cohorte, COUNT(DISTINCT cliente_id) AS n4 FROM cohortes_base GROUP BY cohorte5),6retencion AS (7 SELECT8 am.cohorte, am.meses_desde_inicio,9 COUNT(DISTINCT am.cliente_id) AS activos,10 ROUND(100.0 * COUNT(DISTINCT am.cliente_id) / MAX(tc.n), 1) AS pct11 FROM actividad_mensual am12 JOIN tamano_cohorte tc ON tc.cohorte = am.cohorte13 GROUP BY am.cohorte, am.meses_desde_inicio14)15SELECT16 cohorte,17 meses_desde_inicio,18 pct AS retencion_pct,19 LAG(pct) OVER (PARTITION BY cohorte ORDER BY meses_desde_inicio) AS pct_mes_anterior,20 pct - LAG(pct) OVER (PARTITION BY cohorte ORDER BY meses_desde_inicio) AS caida_vs_anterior,21 -- Media de retencion de TODAS las cohortes en este mismo mes de vida22 ROUND(AVG(pct) OVER (PARTITION BY meses_desde_inicio), 1) AS media_todas_cohortes23FROM retencion24WHERE meses_desde_inicio <= 625ORDER BY cohorte, meses_desde_inicio;
LAG para ver caída mes a mes. AVG OVER para comparar cada cohorte con la media histórica.
### Paso 5: Métricas ejecutivas derivadas
Con la matriz construida, puedes extraer métricas que los ejecutivos entienden sin necesidad de ver tablas:
1-- Metrica 1: Retencion promedio al mes 1 (la mas critica)2SELECT3 ROUND(AVG(pct_mes_1), 1) AS retencion_promedio_mes_14FROM (5 SELECT cohorte,6 ROUND(100.0 * COUNT(DISTINCT am.cliente_id) / MAX(tc.n), 1) AS pct_mes_17 FROM actividad_mensual am8 JOIN (SELECT cohorte, COUNT(DISTINCT cliente_id) AS n FROM cohortes_base GROUP BY cohorte) tc9 ON tc.cohorte = am.cohorte10 WHERE am.meses_desde_inicio = 111 GROUP BY am.cohorte12);1314-- Metrica 2: Mejor y peor cohorte15WITH retention_m1 AS (16 SELECT17 am.cohorte,18 ROUND(100.0 * COUNT(DISTINCT am.cliente_id) / MAX(tc.n), 1) AS pct19 FROM actividad_mensual am20 JOIN (SELECT cohorte, COUNT(DISTINCT cliente_id) AS n FROM cohortes_base GROUP BY cohorte) tc21 ON tc.cohorte = am.cohorte22 WHERE am.meses_desde_inicio = 123 GROUP BY am.cohorte24)25SELECT26 cohorte, pct,27 RANK() OVER (ORDER BY pct DESC) AS rank_mejor,28 RANK() OVER (ORDER BY pct ASC) AS rank_peor29FROM retention_m130ORDER BY pct DESC;
Las métricas ejecutivas que salen de la matriz: retención media, mejor cohorte, peor cohorte.
Consejo de senior: el análisis de cohortes es probablemente el análisis más valioso que puedes hacer para una empresa de producto digital. Si dominas esto en SQL, puedes replicarlo en cualquier empresa SaaS, e-commerce o app. Es una habilidad que te diferencia inmediatamente en entrevistas.
### Resumen de técnicas usadas en este proyecto
- CTEs encadenadas: cohortes_base → actividad_mensual → retencion → resultado final
- Window function agregada: AVG() OVER (PARTITION BY meses_desde_inicio) para comparar con la media
- LAG: para ver la caída de retención vs el mes anterior
- RANK: para identificar la mejor y peor cohorte
- DATE_TRUNC y DATEDIFF: manipulación temporal para agrupar por mes
- GROUP BY con COUNT(DISTINCT): para contar clientes únicos activos
En datos reales, cuidado con los meses incompletos. Si hoy estamos a 15 de marzo, la cohorte de marzo NO ha tenido un mes completo para mostrar retención. Filtra siempre por cohortes con datos completos para evitar conclusiones falsas.
Lleva este análisis a tu próxima entrevista técnica. Cuando te pregunten "¿qué proyecto SQL te enorgullece?", describe cómo construiste una matriz de retención desde cero con CTEs y window functions. Es impresionante, relevante y demuestra pensamiento de negocio además de técnica SQL.
## ejercicios
Paso 1: Asignar cohortes a clientes
Usando la tabla pedidos (completados), identifica la cohorte de cada cliente (mes de su primera compra). Resultado: cliente_id, cohorte. Cuenta cuántos clientes hay en cada cohorte.
Retención al mes 1 por cohorte
Para cada cohorte, calcula qué porcentaje de clientes volvió a comprar en el mes 1 (el mes siguiente a su primera compra). Resultado: cohorte, clientes_iniciales, volvieron_mes_1, pct_retencion.
Mejor y peor cohorte con RANK
Usando la retención al mes 1, identifica la cohorte con MEJOR retención y la de PEOR retención. Usa RANK() para asignar posiciones.
Tendencia: ¿mejora o empeora la retención?
Para cada cohorte (ordenadas cronológicamente), calcula la retención al mes 1 y usa LAG para comparar con la cohorte anterior. ¿La retención está mejorando o empeorando con el tiempo?
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...