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 AM23Pablo Herrera 9:10 AM4?? Standup viernes - día de demo5- Sandra: modelos dbt listos, va a usar la tabla del nuevo6- Marcos: billing pipeline en producción7- Nuevo: feature analysis + preparar demo89Recordatorio: demo a las 16:00. Lucia, Andres (CTO) y yo10estaremos. Preparad lo que tengais aunque no sea perfecto.11Prefiero un "esto es lo que tenemos hasta ahora" honesto12que un "no me da tiempo" a las 15:55.1314Tu 9:12 AM15Hoy me centro en el feature analysis (aha moment).16Tengo las cohortes listas y validadas con Sandra.17Para la demo: cohortes + Simpson + timing del churn18+ features correlacionadas. Tengo material de sobra.1920Pablo Herrera 9:14 AM21?? Perfecto. Si encuentras algo interesante en las features22mandamelo por DM antes de la demo -- me gusta tener contexto23antes de que Lucia haga preguntas.2425 ? 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 TaskFlow2import pandas as pd34# Cargar datos ya procesados5events = 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_at89# Definir retained vs churned10# 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()1516user_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.days2021retained_users = set(user_last[user_last['days_active'] >= 28]['user_id'])22churned_users = set(user_last[user_last['days_active'] <= 14]['user_id'])2324print(f"Retained (28+ días): {len(retained_users):,}")25print(f"Churned (<=14 días): {len(churned_users):,}")2627# Actividad en W0 (primera semana) de cada grupo28def 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ías35 user_events['days_since_signup'] = (36 user_events['event_timestamp'] - user_events['signup_date']37 ).dt.days38 w0 = user_events[user_events['days_since_signup'].between(0, 7)]3940 # Acciones por usuario por tipo41 per_user = w0.groupby(['user_id', 'event_type']).size().reset_index(name='count')42 avg_per_user = per_user.groupby('event_type')['count'].mean()4344 return avg_per_user.rename(label)4546retained_features = w0_features(retained_users, 'retained_avg')47churned_features = w0_features(churned_users, 'churned_avg')4849# Comparar50comparison = 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)5354print("\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 churned4- file_uploaded: 3.2x5- task_assigned: 2.9x6- comment_added: 2.4x78El aha moment parece ser automatizaciones. Pero solo el 12%9de usuarios lo hace en W0.1011Una duda: puede ser que automation_created sea simplemente12un proxy de "usuario power user" y no una causa de retención?1314Sandra Vidal 10:52 AM15?? Buena pregunta. Piensa así: si filtras solo usuarios con16>20 eventos en W0 (engagement alto), y DENTRO de ese grupo17comparas los que hicieron automation vs los que no...18todavía hay diferencia en retención?1920Si si la hay: la feature tiene efecto independiente del21engagement general.22Si no: es solo un proxy.2324Puedes calcularlo? Es un control por nivel de engagement.2526Tu 10:55 AM27Buena idea! Lo cálculo ahora.2829Sandra Vidal 10:56 AM30Si 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 desplazada2import pandas as pd3import json45# La "fecha actual" de la simulacion (los datos son de marzo-septiembre 2024)6FECHA_HOY = pd.Timestamp('2024-09-30')78# Las tareas recurrentes se marcan con is_recurring en properties.9# El backend pre-genera el evento con la fecha en que la tarea DEBERIA10# completarse (+14 dias), no cuando se creo -> timestamps "del futuro".11def is_recurring(props_json):12 if not props_json:13 return False14 try:15 props = json.loads(props_json) if isinstance(props_json, str) else props_json16 return bool(props.get('is_recurring', False))17 except (json.JSONDecodeError, AttributeError):18 return False1920events['is_recurring'] = events['properties_json'].apply(is_recurring)21recurring_events = events[events['is_recurring']]2223print(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()}")2728# 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, todos4exactamente +14 días. Es un bug?56David Martin 11:40 AM7Si. Es la feature de "tareas recurrentes". Cuando un usuario8crea una tarea recurrente, el backend pre-genera las instancias9futuras y emite eventos para cada una con el timestamp de10cuando DEBERIAN completarse, no cuando se CREARON.1112Es un hack que hice para que el calendario mostrara las tareas13futuras. No pense que alguien usaria el timestamp del evento14como "cuando pasó la accion".1516Lo correcto seria: timestamp = ahora (cuando se creo el evento),17y el due_date futuro va en properties. Pero cambiarlo ahora18romperia la logica del calendario.1920Tu 11:45 AM21Entendido. Para mi pipeline, necesito filtrar estos eventos22o al menos marcarlos. Si los incluyo en retención, cuento23como "activo hoy" a alguien que no hizo nada -- solo tiene24una tarea recurrente.2526David Martin 11:47 AM27Te mando un snippet para identificarlos:2829Los eventos de tareas recurrentes tienen en properties:30{"is_recurring": true, "recurrence_id": "rec_xxx"}3132Filtra por is_recurring = true y ya.3334Tu 11:49 AM35Gracias David. Lo filtro en el pipeline y documento36el edge case. Te abro un issue para el fix a largo plazo?3738David Martin 11:50 AM39Si, 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 PM23Tu 12:35 PM4Update: 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 futura8- Fix: filtrar is_recurring=true de métricas de actividad9- PR abierta con el fix + issue para David con el fix a largo plazo10- Impacto: sin el fix, inflabamos la retención ~0.8% (contabamos11 como "activos" a usuarios que solo tienen recurrentes)1213David ya confirmo la causa. No es crítico (0.8%) pero es un14ejemplo de por que las alertas de distribución que montamos15habrian detectado esto (si el volumen de task_created subiera16artificialmente por las recurrentes).1718Pablo Herrera 12:38 PM19?? Excelente trabajó de detective. El 0.8% no es enorme pero20en un análisis de retención donde estamos mirando cambios de217pp, puede distorsionar conclusiones.22Me gusta que lo hayas documentado Y abierto issue. Eso es23ownership.2425Sandra Vidal 12:40 PM26Gran hallazgo ?? Sin esto habriamos tenido un número27artificialmente inflado en las cohortes de equipos que usan28recurrentes (que son justamente los más activos). Simpson's29paradox parte 2 casi jaja.3031Marcos Delgado 12:42 PM32Buen catch.3334 ?? 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
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.
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.
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.
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...