<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>ChannelAttribution: The in-house Attribution Solution Blog</title>
        <link>https://channelattribution.pro/blog/</link>
        <description>ChannelAttribution: The in-house Attribution Solution Blog</description>
        <lastBuildDate>Fri, 11 Jul 2025 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[Context-Aware Markov Attribution]]></title>
            <link>https://channelattribution.pro/blog/markov-attribution-external-features/</link>
            <guid>https://channelattribution.pro/blog/markov-attribution-external-features/</guid>
            <pubDate>Fri, 11 Jul 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[How using multiple Markov models—one per feature—leads to more accurate and nuanced attribution across customer journeys.]]></description>
            <content:encoded><![CDATA[<p>Traditional attribution models estimate the contribution of each channel to conversion. While helpful, these models usually treat every customer the same—ignoring important context like user region, segment, or behavior over time.</p>
<p>In this article, we describe an approach that builds one Markov model for each feature (e.g. region, segment, time to conversion), and combines them into a final, <strong>context-aware attribution score</strong>.</p>
<hr>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="-why-context-matters-in-attribution">🧭 Why Context Matters in Attribution<a href="https://channelattribution.pro/blog/markov-attribution-external-features/#-why-context-matters-in-attribution" class="hash-link" aria-label="Direct link to 🧭 Why Context Matters in Attribution" title="Direct link to 🧭 Why Context Matters in Attribution">​</a></h2>
<p>A typical attribution model might give you this insight:</p>
<blockquote>
<p>“Email drives 18% of conversions.”</p>
</blockquote>
<p>But is that true across all users? Maybe it’s 30% for returning customers, and just 5% for new users.<br>
<!-- -->If we don’t consider context, we miss important patterns.</p>
<hr>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="-comparison-of-two-user-journeys">🧍 Comparison of Two User Journeys<a href="https://channelattribution.pro/blog/markov-attribution-external-features/#-comparison-of-two-user-journeys" class="hash-link" aria-label="Direct link to 🧍 Comparison of Two User Journeys" title="Direct link to 🧍 Comparison of Two User Journeys">​</a></h3>
<p><strong>User A</strong> (New, from UK):<br>
<!-- -->→ Paid Search → Social → Conversion</p>
<p><strong>User B</strong> (Returning, from Italy):<br>
<!-- -->→ Paid Search → Social → Conversion</p>
<p>🟡 <em>Both users took the same path—but their behavior and background are different. Treating them the same reduces model accuracy.</em></p>
<hr>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="-step-by-step-how-context-aware-attribution-works">🧮 Step-by-Step: How Context-Aware Attribution Works<a href="https://channelattribution.pro/blog/markov-attribution-external-features/#-step-by-step-how-context-aware-attribution-works" class="hash-link" aria-label="Direct link to 🧮 Step-by-Step: How Context-Aware Attribution Works" title="Direct link to 🧮 Step-by-Step: How Context-Aware Attribution Works">​</a></h2>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="1-start-with-enriched-path-data">1. Start with Enriched Path Data<a href="https://channelattribution.pro/blog/markov-attribution-external-features/#1-start-with-enriched-path-data" class="hash-link" aria-label="Direct link to 1. Start with Enriched Path Data" title="Direct link to 1. Start with Enriched Path Data">​</a></h3>
<p>Your journey data includes more than just channels:</p>
<table><thead><tr><th>user_id</th><th>step</th><th>channel</th><th>region</th><th>segment</th><th>seconds_to_last_touch</th><th>position</th></tr></thead><tbody><tr><td>1001</td><td>1</td><td>Email</td><td>UK</td><td>New</td><td>1420</td><td>first</td></tr><tr><td>1001</td><td>2</td><td>Social</td><td>UK</td><td>New</td><td>800</td><td>middle</td></tr><tr><td>1001</td><td>3</td><td>Social</td><td>UK</td><td>New</td><td>800</td><td>last</td></tr><tr><td>1002</td><td>1</td><td>Paid Search</td><td>Italy</td><td>Returning</td><td>215</td><td>first</td></tr><tr><td>1002</td><td>2</td><td>Direct</td><td>Italy</td><td>Returning</td><td>0</td><td>last</td></tr></tbody></table>
<p>You might have:</p>
<ul>
<li><strong>Categorical features</strong>: region, segment, position</li>
<li><strong>Numerical features</strong>: seconds to last touch</li>
</ul>
<hr>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="2-discretize-numerical-features">2. Discretize Numerical Features<a href="https://channelattribution.pro/blog/markov-attribution-external-features/#2-discretize-numerical-features" class="hash-link" aria-label="Direct link to 2. Discretize Numerical Features" title="Direct link to 2. Discretize Numerical Features">​</a></h3>
<p>Before modeling, numerical variables like <code>seconds_to_last_touch</code> are <strong>binned</strong> to turn them into categorical values:</p>
<table><thead><tr><th>seconds_to_last_touch</th><th>→</th><th>time_bin</th></tr></thead><tbody><tr><td>1420</td><td>→</td><td>bin_15</td></tr><tr><td>800</td><td>→</td><td>bin_10</td></tr><tr><td>215</td><td>→</td><td>bin_5</td></tr><tr><td>0</td><td>→</td><td>bin_0</td></tr></tbody></table>
<hr>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="3-create-artificial-channels-from-each-feature">3. Create Artificial Channels from Each Feature<a href="https://channelattribution.pro/blog/markov-attribution-external-features/#3-create-artificial-channels-from-each-feature" class="hash-link" aria-label="Direct link to 3. Create Artificial Channels from Each Feature" title="Direct link to 3. Create Artificial Channels from Each Feature">​</a></h3>
<p>We now create “channel + feature” combinations to isolate feature-specific behavior.<br>
<!-- -->Example using <code>region</code>:</p>
<table><thead><tr><th>user_id</th><th>step</th><th>channel_region</th></tr></thead><tbody><tr><td>1001</td><td>1</td><td>Email_UK</td></tr><tr><td>1001</td><td>2</td><td>Social_UK</td></tr><tr><td>1002</td><td>1</td><td>PaidSearch_Italy</td></tr><tr><td>1002</td><td>2</td><td>Direct_Italy</td></tr></tbody></table>
<hr>
<p><strong>Original Path</strong>:<br>
<code>Email → Social → Conversion</code></p>
<p><strong>Transformed Paths</strong>:<br>
<code>Email_UK → Social_UK → Conversion</code></p>
<p>🔁 <em>We now have multiple transformed views of the same path, customized per feature.</em></p>
<hr>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="4-fit-one-markov-model-per-feature">4. Fit One Markov Model per Feature<a href="https://channelattribution.pro/blog/markov-attribution-external-features/#4-fit-one-markov-model-per-feature" class="hash-link" aria-label="Direct link to 4. Fit One Markov Model per Feature" title="Direct link to 4. Fit One Markov Model per Feature">​</a></h3>
<p>Each artificial channel is modeled independently.<br>
<!-- -->For example, for the feature <strong>region</strong>, we fit a Markov model using artificial channels:</p>
<ul>
<li><code>Email_UK → Social_UK → Conversion</code></li>
<li><code>PaidSearch_Italy → Direct_Italy → Conversion</code></li>
<li><code>Email_FR → Social_FR → Conversion</code><br>
<!-- -->...</li>
</ul>
<p>From each model, we compute the **odds ** for each channel:</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mtext>Odds(Channel)</mtext><mo>=</mo><mfrac><mrow><mi>Pr</mi><mo>⁡</mo><mo stretchy="false">(</mo><mtext>Conversion&nbsp;via&nbsp;Channel</mtext><mo stretchy="false">)</mo></mrow><mrow><mn>1</mn><mo>−</mo><mi>Pr</mi><mo>⁡</mo><mo stretchy="false">(</mo><mtext>Conversion&nbsp;via&nbsp;Channel</mtext><mo stretchy="false">)</mo></mrow></mfrac></mrow><annotation encoding="application/x-tex">\text{Odds(Channel)} = \frac{\Pr(\text{Conversion via Channel})}{1 - \Pr(\text{Conversion via Channel})}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord text"><span class="mord">Odds(Channel)</span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.363em;vertical-align:-0.936em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.427em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mop">Pr</span><span class="mopen">(</span><span class="mord text"><span class="mord">Conversion&nbsp;via&nbsp;Channel</span></span><span class="mclose">)</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mop">Pr</span><span class="mopen">(</span><span class="mord text"><span class="mord">Conversion&nbsp;via&nbsp;Channel</span></span><span class="mclose">)</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.936em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span>
<p>These odds quantify how strongly a channels contribute to conversion, and are used to assign attribution credit along the path.</p>
<hr>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="5-evaluate-model-performance">5. Evaluate Model Performance<a href="https://channelattribution.pro/blog/markov-attribution-external-features/#5-evaluate-model-performance" class="hash-link" aria-label="Direct link to 5. Evaluate Model Performance" title="Direct link to 5. Evaluate Model Performance">​</a></h3>
<p>Each feature-specific model is evaluated using <strong>AUC-PR (Area Under the Precision-Recall Curve)</strong>—a better choice than ROC-AUC for imbalanced datasets (few conversions vs. many non-conversions).</p>
<p>We assign a weight to each model based on how much it improves prediction compared to a baseline (the overall conversion rate):</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mtext>Weight</mtext><mo stretchy="false">(</mo><mtext>Model</mtext><mo stretchy="false">)</mo><mo>=</mo><mrow><mo fence="true">{</mo><mtable rowspacing="0.36em" columnalign="left left" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mtext>AUC-PR</mtext><mo separator="true">,</mo></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mtext>if&nbsp;AUC-PR</mtext><mo>&gt;</mo><mtext>CR</mtext></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mn>0</mn><mo separator="true">,</mo></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mtext>otherwise</mtext></mstyle></mtd></mtr></mtable></mrow></mrow><annotation encoding="application/x-tex">\text{Weight}(\text{Model}) = 
\begin{cases}
\text{AUC-PR}, &amp; \text{if AUC-PR} &gt; \text{CR} \\\\
0, &amp; \text{otherwise}
\end{cases}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord text"><span class="mord">Weight</span></span><span class="mopen">(</span><span class="mord text"><span class="mord">Model</span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:4.32em;vertical-align:-1.91em"></span><span class="minner"><span class="mopen"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.35em"><span style="top:-2.2em"><span class="pstrut" style="height:3.15em"></span><span class="delimsizinginner delim-size4"><span>⎩</span></span></span><span style="top:-2.192em"><span class="pstrut" style="height:3.15em"></span><span style="height:0.316em;width:0.8889em"><svg xmlns="http://www.w3.org/2000/svg" width="0.8889em" height="0.316em" style="width:0.8889em" viewBox="0 0 888.89 316" preserveAspectRatio="xMinYMin"><path d="M384 0 H504 V316 H384z M384 0 H504 V316 H384z"></path></svg></span></span><span style="top:-3.15em"><span class="pstrut" style="height:3.15em"></span><span class="delimsizinginner delim-size4"><span>⎨</span></span></span><span style="top:-4.292em"><span class="pstrut" style="height:3.15em"></span><span style="height:0.316em;width:0.8889em"><svg xmlns="http://www.w3.org/2000/svg" width="0.8889em" height="0.316em" style="width:0.8889em" viewBox="0 0 888.89 316" preserveAspectRatio="xMinYMin"><path d="M384 0 H504 V316 H384z M384 0 H504 V316 H384z"></path></svg></span></span><span style="top:-4.6em"><span class="pstrut" style="height:3.15em"></span><span class="delimsizinginner delim-size4"><span>⎧</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.85em"><span></span></span></span></span></span></span><span class="mord"><span class="mtable"><span class="col-align-l"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.41em"><span style="top:-4.41em"><span class="pstrut" style="height:3.008em"></span><span class="mord"><span class="mord text"><span class="mord">AUC-PR</span></span><span class="mpunct">,</span></span></span><span style="top:-2.97em"><span class="pstrut" style="height:3.008em"></span><span class="mord"></span></span><span style="top:-1.53em"><span class="pstrut" style="height:3.008em"></span><span class="mord"><span class="mord">0</span><span class="mpunct">,</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.91em"><span></span></span></span></span></span><span class="arraycolsep" style="width:1em"></span><span class="col-align-l"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.41em"><span style="top:-4.41em"><span class="pstrut" style="height:3.008em"></span><span class="mord"><span class="mord text"><span class="mord">if&nbsp;AUC-PR</span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">&gt;</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mord text"><span class="mord">CR</span></span></span></span><span style="top:-1.53em"><span class="pstrut" style="height:3.008em"></span><span class="mord"><span class="mord text"><span class="mord">otherwise</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.91em"><span></span></span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span>
<p>Where:</p>
<ul>
<li><strong>AUC-PR</strong> is the precision-recall performance of the model</li>
<li><strong>CR</strong> is the global conversion rate in the dataset</li>
</ul>
<p>Only models that outperform the baseline are retained.</p>
<hr>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="6-final-attribution--weighted-mean">6. Final Attribution = Weighted Mean<a href="https://channelattribution.pro/blog/markov-attribution-external-features/#6-final-attribution--weighted-mean" class="hash-link" aria-label="Direct link to 6. Final Attribution = Weighted Mean" title="Direct link to 6. Final Attribution = Weighted Mean">​</a></h3>
<p>The final attribution score is computed as a <strong>weighted average of the feature-specific models</strong>.</p>
<p>Each model contributes in proportion to its performance:</p>
<p>Example:</p>
<ul>
<li>weight(Region) = 0.25</li>
<li>weight(Segment) = 0.38</li>
<li>weight(Seconds to Last Touch) = 0.12</li>
<li>weight(Position) = 0.22</li>
</ul>
<p>The final attribution score for a channel aggregates contributions across all relevant features.</p>
<hr>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="-why-this-works">✅ Why This Works<a href="https://channelattribution.pro/blog/markov-attribution-external-features/#-why-this-works" class="hash-link" aria-label="Direct link to ✅ Why This Works" title="Direct link to ✅ Why This Works">​</a></h2>
<ul>
<li>👁️ You gain <strong>multiple perspectives</strong> on customer behavior</li>
<li>🎯 You only keep models that improve predictive accuracy</li>
<li>⚖️ You combine models in a <strong>data-driven</strong> way—not based on guesswork or static rules</li>
</ul>
<hr>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="-when-to-use-this-approach">💡 When to Use This Approach<a href="https://channelattribution.pro/blog/markov-attribution-external-features/#-when-to-use-this-approach" class="hash-link" aria-label="Direct link to 💡 When to Use This Approach" title="Direct link to 💡 When to Use This Approach">​</a></h2>
<p>This method is especially useful when:</p>
<ul>
<li>You have <strong>rich journey data</strong> with structured features</li>
<li>You need <strong>transaction-level attribution</strong></li>
<li>You want <strong>interpretable results</strong></li>
<li>You want to understand <strong>how attribution shifts</strong> across audience segments</li>
</ul>
<hr>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="-learn-more">📚 Learn More<a href="https://channelattribution.pro/blog/markov-attribution-external-features/#-learn-more" class="hash-link" aria-label="Direct link to 📚 Learn More" title="Direct link to 📚 Learn More">​</a></h2>
<p>If you'd like to see how this approach works in practice, visit <a href="https://channelattribution.pro/docs" style="color:#007BFF">our documentation</a> or <a href="https://channelattribution.pro/form">contact us</a> to discuss your use case.</p>]]></content:encoded>
            <category>attribution</category>
            <category>markov model</category>
            <category>customer journey</category>
            <category>channel attribution</category>
            <category>marketing analytics</category>
        </item>
    </channel>
</rss>