{"id":1767,"date":"2026-07-16T17:00:00","date_gmt":"2026-07-16T22:00:00","guid":{"rendered":"https:\/\/tolinku.com\/blog\/?p=1767"},"modified":"2026-03-07T03:50:09","modified_gmt":"2026-03-07T08:50:09","slug":"media-mix-modeling-apps","status":"publish","type":"post","link":"https:\/\/tolinku.com\/blog\/media-mix-modeling-apps\/","title":{"rendered":"Media Mix Modeling for Mobile App Marketing"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Media mix modeling (MMM) is a statistical approach that measures the impact of marketing channels on business outcomes using aggregate data. Unlike user-level attribution, MMM does not track individual users. It analyzes the relationship between marketing spend (by channel, by week) and total outcomes (installs, revenue) to determine which channels drive results.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This guide covers how MMM applies to mobile app marketing. For incrementality testing, see <a href=\"https:\/\/tolinku.com\/blog\/incrementality-testing-mobile\/\">incrementality testing for mobile marketing<\/a>. For attribution models, see <a href=\"https:\/\/tolinku.com\/blog\/last-click-vs-multi-touch-attribution\/\">last-click vs multi-touch attribution<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why MMM Matters Now<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">MMM is experiencing a resurgence in mobile marketing because of privacy changes:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>ATT (iOS):<\/strong> With limited IDFA access, user-level attribution on iOS is incomplete.<\/li>\n<li><strong>Privacy Sandbox (Android):<\/strong> Google is moving toward aggregate-level measurement.<\/li>\n<li><strong>Cookie deprecation (web):<\/strong> Third-party cookies are going away.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">MMM does not need user-level data. It works with aggregate data that is always available: total spend per channel per week, total installs per week, total revenue per week. Privacy regulations do not restrict this data.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">MMM vs Attribution vs Incrementality<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Aspect<\/th>\n<th>Attribution<\/th>\n<th>Incrementality<\/th>\n<th>MMM<\/th>\n<\/tr>\n<\/thead>\n<tbody><tr>\n<td>Data level<\/td>\n<td>User-level<\/td>\n<td>User-level (experiment)<\/td>\n<td>Aggregate<\/td>\n<\/tr>\n<tr>\n<td>Privacy impact<\/td>\n<td>High (needs user IDs)<\/td>\n<td>Medium (needs user groups)<\/td>\n<td>None<\/td>\n<\/tr>\n<tr>\n<td>Measures<\/td>\n<td>Who converted<\/td>\n<td>What was incremental<\/td>\n<td>What drives outcomes<\/td>\n<\/tr>\n<tr>\n<td>Latency<\/td>\n<td>Real-time<\/td>\n<td>Weeks (experiment duration)<\/td>\n<td>Monthly (model refresh)<\/td>\n<\/tr>\n<tr>\n<td>Coverage<\/td>\n<td>Digital channels only<\/td>\n<td>One channel at a time<\/td>\n<td>All channels (including offline)<\/td>\n<\/tr>\n<tr>\n<td>Cost<\/td>\n<td>Attribution SDK fee<\/td>\n<td>Experiment setup + holdout cost<\/td>\n<td>Data science + compute<\/td>\n<\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The three approaches are complementary:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Attribution<\/strong> for day-to-day campaign management.<\/li>\n<li><strong>Incrementality<\/strong> for validating specific channels.<\/li>\n<li><strong>MMM<\/strong> for budget allocation across all channels.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">How MMM Works<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">The Basic Model<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">MMM uses regression to model the relationship between marketing inputs and business outcomes:<\/p>\n\n\n\n<pre><code>Installs = f(Facebook_Spend, Google_Spend, TikTok_Spend, TV_Spend, Seasonality, Trend, ...)\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A simplified linear model:<\/p>\n\n\n\n<pre><code class=\"language-python\">import statsmodels.api as sm\nimport pandas as pd\n\n# Weekly data\ndata = pd.DataFrame({\n    &#39;installs&#39;: weekly_installs,         # Dependent variable\n    &#39;fb_spend&#39;: weekly_fb_spend,         # Independent variables\n    &#39;google_spend&#39;: weekly_google_spend,\n    &#39;tiktok_spend&#39;: weekly_tiktok_spend,\n    &#39;tv_spend&#39;: weekly_tv_spend,\n    &#39;is_holiday&#39;: weekly_is_holiday,      # Control variable\n    &#39;week_number&#39;: range(len(weekly_installs))  # Trend\n})\n\nX = data[[&#39;fb_spend&#39;, &#39;google_spend&#39;, &#39;tiktok_spend&#39;, &#39;tv_spend&#39;, &#39;is_holiday&#39;, &#39;week_number&#39;]]\nX = sm.add_constant(X)\ny = data[&#39;installs&#39;]\n\nmodel = sm.OLS(y, X).fit()\nprint(model.summary())\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Adstock (Carryover Effects)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Marketing spend has lasting effects. A TV ad today still influences installs next week. Adstock models this decay:<\/p>\n\n\n\n<pre><code class=\"language-python\">def apply_adstock(spend_series, decay_rate=0.5):\n    &quot;&quot;&quot;\n    Apply geometric adstock transformation.\n    decay_rate: 0 = no carryover, 1 = full carryover\n    &quot;&quot;&quot;\n    adstocked = [spend_series[0]]\n    for i in range(1, len(spend_series)):\n        adstocked.append(spend_series[i] + decay_rate * adstocked[i-1])\n    return adstocked\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Different channels have different decay rates:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>TV:<\/strong> High carryover (decay = 0.7-0.9). Effects last weeks.<\/li>\n<li><strong>Search:<\/strong> Low carryover (decay = 0.1-0.3). Effects are immediate.<\/li>\n<li><strong>Social:<\/strong> Medium carryover (decay = 0.3-0.5).<\/li>\n<li><strong>Display:<\/strong> Medium carryover (decay = 0.3-0.5).<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Diminishing Returns<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Doubling spend on a channel does not double installs. MMM models this with saturation curves:<\/p>\n\n\n\n<pre><code class=\"language-python\">def hill_saturation(spend, half_max, shape):\n    &quot;&quot;&quot;\n    Hill function for diminishing returns.\n    half_max: spend level at which 50% of maximum effect is reached\n    shape: steepness of the curve (higher = sharper saturation)\n    &quot;&quot;&quot;\n    return spend**shape \/ (half_max**shape + spend**shape)\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This produces an S-curve where:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Low spend has increasing marginal returns (getting started).<\/li>\n<li>Medium spend has linear returns.<\/li>\n<li>High spend has decreasing marginal returns (saturation).<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Open-Source MMM Tools<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Several open-source tools make MMM accessible without building from scratch:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Tool<\/th>\n<th>Developer<\/th>\n<th>Language<\/th>\n<th>Features<\/th>\n<\/tr>\n<\/thead>\n<tbody><tr>\n<td><a href=\"https:\/\/facebookexperimental.github.io\/Robyn\/\" rel=\"nofollow noopener\" target=\"_blank\">Robyn<\/a><\/td>\n<td>Meta<\/td>\n<td>R<\/td>\n<td>Automated hyperparameter tuning, budget allocation<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/github.com\/google\/lightweight_mmm\" rel=\"nofollow noopener\" target=\"_blank\">LightweightMMM<\/a><\/td>\n<td>Google<\/td>\n<td>Python (JAX)<\/td>\n<td>Bayesian modeling, media saturation<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/www.pymc-marketing.io\/\" rel=\"nofollow noopener\" target=\"_blank\">PyMC-Marketing<\/a><\/td>\n<td>PyMC Labs<\/td>\n<td>Python<\/td>\n<td>Bayesian MMM + CLV, flexible<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/uber.github.io\/orbit\/\" rel=\"nofollow noopener\" target=\"_blank\">Orbit<\/a><\/td>\n<td>Uber<\/td>\n<td>Python<\/td>\n<td>Time series forecasting with marketing variables<\/td>\n<\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Getting Started with Robyn<\/h3>\n\n\n\n<pre><code class=\"language-r\"># Install Robyn\n# install.packages(&quot;Robyn&quot;)\nlibrary(Robyn)\n\n# Prepare input data\nInputCollect &lt;- robyn_inputs(\n  dt_input = weekly_data,\n  dt_holidays = holidays,\n  date_var = &quot;date&quot;,\n  dep_var = &quot;installs&quot;,\n  dep_var_type = &quot;conversion&quot;,\n  paid_media_spends = c(&quot;fb_spend&quot;, &quot;google_spend&quot;, &quot;tiktok_spend&quot;),\n  paid_media_vars = c(&quot;fb_impressions&quot;, &quot;google_clicks&quot;, &quot;tiktok_impressions&quot;),\n  organic_vars = c(&quot;seo_visits&quot;, &quot;email_clicks&quot;),\n  context_vars = c(&quot;competitor_spend&quot;, &quot;is_promotion&quot;),\n  window_start = &quot;2025-01-01&quot;,\n  window_end = &quot;2026-06-30&quot;,\n  adstock = &quot;geometric&quot;\n)\n\n# Run model\nOutputModels &lt;- robyn_run(InputCollect, iterations = 2000, trials = 5)\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Data Requirements<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Minimum Data<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Time range:<\/strong> 2+ years of weekly data (104+ data points). More is better.<\/li>\n<li><strong>Spend variation:<\/strong> Channels must have meaningful spend variation (increases, decreases, pauses). If spend is constant, the model cannot measure its effect.<\/li>\n<li><strong>Outcome data:<\/strong> Weekly installs, revenue, or the business metric you want to optimize.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Additional Data<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Data Type<\/th>\n<th>Examples<\/th>\n<th>Why It Helps<\/th>\n<\/tr>\n<\/thead>\n<tbody><tr>\n<td>Seasonality<\/td>\n<td>Holidays, back-to-school, tax season<\/td>\n<td>Controls for natural demand fluctuations<\/td>\n<\/tr>\n<tr>\n<td>Competitor activity<\/td>\n<td>Competitor ad spend, launches<\/td>\n<td>Controls for market-level effects<\/td>\n<\/tr>\n<tr>\n<td>Macroeconomic<\/td>\n<td>Unemployment rate, consumer confidence<\/td>\n<td>Controls for economic conditions<\/td>\n<\/tr>\n<tr>\n<td>Product changes<\/td>\n<td>App updates, pricing changes, new features<\/td>\n<td>Controls for non-marketing effects<\/td>\n<\/tr>\n<tr>\n<td>PR\/earned media<\/td>\n<td>Press mentions, viral moments<\/td>\n<td>Accounts for non-paid influence<\/td>\n<\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Budget Optimization<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The primary output of MMM is optimal budget allocation:<\/p>\n\n\n\n<pre><code class=\"language-python\"># Example optimization output\noptimal_budget = {\n    &#39;facebook&#39;: {&#39;current&#39;: 50000, &#39;optimal&#39;: 35000, &#39;change&#39;: -30},\n    &#39;google&#39;: {&#39;current&#39;: 40000, &#39;optimal&#39;: 55000, &#39;change&#39;: +37.5},\n    &#39;tiktok&#39;: {&#39;current&#39;: 30000, &#39;optimal&#39;: 25000, &#39;change&#39;: -17},\n    &#39;tv&#39;: {&#39;current&#39;: 80000, &#39;optimal&#39;: 85000, &#39;change&#39;: +6}\n}\n# Total budget stays the same ($200,000)\n# Model predicts 15% more installs with the optimized allocation\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The model identifies that Google is underspent (high marginal returns) and Facebook is overspent (diminishing returns at current level).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Limitations<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Requires historical data.<\/strong> New channels with no historical data cannot be modeled.<\/li>\n<li><strong>Aggregate only.<\/strong> Cannot optimize individual campaigns or creatives.<\/li>\n<li><strong>Correlation vs causation.<\/strong> MMM measures correlation; incrementality tests prove causation.<\/li>\n<li><strong>Slow feedback loop.<\/strong> Models are refreshed monthly or quarterly, not in real-time.<\/li>\n<li><strong>Assumes stationarity.<\/strong> The model assumes relationships are stable. Major market changes invalidate it.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">Tolinku for Marketing Measurement<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/tolinku.com\/features\/analytics\">Tolinku&#39;s analytics<\/a> provide aggregate deep link click and conversion data that can be used as input variables in MMM. Export weekly click and conversion data from the <a href=\"https:\/\/tolinku.com\/docs\/user-guide\/analytics\/\">Tolinku dashboard<\/a> to include deep link campaigns in your media mix model.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For incrementality testing, see <a href=\"https:\/\/tolinku.com\/blog\/incrementality-testing-mobile\/\">incrementality testing for mobile marketing<\/a>. For mobile attribution, see <a href=\"https:\/\/tolinku.com\/blog\/mobile-attribution-developers-guide\/\">mobile attribution: a developer&#39;s guide<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Apply media mix modeling to optimize mobile app marketing spend. Understand when MMM complements attribution and how to get started.<\/p>\n","protected":false},"author":2,"featured_media":1766,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"Media Mix Modeling for Mobile App Marketing","rank_math_description":"Apply media mix modeling to optimize mobile app marketing spend. Understand when MMM complements attribution and how to get started.","rank_math_focus_keyword":"media mix modeling apps","rank_math_canonical_url":"","rank_math_facebook_title":"","rank_math_facebook_description":"","rank_math_facebook_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-media-mix-modeling-apps.png","rank_math_facebook_image_id":"","rank_math_twitter_title":"","rank_math_twitter_description":"","rank_math_twitter_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-media-mix-modeling-apps.png","footnotes":""},"categories":[14],"tags":[37,28,520,533,20,110,532,69],"class_list":["post-1767","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-analytics","tag-analytics","tag-attribution","tag-campaign-measurement","tag-data-science","tag-deep-linking","tag-marketing","tag-media-mix-modeling","tag-mobile-development"],"_links":{"self":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/1767","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/comments?post=1767"}],"version-history":[{"count":3,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/1767\/revisions"}],"predecessor-version":[{"id":2703,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/1767\/revisions\/2703"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media\/1766"}],"wp:attachment":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media?parent=1767"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/categories?post=1767"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/tags?post=1767"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}