lección 5

Día 4 - Feature analysis y el bug del timestamp

Identificas el "aha moment" del producto, descubres un bug crítico en los timestamps de David, y aprendes que los datos pueden mentirte sin que lo sepas.

50 min

### Viernes mañana, 9:00 AM - Buscando el "aha moment"

Viernes. La demo es a las 16:00. Tienes la mañana para responder la gran pregunta que Lucía planteó ayer: "qué hacen los usuarios que se quedan que NO hacen los que se van?" En SaaS esto se llama encontrar el "aha moment" -- la acción o conjunto de acciones que correlacionan con retención a largo plazo.

Ejemplos clásicos: en Facebook era "agregar 7 amigos en 10 días". En Slack era "enviar 2.000 mensajes en el workspace". En Dropbox era "subir un archivo en los primeros 5 minutos". El aha moment de TaskFlow está escondido en los datos de eventos. Tu trabajo: encontrarlo.

1#data-team | Viernes 9:10 AM
2
3Pablo Herrera 9:10 AM
4?? Standup viernes - día de demo
5- Sandra: modelos dbt listos, va a usar la tabla del nuevo
6- Marcos: billing pipeline en producción
7- Nuevo: feature analysis + preparar demo
8
9Recordatorio: demo a las 16:00. Lucia, Andres (CTO) y yo
10estaremos. Preparad lo que tengais aunque no sea perfecto.
11Prefiero un "esto es lo que tenemos hasta ahora" honesto
12que un "no me da tiempo" a las 15:55.
13
14Tu 9:12 AM
15Hoy me centro en el feature analysis (aha moment).
16Tengo las cohortes listas y validadas con Sandra.
17Para la demo: cohortes + Simpson + timing del churn
18+ features correlacionadas. Tengo material de sobra.
19
20Pablo Herrera 9:14 AM
21?? Perfecto. Si encuentras algo interesante en las features
22mandamelo por DM antes de la demo -- me gusta tener contexto
23antes de que Lucia haga preguntas.
24
25 ? 2

El approach es comparar dos grupos: usuarios que siguen activos después de W4 ("retained") vs usuarios que dejaron de usar el producto antes de W4 ("churned"). Para cada grupo calculas la frecuencia de cada tipo de evento en su primera semana (W0). Si un evento es mucho más frecuente en los retained, es candidato a aha moment.

1# feature_analysis.py - Buscar el "aha moment" de TaskFlow
2import pandas as pd
3
4# Cargar datos ya procesados
5events = pd.read_parquet('datos/stg_product_events.parquet')
6users = pd.read_csv('datos/usuarios.csv')
7users['signup_date'] = pd.to_datetime(users['user_created_at']) # la columna real es user_created_at
8
9# Definir retained vs churned
10# Retained: activo después de W4 (28+ días post-signup)
11# Churned: último evento core antes de W2 (14 días)
12user_last = events[events['event_category'] == 'core'].groupby('user_id').agg(
13 last_event=('event_timestamp', 'max')
14).reset_index()
15
16user_last = user_last.merge(users[['user_id', 'signup_date']], on='user_id')
17user_last['days_active'] = (
18 user_last['last_event'] - user_last['signup_date']
19).dt.days
20
21retained_users = set(user_last[user_last['days_active'] >= 28]['user_id'])
22churned_users = set(user_last[user_last['days_active'] <= 14]['user_id'])
23
24print(f"Retained (28+ días): {len(retained_users):,}")
25print(f"Churned (<=14 días): {len(churned_users):,}")
26
27# Actividad en W0 (primera semana) de cada grupo
28def w0_features(user_set, label):
29 """Calcula eventos por tipo en la primera semana."""
30 user_events = events[events['user_id'].isin(user_set)].copy()
31 user_events = user_events.merge(
32 users[['user_id', 'signup_date']], on='user_id'
33 )
34 # Solo W0: primeros 7 días
35 user_events['days_since_signup'] = (
36 user_events['event_timestamp'] - user_events['signup_date']
37 ).dt.days
38 w0 = user_events[user_events['days_since_signup'].between(0, 7)]
39
40 # Acciones por usuario por tipo
41 per_user = w0.groupby(['user_id', 'event_type']).size().reset_index(name='count')
42 avg_per_user = per_user.groupby('event_type')['count'].mean()
43
44 return avg_per_user.rename(label)
45
46retained_features = w0_features(retained_users, 'retained_avg')
47churned_features = w0_features(churned_users, 'churned_avg')
48
49# Comparar
50comparison = pd.concat([retained_features, churned_features], axis=1).fillna(0)
51comparison['ratio'] = comparison['retained_avg'] / comparison['churned_avg'].clip(0.1)
52comparison = comparison.sort_values('ratio', ascending=False)
53
54print("\nTop 10 features con mayor ratio retained/churned:")
55print(comparison.head(10).round(2))

Feature analysis: comparar comportamiento W0 de retained vs churned

Los resultados son reveladores. Las features con mayor ratio retained/churned son: (1) automation_created (ratio 4.8x), (2) file_uploaded (3.2x), (3) task_assigned (2.9x), (4) comment_added (2.4x), (5) board_created (2.1x). Los usuarios que crean automatizaciones en su primera semana se quedan MUCHO más. Pero solo el 12% de usuarios crea una automatización en W0.

Esto tiene sentido intuitivo: crear una automatización requiere entender bien el producto, haber usado boards lo suficiente para necesitar automatizar algo, y haber pasado la curva de aprendizaje. Es una señal de engagement PROFUNDO. La pregunta es: si guías a más usuarios hacia esa feature temprano, retendrán más? O simplemente los que la descubren son los que ya iban a quedarse de todos modos?

1Tu 10:45 AM (DM a Sandra)
2Sandra, mira estos ratios del feature analysis:
3- automation_created: 4.8x más frecuente en retained vs churned
4- file_uploaded: 3.2x
5- task_assigned: 2.9x
6- comment_added: 2.4x
7
8El aha moment parece ser automatizaciones. Pero solo el 12%
9de usuarios lo hace en W0.
10
11Una duda: puede ser que automation_created sea simplemente
12un proxy de "usuario power user" y no una causa de retención?
13
14Sandra Vidal 10:52 AM
15?? Buena pregunta. Piensa así: si filtras solo usuarios con
16>20 eventos en W0 (engagement alto), y DENTRO de ese grupo
17comparas los que hicieron automation vs los que no...
18todavía hay diferencia en retención?
19
20Si si la hay: la feature tiene efecto independiente del
21engagement general.
22Si no: es solo un proxy.
23
24Puedes calcularlo? Es un control por nivel de engagement.
25
26Tu 10:55 AM
27Buena idea! Lo cálculo ahora.
28
29Sandra Vidal 10:56 AM
30Si sale significativo, es un hallazgo de producto ENORME.
31Lucia va a querer hacer algo con eso inmediatamente ??

Haces el cálculo que Sandra sugirió: filtras solo usuarios "engaged" (>20 eventos core en W0) y dentro de ese grupo comparas los que crearon automatización vs los que no. Resultado: incluso controlando por engagement general, los que crean automatizaciones retienen un 23% más. No es tan dramático como el 4.8x (que mezclaba engagement), pero sigue siendo significativo. La feature tiene efecto propio.

### 11:00 AM - El bug: timestamps del futuro

Estas profundizando en el análisis cuando notas algo raro: 2.341 eventos tienen timestamps en el FUTURO. No mañana -- 3 semanas en el futuro. Imposible. Empiezas a investigar:

1# Investigar eventos anomalos: tareas recurrentes con fecha desplazada
2import pandas as pd
3import json
4
5# La "fecha actual" de la simulacion (los datos son de marzo-septiembre 2024)
6FECHA_HOY = pd.Timestamp('2024-09-30')
7
8# Las tareas recurrentes se marcan con is_recurring en properties.
9# El backend pre-genera el evento con la fecha en que la tarea DEBERIA
10# completarse (+14 dias), no cuando se creo -> timestamps "del futuro".
11def is_recurring(props_json):
12 if not props_json:
13 return False
14 try:
15 props = json.loads(props_json) if isinstance(props_json, str) else props_json
16 return bool(props.get('is_recurring', False))
17 except (json.JSONDecodeError, AttributeError):
18 return False
19
20events['is_recurring'] = events['properties_json'].apply(is_recurring)
21recurring_events = events[events['is_recurring']]
22
23print(f"Eventos recurrentes (timestamp desplazado +14d): {len(recurring_events):,}")
24print(f"\nTipos de evento afectados:")
25print(recurring_events['event_type'].value_counts())
26print(f"\nWorkspaces afectados: {recurring_events['workspace_id'].nunique()}")
27
28# Cuantos caen despues de la fecha de corte de los datos?
29posteriores = recurring_events[recurring_events['event_timestamp'] > FECHA_HOY]
30print(f"\nDe esos, con fecha posterior al corte de datos: {len(posteriores):,}")

Investigando eventos recurrentes con fecha desplazada - instinto de data engineer

Los 2.341 eventos con timestamps futuros son todos del tipo task_created y task_completed. Todos de los mismos 12 workspaces. Y todos tienen timestamps exactamente 14 días adelantados. 14 días. No es aleatorio. Es un bug sistemático.

### 11:30 AM - Hablar con David

1Tu 11:32 AM (DM a David Martin)
2David, he encontrado 2.341 eventos con timestamp en el futuro.
3Todos task_created/completed, mismos 12 workspaces, todos
4exactamente +14 días. Es un bug?
5
6David Martin 11:40 AM
7Si. Es la feature de "tareas recurrentes". Cuando un usuario
8crea una tarea recurrente, el backend pre-genera las instancias
9futuras y emite eventos para cada una con el timestamp de
10cuando DEBERIAN completarse, no cuando se CREARON.
11
12Es un hack que hice para que el calendario mostrara las tareas
13futuras. No pense que alguien usaria el timestamp del evento
14como "cuando pasó la accion".
15
16Lo correcto seria: timestamp = ahora (cuando se creo el evento),
17y el due_date futuro va en properties. Pero cambiarlo ahora
18romperia la logica del calendario.
19
20Tu 11:45 AM
21Entendido. Para mi pipeline, necesito filtrar estos eventos
22o al menos marcarlos. Si los incluyo en retención, cuento
23como "activo hoy" a alguien que no hizo nada -- solo tiene
24una tarea recurrente.
25
26David Martin 11:47 AM
27Te mando un snippet para identificarlos:
28
29Los eventos de tareas recurrentes tienen en properties:
30{"is_recurring": true, "recurrence_id": "rec_xxx"}
31
32Filtra por is_recurring = true y ya.
33
34Tu 11:49 AM
35Gracias David. Lo filtro en el pipeline y documento
36el edge case. Te abro un issue para el fix a largo plazo?
37
38David Martin 11:50 AM
39Si, abrelo. Le pongo prioridad media.
40Buena captura.

Este tipo de bug es INVISIBLE si no exploras los datos. Los timestamps futuros no aparecen si solo haces un COUNT(*) GROUP BY week. Solo los ves si miras la distribución temporal con ojo crítico. En la vida real, los bugs de tracking son la norma. El backend emite lo que le parece lógico desde SU perspectiva -- tu trabajo es descubrir cuándo esa perspectiva no encaja con la analítica.

Actualizas tu pipeline para filtrar eventos con is_recurring = true del cálculo de retención. Los eventos existen en la tabla (para análisis de uso de la feature) pero se excluyen de las métricas de actividad. Documentas el edge case en el Notion de definiciones.

### 12:30 PM - El PR del fix y la conversacion con Pablo

1#data-team | Viernes 12:35 PM
2
3Tu 12:35 PM
4Update: he encontrado y resuelto un bug en los datos de eventos.
5Resumen:
6- 2.341 eventos con timestamps futuros (hasta +14 días)
7- Causa: tareas recurrentes pre-generan eventos con fecha futura
8- Fix: filtrar is_recurring=true de métricas de actividad
9- PR abierta con el fix + issue para David con el fix a largo plazo
10- Impacto: sin el fix, inflabamos la retención ~0.8% (contabamos
11 como "activos" a usuarios que solo tienen recurrentes)
12
13David ya confirmo la causa. No es crítico (0.8%) pero es un
14ejemplo de por que las alertas de distribución que montamos
15habrian detectado esto (si el volumen de task_created subiera
16artificialmente por las recurrentes).
17
18Pablo Herrera 12:38 PM
19?? Excelente trabajó de detective. El 0.8% no es enorme pero
20en un análisis de retención donde estamos mirando cambios de
217pp, puede distorsionar conclusiones.
22Me gusta que lo hayas documentado Y abierto issue. Eso es
23ownership.
24
25Sandra Vidal 12:40 PM
26Gran hallazgo ?? Sin esto habriamos tenido un número
27artificialmente inflado en las cohortes de equipos que usan
28recurrentes (que son justamente los más activos). Simpson's
29paradox parte 2 casi jaja.
30
31Marcos Delgado 12:42 PM
32Buen catch.
33
34 ?? 3 ?? 2

Consejo de senior: cada bug que descubres es una oportunidad de mejorar la documentación. El hecho de que "is_recurring cambia la semántica del timestamp" debería estar en un CONTRATO DE DATOS que el equipo de backend y el de datos comparten. Sin ese contrato, cada nuevo data engineer tropezará con el mismo bug. Tu issue en GitHub es el primer paso hacia ese contrato.

## ejercicios

[01]

Calcular el ratio de features retained vs churned

Compara la actividad W0 de usuarios retained (28+ días) vs churned (<=14 días). Para cada tipo de evento, calcula el promedio por usuario en cada grupo y el ratio.

Cargando editor...
[02]

Detectar y filtrar eventos con timestamp anomalo

Escribe código que detecta eventos con timestamps imposibles (futuros o muy antiguos) y los marca para excluirlos de métricas de actividad.

Cargando editor...
[03]

Query: usuarios que crearon automatizaciones en W0

Lucia quiere saber: de los usuarios que crearon una automatización en su primera semana, cual es su retención W4 comparada con los que no? Escribe la query.

Cargando editor...

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...