lección 6
Día 4: Atribucion multi-touch y pipeline completo
Implementa atribución multi-touch (lineal + time-decay), completa el pipeline end-to-end y recibe code review de Ivan.
⏱ 45 min
### Viernes 8:30 AM -- El día de la presentación
Hoy es el día. La call con Ana es a las 10:00. Carmen quiere tener todo cerrado a las 9:30 para repasar. Pero antes, Ivan quiere que dejes el pipeline "production-ready": documentado, testeable y con la logica de atribución completa (no solo el factor proporcional sino también el modelo lineal como backup).
1#canal-pharmalife [08:35]23Ivan Romero: Buenos días. Antes de la call con Ana quiero4revisar tu pipeline completo. Sube el PR con todo:51. Script de ingesta (3 fuentes)62. Unificacion con mapping73. Reconciliacion proporcional84. Atribucion lineal (como análisis adicional)95. Query del report semanal1011Quiero ver el PR listo para merge antes de las 9:30.12Si algo no está claro, me preguntas ahora.1314Carmen Delgado: @Ivan @Tu - la call con Ana es a las 10.15Repasamos a las 9:30 en la sala ROAS. Traed el resumen16ejecutivo impreso (si, en papel, Ana es old school).
La mañana del viernes: pipeline completo + presentación al cliente
### El pipeline end-to-end
Antes de subir el PR, organizas el código en una estructura clara. Ivan es estricto con la organizacion: cada script tiene una responsabilidad única, los nombres son descriptivos, hay un README que explica como ejecutar todo, y los tests basicos cubren las funciones críticas. "Si un colega nuevo llega el lunes y necesita entender tu pipeline, deberia poder hacerlo solo leyendo el README y los nombres de los archivos," dice Ivan.
Te sientas 20 minutos a reorganizar antes de subir el PR. Mueves cosas de sitio, renombras un par de funciones (Ivan odia nombres genericos como "process_data") y escribes el README con las instrucciones de ejecucion. Es trabajó "invisible" que no produce resultados visibles inmediatos, pero Ivan lo valora más que el código en si: "El 80% de la vida de un pipeline se pasa en mantenimiento. Si hoy escribes código que otros entienden, mañana te lo agradeceran (o te lo agradeceras tu mismo en 3 meses cuando no recuerdes que hacía)."
1pipelines/pharmalife/2 README.md <- como ejecutar, dependencias, contactó3 config.py <- constantes (paths, factor, thresholds)4 01_ingest_google.py <- carga CSV de S3 a stg_google5 02_ingest_meta.py <- pull API Meta, aplana JSON, carga stg_meta6 03_ingest_email.py <- carga CSV email, distribuye a diario7 04_unify.py <- JOIN con mapping, carga unified_campaigns8 05_reconcile.py <- calcula factor, genera tabla reconciliada9 06_attribution_linear.py <- atribución lineal desde CRM10 07_report_weekly.py <- genera informe semanal11 08_detect_orphans.py <- detecta campañas sin mapping, alerta Slack12 sql/13 create_tables.sql <- DDL de todas las tablas14 quality_checks.sql <- queries de validación post-carga15 weekly_report.sql <- query del report semanal16 tests/17 test_transform_meta.py <- test unitario de la transformacion Meta18 test_mapping_uniqueness.py <- verifica que mapping no tiene duplicados19 test_factor_range.py <- factor debe estar entre 0.5 y 0.9
Estructura del pipeline: un script por pasó, SQL separado, tests basicos
### Atribucion time-decay: el refinamiento
Ademas de la atribución lineal (que ya implementaste), Ivan te pide implementar time-decay como alternativa. "Ana probablemente no lo vea hoy, pero quiero tenerlo listo para cuando pregunte por que Meta sale mejor que Google. Con time-decay, el último touchpoint recibe más credito -- y eso podria cambiar el ranking."
1from typing import Dict, List, Tuple23def time_decay_attribution(4 amount: float,5 touchpoints: List[Tuple[str, str]],6 decay_factor: float = 0.57) -> Dict[str, float]:8 """9 Atribucion time-decay: los touchpoints más recientes reciben más credito.1011 El último touchpoint recibe peso 1.0, el penultimo 0.5 (decay_factor),12 el anterior 0.25 (decay_factor^2), etc.1314 Ejemplo con decay_factor=0.5 y 3 touchpoints:15 Pesos brutos: [0.25, 0.5, 1.0] (del primero al último)16 Pesos normalizados: [0.143, 0.286, 0.571] (suman 1.0)17 Revenue: [amount*0.143, amount*0.286, amount*0.571]18 """19 n = len(touchpoints)20 if n == 0:21 return {}2223 # Calcular pesos (último = 1.0, penultimo = decay, etc.)24 weights = [decay_factor ** (n - 1 - i) for i in range(n)]25 total_weight = sum(weights)2627 # Normalizar y distribuir28 result: Dict[str, float] = {}29 for i, (source, _) in enumerate(touchpoints):30 normalized_weight = weights[i] / total_weight31 revenue_share = amount * normalized_weight32 result[source] = result.get(source, 0) + revenue_share3334 return result3536# Ejemplo: venta de 90 EUR, touchpoints: google -> email -> meta37tp = [('google', 'PL_Search_X'), ('email', 'Weekly_Y'), ('meta', 'pharmalife_Z')]38result = time_decay_attribution(90.0, tp, decay_factor=0.5)39# google: 90 * 0.25/1.75 = 12.86 EUR (14.3%)40# email: 90 * 0.50/1.75 = 25.71 EUR (28.6%)41# meta: 90 * 1.00/1.75 = 51.43 EUR (57.1%)42print(result) # {'google': 12.86, 'email': 25.71, 'meta': 51.43}
Time-decay: el último canal recibe más credito -- favorece canales de cierre
### Code review de Ivan: el PR final
A las 9:10 subes el PR con todo el pipeline. Ivan lo revisa en 15 minutos (es rápido cuando hay urgencia) y deja comentarios constructivos:
1PR #251: feat(pharmalife): pipeline completo v1 + atribución23Ivan Romero [09:25]:4> Aprobado. 3 comentarios menores para la v2:5>6> 1. config.py: el factor 0.6688 está hardcodeado. Para la v27> deberia calcularse dinamicamente cada semana. Deja un TODO.8>9> 2. 06_attribution_linear.py: buena implementación. Me gusta10> que separaste parse_touchpoints() como función pura testeable.11> Anade un test con un caso edge: venta con un solo touchpoint.12>13> 3. 08_detect_orphans.py: perfecto que generes el mensaje de14> Slack formateado. Pero falta el pasó de ENVIARLO realmente.15> Para el MVP, printea el mensaje y que alguien lo copie.16> Para la v2, usa la API de Slack (webhook).17>18> GENERAL: Buen trabajó para 4 días. La estructura es limpia,19> la nomenclatura es consistente y el README explica bien el20> flujo. Merge aprobado.
PR aprobado: feedback constructivo para iterar después de la presentación
En un equipo profesional, "aprobado con comentarios menores" es el resultado más comun de un buen PR. No esperes que tu código sea perfecto la primera vez -- espera que sea correcto, legible y mejorable. Los comentarios de Ivan no son críticas: son la hoja de ruta para la v2.
### La importancia de la idempotencia
Ivan aprovecha la revision para ensearte un concepto clave: "Fijate en el ON CONFLICT DO NOTHING del SQL. Eso es idempotencia. Significa que si ejecutas el pipeline 2 veces con los mismos datos, el resultado es el mismo. No duplicas filas, no corrompes datos. Es ESENCIAL para pipelines de producción porque los DAGs a veces se re-ejecutan (por fallos, por backfill, por error humano)."
1IDEMPOTENCIA = ejecutar N veces produce el mismo resultado que ejecutar 1 vez.23Patron 1 (el nuestro): ON CONFLICT DO NOTHING4 - Si la fila ya existe, no hace nada. Simple pero no actualiza cambios.56Patron 2: DELETE + INSERT (truncate and reload)7 - Borra todo del período y reinsertas. Mas agresivo pero garantiza freshness.89Patron 3: ON CONFLICT DO UPDATE (upsert)10 - Si existe, actualiza. Si no, inserta. El más completo pero más complejo.1112Para el MVP usamos Patron 1. Para producción moveremos a Patron 213(DELETE WHERE date BETWEEN start AND end, luego INSERT).14Es más limpio para re-ejecuciones y permite corregir datos históricos.
Idempotencia: un pipeline que puedes re-ejecutar sin miedo a duplicar
### Diagrama del flujo de reconciliación
## ejercicios
Implementar atribución time-decay en SQL
Implementa el modelo time-decay directamente en SQL. Cada touchpoint recibe un peso exponencial decreciente segun su posición (el último pesa más).
Comparar los 3 modelos de atribución
Escribe un script Python que calcule ROAS con los 3 modelos (proporcional, lineal, time-decay) y presente una tabla comparativa para que Carmen elija cual presentar a Ana.
Escribir tests de calidad para el pipeline
Implementa funciones de test en Python que validen la integridad del pipeline: el factor está en rango razonable, no hay duplicados, las sumas cuadran y el mapping cubre >90% del gasto.
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...