Módulo 3: Cohort Retention

Leyendo una tabla de cohortes

Descripción

Las lecciones anteriores miraron cohortes de a una. En la práctica, ningún equipo de producto analiza una sola cohorte aislada — arman una tabla de cohortes (cohort table): una fila por cada semana de entrada, una columna por cada semana desde esa entrada, y en cada celda, el porcentaje de retención de esa cohorte en ese punto de su vida. Es, con diferencia, la visualización más usada en cualquier herramienta de analítica de producto (Amplitude, Mixpanel, y prácticamente cualquier otra), y tiene una forma muy particular: es triangular. Las cohortes más viejas tienen muchas columnas llenas (llevan más tiempo, así que hay más semanas que medir); las cohortes más nuevas tienen pocas o ninguna —simplemente no existieron todavía el tiempo suficiente—.

Esa forma triangular no es un defecto de la tabla: es información. Y la tabla completa se puede leer de dos formas distintas, que contestan dos preguntas distintas — leer una fila (una sola cohorte, en el tiempo) contesta "¿cómo se fuga esta cohorte específica?"; leer una columna (todas las cohortes, en el mismo punto de vida) contesta "¿el producto está mejorando en retener gente, generación tras generación?". Esta lección te enseña a leer ambas.

Conexión con el módulo. Esta lección reutiliza la misma fórmula de tasa de retentionCurve() (activos ÷ inicial × 100, lección 3) para construir una tabla completa, y resuelve exactamente la trampa que dejó pendiente la lección 5: cómo comparar cohortes distintas sin caer en el error de mezclar semanas de vida distintas.

Una analogía: el registro de camadas de un refugio de mascotas

Un refugio de animales lleva un registro por camada (litter): cada grupo de cachorros nacidos la misma semana forma su propia fila, y el refugio anota, semana tras semana desde el nacimiento, cuántos de esa camada específica siguen en adopción activa (no adoptados todavía, no perdidos el rastro). La camada nacida hace tres meses tiene doce semanas de registro; la camada nacida ayer tiene una sola columna llena —la de esta misma semana—, y el resto de su fila queda en blanco, no porque falte el dato, sino porque ese dato todavía no existe: no ha pasado el tiempo suficiente.

Si el refugio quiere saber "¿cómo le fue a la camada de marzo en particular?", lee esa fila de punta a punta. Pero si quiere saber "¿estamos mejorando en encontrarle hogar a los cachorros más rápido que antes?", no mira una fila — compara la misma semana de vida entre camadas distintas: cuántos de la camada de enero seguían sin adoptar en su semana 2, contra cuántos de la camada de marzo seguían sin adoptar en su propia semana 2. Esa es, exactamente, la diferencia entre leer por fila y leer por columna en una tabla de cohortes de producto.

Ejemplo trabajado: la tabla de cohortes de Mercado

Cinco cohortes de Mercado, una por semana. Reutilizamos la misma fórmula de tasa que retentionCurve() para construir la tabla completa, con las cohortes más nuevas dejando celdas vacías donde todavía no hay datos:

// Reutilizamos la formula de tasa de retentionCurve() (leccion 3): activos / inicial * 100.
// Cinco cohortes de Mercado, una por semana. Las mas nuevas todavia no tienen
// suficientes semanas de vida como para llenar toda la fila -- por eso la tabla
// sale TRIANGULAR: cada fila mas nueva tiene menos columnas que la anterior.
const cohorts = [
  { week: '2026-06-01', active: [1000, 380, 290, 255, 248] },
  { week: '2026-06-08', active: [1100, 430, 320, 285] },
  { week: '2026-06-15', active: [950, 390, 300] },
  { week: '2026-06-22', active: [1200, 510] },
  { week: '2026-06-29', active: [1300] },
];

function cohortTable(cohorts) {
  const maxWeeks = Math.max(...cohorts.map((c) => c.active.length));
  const header = ['Cohorte'.padEnd(12)]
    .concat(Array.from({ length: maxWeeks }, (_, w) => ('W' + w).padStart(6)))
    .join(' | ');
  console.log(header);
  console.log('-'.repeat(header.length));
  cohorts.forEach((c) => {
    const rates = c.active.map((a) => ((a / c.active[0]) * 100).toFixed(0) + '%');
    const cells = Array.from({ length: maxWeeks }, (_, w) => (rates[w] ? rates[w] : '--').padStart(6));
    console.log(c.week.padEnd(12) + ' | ' + cells.join(' | '));
  });
}

console.log('=== Tabla de cohortes de Mercado (triangular) ===\n');
cohortTable(cohorts);

console.log('\n=== Leyendo una FILA (una sola cohorte a lo largo del tiempo) ===');
console.log('Cohorte 2026-06-01: 100% -> 38% -> 29% -> 25.5% -> 24.8%  (su propia fuga, semana a semana)');

console.log('\n=== Leyendo una COLUMNA (todas las cohortes en el MISMO punto de vida) ===');
const w1 = cohorts.filter((c) => c.active.length > 1).map((c) => ({
  week: c.week,
  rate: ((c.active[1] / c.active[0]) * 100).toFixed(1) + '%',
}));
w1.forEach((c) => console.log('  ' + c.week + ' en su semana 1: ' + c.rate));
console.log('\nLa columna W1 sube de 38.0% a 42.5% cohorte tras cohorte: cada generacion');
console.log('mas nueva retiene mejor en su primera semana que la anterior -- una senal real');
console.log('de mejora, que NUNCA se ve comparando filas de distinto largo entre si.');

Qué esperar. Al correr el archivo con Node, la salida es exactamente esta:

=== Tabla de cohortes de Mercado (triangular) ===

Cohorte      |     W0 |     W1 |     W2 |     W3 |     W4
---------------------------------------------------------
2026-06-01   |   100% |    38% |    29% |    26% |    25%
2026-06-08   |   100% |    39% |    29% |    26% |     --
2026-06-15   |   100% |    41% |    32% |     -- |     --
2026-06-22   |   100% |    43% |     -- |     -- |     --
2026-06-29   |   100% |     -- |     -- |     -- |     --

=== Leyendo una FILA (una sola cohorte a lo largo del tiempo) ===
Cohorte 2026-06-01: 100% -> 38% -> 29% -> 25.5% -> 24.8%  (su propia fuga, semana a semana)

=== Leyendo una COLUMNA (todas las cohortes en el MISMO punto de vida) ===
  2026-06-01 en su semana 1: 38.0%
  2026-06-08 en su semana 1: 39.1%
  2026-06-15 en su semana 1: 41.1%
  2026-06-22 en su semana 1: 42.5%

La columna W1 sube de 38.0% a 42.5% cohorte tras cohorte: cada generacion
mas nueva retiene mejor en su primera semana que la anterior -- una senal real
de mejora, que NUNCA se ve comparando filas de distinto largo entre si.

Mira la forma triangular de la tabla: la fila de 2026-06-29 (la cohorte más nueva, apenas entrando) solo tiene la columna W0 llena — el resto son guiones, no ceros. Esa distinción importa: un guion significa "todavía no pasó suficiente tiempo para saberlo"; un cero significaría "sabemos que nadie de esta cohorte sigue activo", que es una afirmación completamente distinta y, en este caso, falsa. Confundir "no hay dato todavía" con "el dato es cero" es un error de lectura común, y el motivo por el que cohortTable() de hoy imprime -- en vez de forzar un 0%.

Ahora compara las dos lecturas. Leyendo la fila de 2026-06-01, ves la fuga característica de una sola cohorte: 100% → 38% → 29% → 26% → 25%, la misma forma de caída-y-meseta de las lecciones 3 y 4. Leyendo la columna W1 de arriba hacia abajo —38.0%, 39.1%, 41.1%, 42.5%—, ves algo que ninguna fila sola podría mostrarte: cada cohorte más nueva retiene mejor, en su propia primera semana, que la cohorte anterior en la suya. Esa mejora progresiva column por columna es exactamente el tipo de señal real de mejora de producto que el WAU agregado de la lección 5 no puede distinguir de un simple aumento de volumen.

Por qué comparar filas de distinto largo es un error

Es tentador, mirando la tabla de hoy, comparar el 25% final de la fila 2026-06-01 (su semana 4) contra el 43% de la fila 2026-06-22 (apenas su semana 1) y concluir "la cohorte de junio 22 está reteniendo mucho mejor". Esa comparación está midiendo dos cosas distintas: una cohorte que ya pasó por toda su caída inicial y llegó a su meseta, contra otra que todavía no tuvo tiempo de empezar a fugarse en serio. La forma correcta de comparar siempre respeta la columna: W1 contra W1, W4 contra W4 — nunca la última celda disponible de una fila contra la última celda disponible de otra, si esas celdas no están en la misma columna.

Errores comunes

Comparar cohortes de distinto largo de vida. Qué pasa: alguien compara el último dato disponible de la fila 2026-06-01 (25%, su semana 4) contra el último dato disponible de 2026-06-22 (43%, apenas su semana 1), como si fueran comparables. Por qué pasa: ambos son "el número más reciente que tenemos de esta cohorte", y comparar "lo más reciente contra lo más reciente" se siente natural, aunque corresponda a semanas de vida completamente distintas. Cómo detectarlo: la comparación no verifica que ambas celdas estén en la misma columna (W). Cómo corregirlo: como en el ejemplo de hoy, compara siempre columna contra columna —la misma cantidad de semanas desde la entrada— nunca "el dato más nuevo de cada fila".

Leer solo filas y nunca columnas. Qué pasa: un equipo revisa la tabla de cohortes cada semana, mirando siempre cómo evoluciona la cohorte más reciente (lectura por fila), sin nunca comparar la misma columna entre distintas cohortes. Por qué pasa: seguir una sola cohorte en el tiempo se siente como la forma "natural" de leer una tabla —de izquierda a derecha—, mientras que leer una columna de arriba hacia abajo requiere un movimiento de atención menos habitual. Cómo detectarlo: en meses de revisar la tabla, nadie del equipo puede decir si la retención de la semana 1 está mejorando o empeorando generación tras generación —solo pueden hablar de cómo le fue a la cohorte más reciente. Cómo corregirlo: revisa la tabla completa en ambas direcciones cada vez, como hizo el ejemplo de hoy — la columna es, muchas veces, donde vive la señal de si el producto está mejorando de verdad.

Rellenar las celdas vacías con 0% o con un promedio. Qué pasa: alguien completa el triángulo con ceros o con el promedio de las filas existentes, "para que la tabla se vea completa" en un reporte. Por qué pasa: una tabla con huecos se siente incompleta o poco profesional, y rellenar los espacios en blanco parece una mejora visual. Cómo detectarlo: la tabla final no tiene ninguna celda vacía, aunque incluya cohortes que apenas llevan una o dos semanas de vida. Cómo corregirlo: deja las celdas sin dato como tales —un guion, un espacio en blanco, null— nunca como cero. Un cero afirma "sabemos que esta cohorte llegó a cero en esta semana", que es información falsa; un espacio en blanco dice, correctamente, "todavía no lo sabemos".

Ejercicios

Ejercicio 1 — Lee la tabla de hoy. Usando la tabla de cohortes ejecutada arriba, ¿cuál cohorte tuvo la retención más alta en su semana 2 (W2)? Da el nombre de la cohorte y el porcentaje.

Ver solución

La cohorte 2026-06-15, con 32% en su W2 — más alta que 2026-06-01 (29%) y 2026-06-08 (29%). Es la misma columna que la lección menciona como tendencia de mejora: cada cohorte más nueva con datos en W2 retiene un poco mejor que la anterior en ese mismo punto de vida.

Ejercicio 2 — Completa una fila nueva. Una sexta cohorte, 2026-07-06, tiene estos datos: [1050, 470]. Calcula sus tasas para W0 y W1, y di en qué columnas de la tabla original (arriba) se podría comparar directamente contra esta nueva fila.

Ver solución

Tasas: W0 = 1050/1050 = 100%; W1 = 470/1050 = 44.8% (redondeado, 45%). Esta fila solo tiene datos en W0 y W1, así que solo se puede comparar de forma directa (columna contra columna) contra esas mismas dos columnas de las otras cinco cohortes — es decir, contra W1 de 2026-06-01 (38%), 2026-06-08 (39%), 2026-06-15 (41%) y 2026-06-22 (43%). No se puede comparar, por ejemplo, contra el W4 de 2026-06-01 (25%), porque esta cohorte nueva todavía no tiene esa columna. De hecho, con 44.8-45%, esta sexta cohorte continuaría la misma tendencia de mejora progresiva en W1 que ya se veía en la tabla original.

Ejercicio 3 — Diseña la alerta correcta. El equipo de Mercado quiere una alerta automática que dispare cuando una cohorte nueva esté reteniendo peor que la cohorte anterior en el mismo punto de vida (una señal de que algo empeoró en el producto). Describe, en pseudocódigo o en prosa precisa, qué dos celdas exactas de la tabla habría que comparar para esa cohorte y esa semana.

Ver solución

Para la cohorte que entró en la semana N y su dato disponible más reciente en la columna W_k, la alerta debería comparar tabla[cohorte_N][W_k] contra tabla[cohorte_N-1][W_k] — la misma columna (W_k), pero la fila de la cohorte inmediatamente anterior. Si tabla[cohorte_N][W_k] < tabla[cohorte_N-1][W_k], dispara la alerta. La condición explícita de "misma columna, filas consecutivas" es la parte que garantiza una comparación justa; comparar contra cualquier otra columna, o contra un promedio de varias cohortes anteriores, reintroduciría exactamente el error de "comparar cohortes de distinto largo de vida" de esta lección.

Resumen y siguiente paso

En esta lección construiste una tabla de cohortes completa —triangular por definición, con cohortes más nuevas mostrando menos columnas llenas— y aprendiste a leerla en sus dos direcciones: por fila (la fuga de una cohorte específica en el tiempo) y por columna (si el producto mejora, generación tras generación, en el mismo punto de vida). Viste, con datos reales de Mercado, cómo la columna W1 reveló una mejora progresiva —38% a 43%— que ninguna fila sola podría haber mostrado.

Antes de avanzar deberías poder: construir una tabla de cohortes a partir de varios arreglos de datos, explicar por qué la tabla sale triangular, y comparar dos cohortes correctamente usando la misma columna en vez del último dato disponible de cada una.

La lección 7 cierra el marco conceptual del módulo: cómo se define con precisión una ventana de retención como "D7" o "D30" (el nombre formal de cada columna de esta tabla), y por qué, de todas las métricas que puede medir un equipo de producto, la retención es la que mejor aproxima el valor real que el producto entrega.

Recursos

  • Mixpanel, "Cohort analysis: How to read the chart, choose a platform, and turn retention into growth" — mixpanel.com/blog/cohort-analysis. Instruye, literalmente, "lee a lo largo de una fila para seguir una cohorte" y "lee hacia abajo en una columna para comparar cohortes en el mismo punto de su vida" — la fuente directa de la distinción central de esta lección. En inglés.
  • Mixpanel, "Retention: Measure engagement over time" (documentación oficial) — docs.mixpanel.com/docs/reports/retention. La referencia técnica de cómo una herramienta real de producto construye y muestra esta misma tabla triangular. En inglés.
  • Amplitude, "What Is Cohort Retention Analysis: Essential Metrics Guide" — amplitude.com/explore/analytics/cohort-retention-analysis. Complementa con ejemplos visuales de la tabla triangular en un producto real de analítica. En inglés.