[{"data":1,"prerenderedAt":1253},["ShallowReactive",2],{"article-alternates":3,"article-\u002Ffr\u002Fdata\u002Fmarketing-mix-modeling-robyn-setup":13},{"i18nKey":4,"paths":5},"data-005-2026-08",{"de":6,"en":7,"es":8,"fr":9,"it":10,"ru":11,"tr":12},"\u002Fde\u002Fdata\u002Frobyn-praktik-kurulum-marketing-mix-modeling","\u002Fen\u002Fdata\u002Fmarketing-mix-modeling-robyn-practical-setup","\u002Fes\u002Fdata\u002Fmarketing-mix-modeling-robyn-configuracion-practica","\u002Ffr\u002Fdata\u002Fmarketing-mix-modeling-robyn-setup","\u002Fit\u002Fdata\u002Fmarketing-mix-modeling-robyn-setup","\u002Fru\u002Fdata\u002Frobyn-marketing-mix-modeling-praktika","\u002Ftr\u002Fdata\u002Fmarketing-mix-modeling-robyn-ile-pratik-kurulum",{"_path":9,"_dir":14,"_draft":15,"_partial":15,"_locale":16,"title":17,"description":18,"publishedAt":19,"modifiedAt":19,"category":14,"i18nKey":4,"tags":20,"readingTime":26,"author":27,"body":28,"_type":1247,"_id":1248,"_source":1249,"_file":1250,"_stem":1251,"_extension":1252},"data",false,"","Marketing Mix Modeling: Configuration Pratique avec Robyn","Déployez l'outil MMM open-source de Meta, Robyn, en production : courbes de saturation, adstock decay et validation holdout dans vos pipelines data.","2026-08-09",[21,22,23,24,25],"marketing-mix-modeling","robyn","adstock","attribution","data-science",9,"Roibase",{"type":29,"children":30,"toc":1240},"root",[31,39,46,60,104,196,216,237,243,248,256,307,312,366,399,419,425,430,498,509,522,553,564,570,586,637,1021,1034,1039,1165,1185,1190,1196,1201,1215,1220,1225,1229,1234],{"type":32,"tag":33,"props":34,"children":35},"element","p",{},[36],{"type":37,"value":38},"text","Le Marketing Mix Modeling (MMM) est revenu au premier plan fin 2020 avec l'effondrement de l'attribution basée sur les cookies. Mais passer des articles académiques à l'environnement production, c'est un autre niveau. Robyn, qu'Meta a rendue open-source en 2021, ancre cette transition dans la discipline d'ingénierie : courbes de saturation, adstock decay et validation holdout — des concepts statistiques que les praticiens doivent transformer d'un script R en pipeline opérationnel. Cet article démontre comment déployer les trois mécanismes qui constituent le cœur de Robyn — l'affaiblissement de l'impact publicitaire dans le temps, la relation dépenses-revenus atteignant la saturation, et le processus holdout qui teste la puissance prédictive du modèle — en setup production.",{"type":32,"tag":40,"props":41,"children":43},"h2",{"id":42},"adstock-decay-étaler-limpact-publicitaire-dans-le-temps",[44],{"type":37,"value":45},"Adstock Decay : Étaler l'Impact Publicitaire dans le Temps",{"type":32,"tag":33,"props":47,"children":48},{},[49,51,58],{"type":37,"value":50},"Un spot TV diffusé un jour ne génère pas de ventes ce jour-là ; son impact s'étend sur une semaine. Une annonce de recherche cliquée à la seconde peut convertir instantanément, mais le recall de marque déclenche une conversion trois jours plus tard. Le terme « adstock » désigne cette structure mathématique qui modélise ce délai temporel. Robyn propose deux types d'adstock : geometric et Weibull. Geometric implique une décroissance exponentielle simple ; chaque jour, l'effet du jour précédent est multiplié par un paramètre ",{"type":32,"tag":52,"props":53,"children":55},"code",{"className":54},[],[56],{"type":37,"value":57},"theta",{"type":37,"value":59},". Weibull est plus flexible — il permet de contrôler indépendamment les courbes de montée et de descente de l'impact.",{"type":32,"tag":33,"props":61,"children":62},{},[63,65,71,73,79,81,87,89,95,97,102],{"type":37,"value":64},"En setup pratique, vous calibrez les paramètres d'adstock par type de canal. Paid search généralement ",{"type":32,"tag":52,"props":66,"children":68},{"className":67},[],[69],{"type":37,"value":70},"theta=0.3",{"type":37,"value":72}," (décroissance rapide), TV ",{"type":32,"tag":52,"props":74,"children":76},{"className":75},[],[77],{"type":37,"value":78},"theta=0.7",{"type":37,"value":80}," (longue traîne), display autour de ",{"type":32,"tag":52,"props":82,"children":84},{"className":83},[],[85],{"type":37,"value":86},"theta=0.5",{"type":37,"value":88},". Ces valeurs ne sont pas arbitraires — elles sont découvertes via recherche d'hyperparamètres sur un ensemble holdout de périodes passées. Dans la fonction ",{"type":32,"tag":52,"props":90,"children":92},{"className":91},[],[93],{"type":37,"value":94},"robyn_inputs()",{"type":37,"value":96}," de Robyn, vous définissez l'argument ",{"type":32,"tag":52,"props":98,"children":100},{"className":99},[],[101],{"type":37,"value":23},{"type":37,"value":103}," par canal :",{"type":32,"tag":105,"props":106,"children":110},"pre",{"code":107,"language":108,"meta":16,"className":109,"style":16},"InputCollect \u003C- robyn_inputs(\n  dt_input = dt_simulated_weekly,\n  adstock = \"geometric\",\n  adstock_params = list(\n    tv_s = c(0.3, 0.8),\n    search_clicks_p = c(0.0, 0.3),\n    facebook_i = c(0.0, 0.5)\n  )\n)\n","r","language-r shiki shiki-themes github-dark",[111],{"type":32,"tag":52,"props":112,"children":113},{"__ignoreMap":16},[114,125,134,143,152,161,170,179,188],{"type":32,"tag":115,"props":116,"children":119},"span",{"class":117,"line":118},"line",1,[120],{"type":32,"tag":115,"props":121,"children":122},{},[123],{"type":37,"value":124},"InputCollect \u003C- robyn_inputs(\n",{"type":32,"tag":115,"props":126,"children":128},{"class":117,"line":127},2,[129],{"type":32,"tag":115,"props":130,"children":131},{},[132],{"type":37,"value":133},"  dt_input = dt_simulated_weekly,\n",{"type":32,"tag":115,"props":135,"children":137},{"class":117,"line":136},3,[138],{"type":32,"tag":115,"props":139,"children":140},{},[141],{"type":37,"value":142},"  adstock = \"geometric\",\n",{"type":32,"tag":115,"props":144,"children":146},{"class":117,"line":145},4,[147],{"type":32,"tag":115,"props":148,"children":149},{},[150],{"type":37,"value":151},"  adstock_params = list(\n",{"type":32,"tag":115,"props":153,"children":155},{"class":117,"line":154},5,[156],{"type":32,"tag":115,"props":157,"children":158},{},[159],{"type":37,"value":160},"    tv_s = c(0.3, 0.8),\n",{"type":32,"tag":115,"props":162,"children":164},{"class":117,"line":163},6,[165],{"type":32,"tag":115,"props":166,"children":167},{},[168],{"type":37,"value":169},"    search_clicks_p = c(0.0, 0.3),\n",{"type":32,"tag":115,"props":171,"children":173},{"class":117,"line":172},7,[174],{"type":32,"tag":115,"props":175,"children":176},{},[177],{"type":37,"value":178},"    facebook_i = c(0.0, 0.5)\n",{"type":32,"tag":115,"props":180,"children":182},{"class":117,"line":181},8,[183],{"type":32,"tag":115,"props":184,"children":185},{},[186],{"type":37,"value":187},"  )\n",{"type":32,"tag":115,"props":189,"children":190},{"class":117,"line":26},[191],{"type":32,"tag":115,"props":192,"children":193},{},[194],{"type":37,"value":195},")\n",{"type":32,"tag":33,"props":197,"children":198},{},[199,201,207,209,214],{"type":37,"value":200},"Ici, ",{"type":32,"tag":52,"props":202,"children":204},{"className":203},[],[205],{"type":37,"value":206},"c(min, max)",{"type":37,"value":208}," définit une plage ; l'algorithme d'optimisation Nevergrad explore cette plage pour trouver la meilleure valeur ",{"type":32,"tag":52,"props":210,"children":212},{"className":211},[],[213],{"type":37,"value":57},{"type":37,"value":215},". Si vous utilisez Weibull au lieu de geometric, les paramètres shape et scale s'ajoutent. L'avantage de Weibull est une meilleure adaptation aux canaux comme display, où l'impact « atteint un pic tard » — l'effet est faible les deux premiers jours, culmine entre les jours 3-5.",{"type":32,"tag":33,"props":217,"children":218},{},[219,221,227,229,235],{"type":37,"value":220},"Une mauvaise configuration de l'adstock conduit le modèle à maldistribuer les contributions entre canaux. Par exemple, si vous modélisez TV avec geometric ",{"type":32,"tag":52,"props":222,"children":224},{"className":223},[],[225],{"type":37,"value":226},"theta=0.1",{"type":37,"value":228},", seul le jour de diffusion se voit attribuer un impact, et le trafic organique sur une semaine est manqué. Inversement, affecter search ",{"type":32,"tag":52,"props":230,"children":232},{"className":231},[],[233],{"type":37,"value":234},"theta=0.9",{"type":37,"value":236}," signifie imputer les ventes d'aujourd'hui à un clic d'il y a une semaine — illogique. C'est pourquoi la configuration d'adstock doit refléter la caractéristique du canal et être bornée par la connaissance métier.",{"type":32,"tag":40,"props":238,"children":240},{"id":239},"courbe-de-saturation-relation-dépenses-revenus-atteignant-le-plateau",[241],{"type":37,"value":242},"Courbe de Saturation : Relation Dépenses-Revenus Atteignant le Plateau",{"type":32,"tag":33,"props":244,"children":245},{},[246],{"type":37,"value":247},"La régression linéaire suppose que chaque euro dépensé génère le même rendement. En réalité, sur les premiers 10 000 € le ROAS est 8, à 100 000 € il chute à 3, à 1 million € il s'effondre en dessous de 1 — le rendement marginal décroît. La saturation est la transformation qui modélise cette courbe. Le type de saturation le plus courant dans Robyn est l'équation de Hill (Michaelis-Menten) :",{"type":32,"tag":105,"props":249,"children":251},{"code":250},"y = Vmax * (x^S) \u002F (K^S + x^S)\n",[252],{"type":32,"tag":52,"props":253,"children":254},{"__ignoreMap":16},[255],{"type":37,"value":250},{"type":32,"tag":33,"props":257,"children":258},{},[259,261,267,269,275,277,283,285,290,292,297,299,305],{"type":37,"value":260},"Où ",{"type":32,"tag":52,"props":262,"children":264},{"className":263},[],[265],{"type":37,"value":266},"Vmax",{"type":37,"value":268}," est l'effet maximal, ",{"type":32,"tag":52,"props":270,"children":272},{"className":271},[],[273],{"type":37,"value":274},"K",{"type":37,"value":276}," le niveau de dépenses auquel la saturation atteint la moitié du maximum (point d'inflexion), et ",{"type":32,"tag":52,"props":278,"children":280},{"className":279},[],[281],{"type":37,"value":282},"S",{"type":37,"value":284}," la pente de la courbe (shape). ",{"type":32,"tag":52,"props":286,"children":288},{"className":287},[],[289],{"type":37,"value":274},{"type":37,"value":291}," bas signifie saturation rapide du canal ; ",{"type":32,"tag":52,"props":293,"children":295},{"className":294},[],[296],{"type":37,"value":274},{"type":37,"value":298}," haut signifie saturation tardive. Quand ",{"type":32,"tag":52,"props":300,"children":302},{"className":301},[],[303],{"type":37,"value":304},"S>1",{"type":37,"value":306},", la courbe prend une forme en S — début lent, milieu rapide, fin lent.",{"type":32,"tag":33,"props":308,"children":309},{},[310],{"type":37,"value":311},"Dans Robyn, vous définissez les paramètres de Hill aussi par canal :",{"type":32,"tag":105,"props":313,"children":315},{"code":314,"language":108,"meta":16,"className":109,"style":16},"hyperparameters \u003C- list(\n  tv_s_alphas = c(0.5, 3),\n  tv_s_gammas = c(0.3, 1),\n  search_clicks_p_alphas = c(0.5, 3),\n  search_clicks_p_gammas = c(0.3, 1)\n)\n",[316],{"type":32,"tag":52,"props":317,"children":318},{"__ignoreMap":16},[319,327,335,343,351,359],{"type":32,"tag":115,"props":320,"children":321},{"class":117,"line":118},[322],{"type":32,"tag":115,"props":323,"children":324},{},[325],{"type":37,"value":326},"hyperparameters \u003C- list(\n",{"type":32,"tag":115,"props":328,"children":329},{"class":117,"line":127},[330],{"type":32,"tag":115,"props":331,"children":332},{},[333],{"type":37,"value":334},"  tv_s_alphas = c(0.5, 3),\n",{"type":32,"tag":115,"props":336,"children":337},{"class":117,"line":136},[338],{"type":32,"tag":115,"props":339,"children":340},{},[341],{"type":37,"value":342},"  tv_s_gammas = c(0.3, 1),\n",{"type":32,"tag":115,"props":344,"children":345},{"class":117,"line":145},[346],{"type":32,"tag":115,"props":347,"children":348},{},[349],{"type":37,"value":350},"  search_clicks_p_alphas = c(0.5, 3),\n",{"type":32,"tag":115,"props":352,"children":353},{"class":117,"line":154},[354],{"type":32,"tag":115,"props":355,"children":356},{},[357],{"type":37,"value":358},"  search_clicks_p_gammas = c(0.3, 1)\n",{"type":32,"tag":115,"props":360,"children":361},{"class":117,"line":163},[362],{"type":32,"tag":115,"props":363,"children":364},{},[365],{"type":37,"value":195},{"type":32,"tag":33,"props":367,"children":368},{},[369,375,377,382,384,390,392,397],{"type":32,"tag":52,"props":370,"children":372},{"className":371},[],[373],{"type":37,"value":374},"alphas",{"type":37,"value":376}," correspond au paramètre ",{"type":32,"tag":52,"props":378,"children":380},{"className":379},[],[381],{"type":37,"value":282},{"type":37,"value":383}," de Hill, ",{"type":32,"tag":52,"props":385,"children":387},{"className":386},[],[388],{"type":37,"value":389},"gammas",{"type":37,"value":391}," au paramètre ",{"type":32,"tag":52,"props":393,"children":395},{"className":394},[],[396],{"type":37,"value":274},{"type":37,"value":398}," (notation de Robyn). L'optimisation cherche le meilleur fit dans ces plages. Mais ne laissez pas la recherche aveugle — si vous dépensez déjà 80 % de votre budget TV, la saturation doit être >90 %, sinon le modèle génère un ROAS marginal irréaliste.",{"type":32,"tag":33,"props":400,"children":401},{},[402,404,410,412,417],{"type":37,"value":403},"La configuration de saturation impacte directement votre stratégie d'allocation de budget. Si le modèle trace correctement la courbe de saturation, vous pouvez calculer le ROAS marginal de chaque canal et redéployer le budget. La fonction ",{"type":32,"tag":52,"props":405,"children":407},{"className":406},[],[408],{"type":37,"value":409},"robyn_allocator()",{"type":37,"value":411}," de Robyn le fait — avec un budget total fixe, quel canal réduire et quel canal augmenter maximise les ventes ? Mais cette recommandation n'est valable que si les paramètres de saturation sont corrects. Une mauvaise valeur ",{"type":32,"tag":52,"props":413,"children":415},{"className":414},[],[416],{"type":37,"value":274},{"type":37,"value":418}," équivaut à des millions d'euros de décision erronée.",{"type":32,"tag":40,"props":420,"children":422},{"id":421},"validation-holdout-tester-la-capacité-prédictive-du-modèle",[423],{"type":37,"value":424},"Validation Holdout : Tester la Capacité Prédictive du Modèle",{"type":32,"tag":33,"props":426,"children":427},{},[428],{"type":37,"value":429},"Le plus grand risque du MMM est l'overfitting — le modèle mémorise les données historiques au lieu de généraliser. Pour contrer cela, une validation holdout en série chronologique est nécessaire. En setup Robyn, vous écartez les 4-8 dernières semaines comme ensemble holdout, le modèle est entraîné sur le reste, puis fait des prédictions sur la période holdout. NRMSE (Normalized Root Mean Square Error) et MAPE (Mean Absolute Percentage Error) bas signifient que le modèle généralise bien.",{"type":32,"tag":105,"props":431,"children":433},{"code":432,"language":108,"meta":16,"className":109,"style":16},"InputCollect \u003C- robyn_inputs(\n  dt_input = dt_simulated_weekly,\n  window_start = \"2022-01-01\",\n  window_end = \"2023-10-31\",\n  rollingWindowStartWhich = 1,\n  rollingWindowEndWhich = 52,\n  rollingWindowLength = 4\n)\n",[434],{"type":32,"tag":52,"props":435,"children":436},{"__ignoreMap":16},[437,444,451,459,467,475,483,491],{"type":32,"tag":115,"props":438,"children":439},{"class":117,"line":118},[440],{"type":32,"tag":115,"props":441,"children":442},{},[443],{"type":37,"value":124},{"type":32,"tag":115,"props":445,"children":446},{"class":117,"line":127},[447],{"type":32,"tag":115,"props":448,"children":449},{},[450],{"type":37,"value":133},{"type":32,"tag":115,"props":452,"children":453},{"class":117,"line":136},[454],{"type":32,"tag":115,"props":455,"children":456},{},[457],{"type":37,"value":458},"  window_start = \"2022-01-01\",\n",{"type":32,"tag":115,"props":460,"children":461},{"class":117,"line":145},[462],{"type":32,"tag":115,"props":463,"children":464},{},[465],{"type":37,"value":466},"  window_end = \"2023-10-31\",\n",{"type":32,"tag":115,"props":468,"children":469},{"class":117,"line":154},[470],{"type":32,"tag":115,"props":471,"children":472},{},[473],{"type":37,"value":474},"  rollingWindowStartWhich = 1,\n",{"type":32,"tag":115,"props":476,"children":477},{"class":117,"line":163},[478],{"type":32,"tag":115,"props":479,"children":480},{},[481],{"type":37,"value":482},"  rollingWindowEndWhich = 52,\n",{"type":32,"tag":115,"props":484,"children":485},{"class":117,"line":172},[486],{"type":32,"tag":115,"props":487,"children":488},{},[489],{"type":37,"value":490},"  rollingWindowLength = 4\n",{"type":32,"tag":115,"props":492,"children":493},{"class":117,"line":181},[494],{"type":32,"tag":115,"props":495,"children":496},{},[497],{"type":37,"value":195},{"type":32,"tag":33,"props":499,"children":500},{},[501,507],{"type":32,"tag":52,"props":502,"children":504},{"className":503},[],[505],{"type":37,"value":506},"rollingWindowLength = 4",{"type":37,"value":508}," place les 4 dernières semaines en holdout. Le modèle est entraîné sans les voir, puis en produit des prédictions. Dans la sortie de Robyn, chaque modèle affiche son NRMSE holdout — \u003C10 % c'est bon, >20 % c'est suspect. Mais ne décidez pas sur une seule métrique ; vérifiez les anomalies pendant la période holdout (campagne, congés). Par exemple, si Black Friday tombe pendant le holdout, le modèle underestimate, car ce pattern de pic n'existe pas dans la demande normale.",{"type":32,"tag":33,"props":510,"children":511},{},[512,514,520],{"type":37,"value":513},"Après validation holdout, ré-entraîner le modèle est pratique courante — vous filez un fit final sur toutes les données, mais choisissez les hyperparamètres selon les résultats holdout. Cette boucle « train-valide-finalize ». Dans Robyn, vous utilisez ",{"type":32,"tag":52,"props":515,"children":517},{"className":516},[],[518],{"type":37,"value":519},"robyn_refresh()",{"type":37,"value":521}," :",{"type":32,"tag":105,"props":523,"children":525},{"code":524,"language":108,"meta":16,"className":109,"style":16},"Robyn1 \u003C- robyn_run(InputCollect = InputCollect, plot_folder = OutputCollect$plot_folder)\nOutputCollect \u003C- robyn_outputs(Robyn1, select_model = \"1_100_3\")\nRobynRefresh \u003C- robyn_refresh(Robyn1, dt_input = dt_simulated_weekly, refresh_steps = 4)\n",[526],{"type":32,"tag":52,"props":527,"children":528},{"__ignoreMap":16},[529,537,545],{"type":32,"tag":115,"props":530,"children":531},{"class":117,"line":118},[532],{"type":32,"tag":115,"props":533,"children":534},{},[535],{"type":37,"value":536},"Robyn1 \u003C- robyn_run(InputCollect = InputCollect, plot_folder = OutputCollect$plot_folder)\n",{"type":32,"tag":115,"props":538,"children":539},{"class":117,"line":127},[540],{"type":32,"tag":115,"props":541,"children":542},{},[543],{"type":37,"value":544},"OutputCollect \u003C- robyn_outputs(Robyn1, select_model = \"1_100_3\")\n",{"type":32,"tag":115,"props":546,"children":547},{"class":117,"line":136},[548],{"type":32,"tag":115,"props":549,"children":550},{},[551],{"type":37,"value":552},"RobynRefresh \u003C- robyn_refresh(Robyn1, dt_input = dt_simulated_weekly, refresh_steps = 4)\n",{"type":32,"tag":33,"props":554,"children":555},{},[556,562],{"type":32,"tag":52,"props":557,"children":559},{"className":558},[],[560],{"type":37,"value":561},"refresh_steps = 4",{"type":37,"value":563}," met à jour le modèle avec les 4 dernières semaines de données neuves mais conserve les paramètres de saturation\u002Fadstock fixes (calibrage préservé). C'est la base d'un pipeline s'exécutant en continu en production — chaque semaine, vous ajoutez une ligne, le modèle re-fit, le dashboard se met à jour.",{"type":32,"tag":40,"props":565,"children":567},{"id":566},"porter-le-pipeline-robyn-en-production",[568],{"type":37,"value":569},"Porter le Pipeline Robyn en Production",{"type":32,"tag":33,"props":571,"children":572},{},[573,575,584],{"type":37,"value":574},"Un script R Robyn ne se pose pas et s'oublie ; c'est un composant d'un pipeline data production. Une architecture typique : table des dépenses marketing dans BigQuery + table des conversions GA4 + table de revenu CRM → agrégation hebdomadaire avec dbt → DAG Airflow qui déclenche le script R Robyn → résultat JSON sur un dashboard Looker Studio. Cette stack tourne dans une ",{"type":32,"tag":576,"props":577,"children":581},"a",{"href":578,"rel":579},"https:\u002F\u002Fwww.roibase.com.tr\u002Ffr\u002Ffirstparty",[580],"nofollow",[582],{"type":37,"value":583},"architecture data first-party",{"type":37,"value":585},".",{"type":32,"tag":33,"props":587,"children":588},{},[589,591,597,599,605,607,613,615,621,622,628,629,635],{"type":37,"value":590},"Première étape : normaliser le schéma de données. Robyn s'attend à une table ",{"type":32,"tag":52,"props":592,"children":594},{"className":593},[],[595],{"type":37,"value":596},"dt_input",{"type":37,"value":598}," : ",{"type":32,"tag":52,"props":600,"children":602},{"className":601},[],[603],{"type":37,"value":604},"DATE",{"type":37,"value":606}," (hebdomadaire), ",{"type":32,"tag":52,"props":608,"children":610},{"className":609},[],[611],{"type":37,"value":612},"revenue",{"type":37,"value":614},", ",{"type":32,"tag":52,"props":616,"children":618},{"className":617},[],[619],{"type":37,"value":620},"tv_spend",{"type":37,"value":614},{"type":32,"tag":52,"props":623,"children":625},{"className":624},[],[626],{"type":37,"value":627},"search_spend",{"type":37,"value":614},{"type":32,"tag":52,"props":630,"children":632},{"className":631},[],[633],{"type":37,"value":634},"facebook_impressions",{"type":37,"value":636}," — colonnes séparées par canal. Distinction organic\u002Fpaid requise, sinon le modèle ne peut pas faire d'attribution. Les semaines manquantes doivent être imputées (zéro ou interpolation), les outliers flaggés. Exemple de modèle dbt :",{"type":32,"tag":105,"props":638,"children":642},{"code":639,"language":640,"meta":16,"className":641,"style":16},"with base as (\n  select\n    date_trunc(event_date, week) as week_start,\n    sum(case when source = 'google\u002Fcpc' then cost else 0 end) as search_spend,\n    sum(case when source = 'facebook' then cost else 0 end) as facebook_spend,\n    count(distinct case when event_name = 'purchase' then user_pseudo_id end) as conversions\n  from `project.analytics_123456789.events_*`\n  where _table_suffix between '20220101' and '20231231'\n  group by 1\n)\nselect * from base\norder by week_start\n","sql","language-sql shiki shiki-themes github-dark",[643],{"type":32,"tag":52,"props":644,"children":645},{"__ignoreMap":16},[646,671,679,706,784,849,916,929,962,975,983,1007],{"type":32,"tag":115,"props":647,"children":648},{"class":117,"line":118},[649,655,661,666],{"type":32,"tag":115,"props":650,"children":652},{"style":651},"--shiki-default:#F97583",[653],{"type":37,"value":654},"with",{"type":32,"tag":115,"props":656,"children":658},{"style":657},"--shiki-default:#E1E4E8",[659],{"type":37,"value":660}," base ",{"type":32,"tag":115,"props":662,"children":663},{"style":651},[664],{"type":37,"value":665},"as",{"type":32,"tag":115,"props":667,"children":668},{"style":657},[669],{"type":37,"value":670}," (\n",{"type":32,"tag":115,"props":672,"children":673},{"class":117,"line":127},[674],{"type":32,"tag":115,"props":675,"children":676},{"style":651},[677],{"type":37,"value":678},"  select\n",{"type":32,"tag":115,"props":680,"children":681},{"class":117,"line":136},[682,687,692,697,701],{"type":32,"tag":115,"props":683,"children":684},{"style":657},[685],{"type":37,"value":686},"    date_trunc(event_date, ",{"type":32,"tag":115,"props":688,"children":689},{"style":651},[690],{"type":37,"value":691},"week",{"type":32,"tag":115,"props":693,"children":694},{"style":657},[695],{"type":37,"value":696},") ",{"type":32,"tag":115,"props":698,"children":699},{"style":651},[700],{"type":37,"value":665},{"type":32,"tag":115,"props":702,"children":703},{"style":657},[704],{"type":37,"value":705}," week_start,\n",{"type":32,"tag":115,"props":707,"children":708},{"class":117,"line":145},[709,715,720,725,730,735,740,746,751,756,761,766,771,775,779],{"type":32,"tag":115,"props":710,"children":712},{"style":711},"--shiki-default:#79B8FF",[713],{"type":37,"value":714},"    sum",{"type":32,"tag":115,"props":716,"children":717},{"style":657},[718],{"type":37,"value":719},"(",{"type":32,"tag":115,"props":721,"children":722},{"style":651},[723],{"type":37,"value":724},"case",{"type":32,"tag":115,"props":726,"children":727},{"style":651},[728],{"type":37,"value":729}," when",{"type":32,"tag":115,"props":731,"children":732},{"style":657},[733],{"type":37,"value":734}," source ",{"type":32,"tag":115,"props":736,"children":737},{"style":651},[738],{"type":37,"value":739},"=",{"type":32,"tag":115,"props":741,"children":743},{"style":742},"--shiki-default:#9ECBFF",[744],{"type":37,"value":745}," 'google\u002Fcpc'",{"type":32,"tag":115,"props":747,"children":748},{"style":651},[749],{"type":37,"value":750}," then",{"type":32,"tag":115,"props":752,"children":753},{"style":657},[754],{"type":37,"value":755}," cost ",{"type":32,"tag":115,"props":757,"children":758},{"style":651},[759],{"type":37,"value":760},"else",{"type":32,"tag":115,"props":762,"children":763},{"style":711},[764],{"type":37,"value":765}," 0",{"type":32,"tag":115,"props":767,"children":768},{"style":651},[769],{"type":37,"value":770}," end",{"type":32,"tag":115,"props":772,"children":773},{"style":657},[774],{"type":37,"value":696},{"type":32,"tag":115,"props":776,"children":777},{"style":651},[778],{"type":37,"value":665},{"type":32,"tag":115,"props":780,"children":781},{"style":657},[782],{"type":37,"value":783}," search_spend,\n",{"type":32,"tag":115,"props":785,"children":786},{"class":117,"line":154},[787,791,795,799,803,807,811,816,820,824,828,832,836,840,844],{"type":32,"tag":115,"props":788,"children":789},{"style":711},[790],{"type":37,"value":714},{"type":32,"tag":115,"props":792,"children":793},{"style":657},[794],{"type":37,"value":719},{"type":32,"tag":115,"props":796,"children":797},{"style":651},[798],{"type":37,"value":724},{"type":32,"tag":115,"props":800,"children":801},{"style":651},[802],{"type":37,"value":729},{"type":32,"tag":115,"props":804,"children":805},{"style":657},[806],{"type":37,"value":734},{"type":32,"tag":115,"props":808,"children":809},{"style":651},[810],{"type":37,"value":739},{"type":32,"tag":115,"props":812,"children":813},{"style":742},[814],{"type":37,"value":815}," 'facebook'",{"type":32,"tag":115,"props":817,"children":818},{"style":651},[819],{"type":37,"value":750},{"type":32,"tag":115,"props":821,"children":822},{"style":657},[823],{"type":37,"value":755},{"type":32,"tag":115,"props":825,"children":826},{"style":651},[827],{"type":37,"value":760},{"type":32,"tag":115,"props":829,"children":830},{"style":711},[831],{"type":37,"value":765},{"type":32,"tag":115,"props":833,"children":834},{"style":651},[835],{"type":37,"value":770},{"type":32,"tag":115,"props":837,"children":838},{"style":657},[839],{"type":37,"value":696},{"type":32,"tag":115,"props":841,"children":842},{"style":651},[843],{"type":37,"value":665},{"type":32,"tag":115,"props":845,"children":846},{"style":657},[847],{"type":37,"value":848}," facebook_spend,\n",{"type":32,"tag":115,"props":850,"children":851},{"class":117,"line":163},[852,857,861,866,871,875,880,884,889,893,898,903,907,911],{"type":32,"tag":115,"props":853,"children":854},{"style":711},[855],{"type":37,"value":856},"    count",{"type":32,"tag":115,"props":858,"children":859},{"style":657},[860],{"type":37,"value":719},{"type":32,"tag":115,"props":862,"children":863},{"style":651},[864],{"type":37,"value":865},"distinct",{"type":32,"tag":115,"props":867,"children":868},{"style":651},[869],{"type":37,"value":870}," case",{"type":32,"tag":115,"props":872,"children":873},{"style":651},[874],{"type":37,"value":729},{"type":32,"tag":115,"props":876,"children":877},{"style":657},[878],{"type":37,"value":879}," event_name ",{"type":32,"tag":115,"props":881,"children":882},{"style":651},[883],{"type":37,"value":739},{"type":32,"tag":115,"props":885,"children":886},{"style":742},[887],{"type":37,"value":888}," 'purchase'",{"type":32,"tag":115,"props":890,"children":891},{"style":651},[892],{"type":37,"value":750},{"type":32,"tag":115,"props":894,"children":895},{"style":657},[896],{"type":37,"value":897}," user_pseudo_id ",{"type":32,"tag":115,"props":899,"children":900},{"style":651},[901],{"type":37,"value":902},"end",{"type":32,"tag":115,"props":904,"children":905},{"style":657},[906],{"type":37,"value":696},{"type":32,"tag":115,"props":908,"children":909},{"style":651},[910],{"type":37,"value":665},{"type":32,"tag":115,"props":912,"children":913},{"style":657},[914],{"type":37,"value":915}," conversions\n",{"type":32,"tag":115,"props":917,"children":918},{"class":117,"line":172},[919,924],{"type":32,"tag":115,"props":920,"children":921},{"style":651},[922],{"type":37,"value":923},"  from",{"type":32,"tag":115,"props":925,"children":926},{"style":742},[927],{"type":37,"value":928}," `project.analytics_123456789.events_*`\n",{"type":32,"tag":115,"props":930,"children":931},{"class":117,"line":181},[932,937,942,947,952,957],{"type":32,"tag":115,"props":933,"children":934},{"style":651},[935],{"type":37,"value":936},"  where",{"type":32,"tag":115,"props":938,"children":939},{"style":657},[940],{"type":37,"value":941}," _table_suffix ",{"type":32,"tag":115,"props":943,"children":944},{"style":651},[945],{"type":37,"value":946},"between",{"type":32,"tag":115,"props":948,"children":949},{"style":742},[950],{"type":37,"value":951}," '20220101'",{"type":32,"tag":115,"props":953,"children":954},{"style":651},[955],{"type":37,"value":956}," and",{"type":32,"tag":115,"props":958,"children":959},{"style":742},[960],{"type":37,"value":961}," '20231231'\n",{"type":32,"tag":115,"props":963,"children":964},{"class":117,"line":26},[965,970],{"type":32,"tag":115,"props":966,"children":967},{"style":651},[968],{"type":37,"value":969},"  group by",{"type":32,"tag":115,"props":971,"children":972},{"style":711},[973],{"type":37,"value":974}," 1\n",{"type":32,"tag":115,"props":976,"children":978},{"class":117,"line":977},10,[979],{"type":32,"tag":115,"props":980,"children":981},{"style":657},[982],{"type":37,"value":195},{"type":32,"tag":115,"props":984,"children":986},{"class":117,"line":985},11,[987,992,997,1002],{"type":32,"tag":115,"props":988,"children":989},{"style":651},[990],{"type":37,"value":991},"select",{"type":32,"tag":115,"props":993,"children":994},{"style":651},[995],{"type":37,"value":996}," *",{"type":32,"tag":115,"props":998,"children":999},{"style":651},[1000],{"type":37,"value":1001}," from",{"type":32,"tag":115,"props":1003,"children":1004},{"style":657},[1005],{"type":37,"value":1006}," base\n",{"type":32,"tag":115,"props":1008,"children":1010},{"class":117,"line":1009},12,[1011,1016],{"type":32,"tag":115,"props":1012,"children":1013},{"style":651},[1014],{"type":37,"value":1015},"order by",{"type":32,"tag":115,"props":1017,"children":1018},{"style":657},[1019],{"type":37,"value":1020}," week_start\n",{"type":32,"tag":33,"props":1022,"children":1023},{},[1024,1026,1032],{"type":37,"value":1025},"Cette table est exportée de BigQuery en CSV et alimentée au script Robyn, ou directement récupérée via le package R ",{"type":32,"tag":52,"props":1027,"children":1029},{"className":1028},[],[1030],{"type":37,"value":1031},"bigrquery",{"type":37,"value":1033},". Cette dernière est préférable — garantie de fraîcheur des données.",{"type":32,"tag":33,"props":1035,"children":1036},{},[1037],{"type":37,"value":1038},"Un étage du DAG Airflow pour Robyn :",{"type":32,"tag":105,"props":1040,"children":1044},{"code":1041,"language":1042,"meta":16,"className":1043,"style":16},"from airflow.operators.bash import BashOperator\n\nrun_robyn = BashOperator(\n    task_id='run_robyn_mmm',\n    bash_command='Rscript \u002Fpath\u002Fto\u002Frobyn_model.R ',\n    dag=dag\n)\n","python","language-python shiki shiki-themes github-dark",[1045],{"type":32,"tag":52,"props":1046,"children":1047},{"__ignoreMap":16},[1048,1071,1080,1097,1120,1141,1158],{"type":32,"tag":115,"props":1049,"children":1050},{"class":117,"line":118},[1051,1056,1061,1066],{"type":32,"tag":115,"props":1052,"children":1053},{"style":651},[1054],{"type":37,"value":1055},"from",{"type":32,"tag":115,"props":1057,"children":1058},{"style":657},[1059],{"type":37,"value":1060}," airflow.operators.bash ",{"type":32,"tag":115,"props":1062,"children":1063},{"style":651},[1064],{"type":37,"value":1065},"import",{"type":32,"tag":115,"props":1067,"children":1068},{"style":657},[1069],{"type":37,"value":1070}," BashOperator\n",{"type":32,"tag":115,"props":1072,"children":1073},{"class":117,"line":127},[1074],{"type":32,"tag":115,"props":1075,"children":1077},{"emptyLinePlaceholder":1076},true,[1078],{"type":37,"value":1079},"\n",{"type":32,"tag":115,"props":1081,"children":1082},{"class":117,"line":136},[1083,1088,1092],{"type":32,"tag":115,"props":1084,"children":1085},{"style":657},[1086],{"type":37,"value":1087},"run_robyn ",{"type":32,"tag":115,"props":1089,"children":1090},{"style":651},[1091],{"type":37,"value":739},{"type":32,"tag":115,"props":1093,"children":1094},{"style":657},[1095],{"type":37,"value":1096}," BashOperator(\n",{"type":32,"tag":115,"props":1098,"children":1099},{"class":117,"line":145},[1100,1106,1110,1115],{"type":32,"tag":115,"props":1101,"children":1103},{"style":1102},"--shiki-default:#FFAB70",[1104],{"type":37,"value":1105},"    task_id",{"type":32,"tag":115,"props":1107,"children":1108},{"style":651},[1109],{"type":37,"value":739},{"type":32,"tag":115,"props":1111,"children":1112},{"style":742},[1113],{"type":37,"value":1114},"'run_robyn_mmm'",{"type":32,"tag":115,"props":1116,"children":1117},{"style":657},[1118],{"type":37,"value":1119},",\n",{"type":32,"tag":115,"props":1121,"children":1122},{"class":117,"line":154},[1123,1128,1132,1137],{"type":32,"tag":115,"props":1124,"children":1125},{"style":1102},[1126],{"type":37,"value":1127},"    bash_command",{"type":32,"tag":115,"props":1129,"children":1130},{"style":651},[1131],{"type":37,"value":739},{"type":32,"tag":115,"props":1133,"children":1134},{"style":742},[1135],{"type":37,"value":1136},"'Rscript \u002Fpath\u002Fto\u002Frobyn_model.R '",{"type":32,"tag":115,"props":1138,"children":1139},{"style":657},[1140],{"type":37,"value":1119},{"type":32,"tag":115,"props":1142,"children":1143},{"class":117,"line":163},[1144,1149,1153],{"type":32,"tag":115,"props":1145,"children":1146},{"style":1102},[1147],{"type":37,"value":1148},"    dag",{"type":32,"tag":115,"props":1150,"children":1151},{"style":651},[1152],{"type":37,"value":739},{"type":32,"tag":115,"props":1154,"children":1155},{"style":657},[1156],{"type":37,"value":1157},"dag\n",{"type":32,"tag":115,"props":1159,"children":1160},{"class":117,"line":172},[1161],{"type":32,"tag":115,"props":1162,"children":1163},{"style":657},[1164],{"type":37,"value":195},{"type":32,"tag":33,"props":1166,"children":1167},{},[1168,1170,1176,1178,1183],{"type":37,"value":1169},"À l'intérieur du script, vous sauvegardez l'objet du modèle au format RDS avec ",{"type":32,"tag":52,"props":1171,"children":1173},{"className":1172},[],[1174],{"type":37,"value":1175},"robyn_save()",{"type":37,"value":1177}," et l'écrivez sur GCS. Les semaines suivantes, vous le chargez pour ",{"type":32,"tag":52,"props":1179,"children":1181},{"className":1180},[],[1182],{"type":37,"value":519},{"type":37,"value":1184},". Ainsi, au lieu d'un re-entraînement complet chaque semaine, une mise à jour incrémentale — le temps de calcul passe de 2 heures à 15 minutes.",{"type":32,"tag":33,"props":1186,"children":1187},{},[1188],{"type":37,"value":1189},"Les métriques holdout sont sauvegardées en JSON, écrites dans BigQuery, et visualisées comme graphique de tendance dans Looker Studio. Un pic dans NRMSE (par exemple de 8 % à 18 %) déclenche une alerte — le modèle s'est dégradé, une recalibration est requise. Sans ce monitoring, MMM échoue silencieusement ; une allocation budgétaire incorrecte passe inaperçue pendant trois mois.",{"type":32,"tag":40,"props":1191,"children":1193},{"id":1192},"relier-la-sortie-du-modèle-au-mécanisme-de-décision",[1194],{"type":37,"value":1195},"Relier la Sortie du Modèle au Mécanisme de Décision",{"type":32,"tag":33,"props":1197,"children":1198},{},[1199],{"type":37,"value":1200},"La sortie de Robyn n'est pas un camembert de contribution par canal, mais un tableau de ROAS marginal. Le dernier euro dépensé par chaque canal génère quel rendement. Avec cela, vous exécutez un optimiseur de budget : si le ROAS marginal de TV est 2 et celui de search 5, shift vers search. Mais cet optimisation mécanique peut entrer en conflit avec la stratégie de marque — si TV tourne pour la brand awareness, fixer le regard sur le ROAS court terme induit une erreur.",{"type":32,"tag":33,"props":1202,"children":1203},{},[1204,1206,1213],{"type":37,"value":1205},"C'est pourquoi les résultats MMM ne doivent pas être un outil isolé de prise de décision, mais synthétisés avec d'autres signaux dans votre couche ",{"type":32,"tag":576,"props":1207,"children":1210},{"href":1208,"rel":1209},"https:\u002F\u002Fwww.roibase.com.tr\u002Ffr\u002Fverianalizi",[580],[1211],{"type":37,"value":1212},"d'analyse de données",{"type":37,"value":1214}," : études de brand lift, tests d'incrementalité, lifetime value des clients. Si Robyn dit contribution 30 % mais un test geo-lift trouve 15 %, vous devez réconcilier — il y a une faille dans les hypothèses du modèle (par exemple, adstock decay trop haut).",{"type":32,"tag":33,"props":1216,"children":1217},{},[1218],{"type":37,"value":1219},"En production, MMM refresh hebdomadaire, mais les décisions budgétaires se prennent mensuellement ou trimestriellement. Le modèle tourne chaque semaine, les métriques entrent en tendance, mais vous observez la moyenne sur 4 semaines. Basculer des millions sur une seule semaine provoque de la volatilité. De plus, la fenêtre holdout est 4 semaines, donc votre cycle de révision budgétaire doit s'aligner dessus.",{"type":32,"tag":33,"props":1221,"children":1222},{},[1223],{"type":37,"value":1224},"Enfin, MMM ne remplace pas l'attribution incrémentale — il la complète. GA4 last-click pour les tactiques court terme, MMM pour la stratégie long terme. Quand vous présentez les deux sur des dashboards séparés au C-level, la question « laquelle est vraie ? » arrive. Réponse : chacune est vraie dans son contexte ; GA4 révèle le parcours utilisateur, MMM l'incrementalité agrégée. Pour les décisions budgétaires, prenez une moyenne pondérée des deux (par exemple 60 % MMM, 40 % GA4). Calibrez cette formule de blend selon la culture de l'entreprise et son niveau de maturité data.",{"type":32,"tag":1226,"props":1227,"children":1228},"hr",{},[],{"type":32,"tag":33,"props":1230,"children":1231},{},[1232],{"type":37,"value":1233},"Le Marketing Mix Modeling n'est plus un exercice académique, c'est un module intégré dans votre pipeline data production. Robyn rend cette transition possible en paramétrant l'adstock, la saturation et le holdout — concepts statistiques transformés en composants versionnables, itérables et automatisables. Mais exécuter le script Robyn une fois et générer un rapport PDF ne suffit pas — vous devez construire une boucle de refresh hebdomadaire, monitoring holdout et allocation budgétaire. Le faire dans la stack BigQuery + dbt + Airflow est idéal ; les sorties MMM alimentent alors un moteur de décision real-time, et quand la performance des canaux change, l'allocation s'ajuste automatiquement. Vous av",{"type":32,"tag":1235,"props":1236,"children":1237},"style",{},[1238],{"type":37,"value":1239},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":16,"searchDepth":136,"depth":136,"links":1241},[1242,1243,1244,1245,1246],{"id":42,"depth":127,"text":45},{"id":239,"depth":127,"text":242},{"id":421,"depth":127,"text":424},{"id":566,"depth":127,"text":569},{"id":1192,"depth":127,"text":1195},"markdown","content:fr:data:marketing-mix-modeling-robyn-setup.md","content","fr\u002Fdata\u002Fmarketing-mix-modeling-robyn-setup.md","fr\u002Fdata\u002Fmarketing-mix-modeling-robyn-setup","md",1786860292113]