<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>模組八：行為資料的商業利用 on Tarragon</title><link>https://tarrragon.github.io/blog/monitoring/08-business-analytics/</link><description>Recent content in 模組八：行為資料的商業利用 on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Fri, 19 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/monitoring/08-business-analytics/index.xml" rel="self" type="application/rss+xml"/><item><title>行為事件設計</title><link>https://tarrragon.github.io/blog/monitoring/08-business-analytics/behavior-event-design/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/monitoring/08-business-analytics/behavior-event-design/</guid><description>&lt;p>行為事件是使用者操作的結構化記錄，每一筆事件回答「誰、在什麼時候、做了什麼、結果如何」。行為分析的品質上限由事件設計決定 — 事件粒度太粗無法回答細節問題，事件粒度太細讓儲存和查詢成本失控。&lt;/p>
&lt;h2 id="事件命名">事件命名&lt;/h2>
&lt;p>行為事件的命名遵循 &lt;code>namespace.action&lt;/code> 格式（&lt;a href="https://tarrragon.github.io/blog/monitoring/01-mental-model/event-naming-convention/" data-link-title="事件命名規範" data-link-desc="namespace.action 格式的事件命名、命名一致性的工程價值、和商業方案命名慣例的對應">模組一 事件命名規範&lt;/a>）。行為分析場景對命名的額外要求是：同一個 funnel 內的事件要能用 namespace 前綴篩選。&lt;/p>
&lt;p>例：註冊流程的事件用共同前綴 &lt;code>signup&lt;/code>：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">signup.page.view 使用者看到註冊頁
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">signup.form.submit 使用者送出表單
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">signup.email.verify 使用者點擊驗證信連結
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">signup.complete 註冊完成&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>用 &lt;code>signup.*&lt;/code> 就能篩選出整個註冊流程的事件，不需要事先知道每一步的完整名稱。&lt;/p>
&lt;h2 id="屬性設計">屬性設計&lt;/h2>
&lt;p>每個事件除了名稱，還帶有屬性（properties / parameters）描述事件的 context。屬性分成三層：&lt;/p>
&lt;h3 id="通用屬性每個事件都有">通用屬性（每個事件都有）&lt;/h3>
&lt;ul>
&lt;li>&lt;code>timestamp&lt;/code>：事件發生的時間（UTC，毫秒精度）&lt;/li>
&lt;li>&lt;code>session_id&lt;/code>：當次使用的 session 識別碼&lt;/li>
&lt;li>&lt;code>user_id&lt;/code>：使用者識別碼（去識別化後，見 &lt;a href="https://tarrragon.github.io/blog/monitoring/07-security-privacy/" data-link-title="模組七：資安與隱私" data-link-desc="SDK redaction / transport 加密 / collector access control / 去識別化 — 蒐集的資料本身就是風險資產">模組七&lt;/a>）&lt;/li>
&lt;li>&lt;code>platform&lt;/code>：iOS / Android / Web&lt;/li>
&lt;li>&lt;code>app_version&lt;/code>：app 版本號&lt;/li>
&lt;/ul>
&lt;h3 id="事件類型屬性同類事件共有">事件類型屬性（同類事件共有）&lt;/h3>
&lt;ul>
&lt;li>頁面瀏覽事件：&lt;code>page_name&lt;/code>、&lt;code>referrer&lt;/code>&lt;/li>
&lt;li>按鈕點擊事件：&lt;code>button_id&lt;/code>、&lt;code>button_text&lt;/code>&lt;/li>
&lt;li>搜尋事件：&lt;code>query&lt;/code>、&lt;code>result_count&lt;/code>&lt;/li>
&lt;/ul>
&lt;h3 id="事件專屬屬性特定事件才有">事件專屬屬性（特定事件才有）&lt;/h3>
&lt;ul>
&lt;li>&lt;code>signup.form.submit&lt;/code>：&lt;code>form_method&lt;/code>（email / Google / Apple）&lt;/li>
&lt;li>&lt;code>purchase.complete&lt;/code>：&lt;code>amount&lt;/code>、&lt;code>currency&lt;/code>、&lt;code>product_id&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>屬性設計的判斷標準是：這個屬性是否用於回答一個分析問題。「註冊方式的轉換率差異」需要 &lt;code>form_method&lt;/code> 屬性；如果沒有這個分析問題，就不需要這個屬性。&lt;/p>
&lt;h2 id="funnel-定義">Funnel 定義&lt;/h2>
&lt;p>Funnel 是一連串有順序的事件，代表使用者完成一個目標的步驟。Funnel 定義在事件設計階段完成 — 決定哪些事件構成一個 funnel、順序是什麼、每步之間的最大時間間隔。&lt;/p>
&lt;p>定義一個 funnel 需要：&lt;/p>
&lt;p>&lt;strong>步驟清單&lt;/strong>：funnel 包含哪些事件，順序是什麼。&lt;/p>
&lt;p>&lt;strong>時間窗口&lt;/strong>：步驟之間的最大間隔。使用者在步驟 A 之後 30 天才做步驟 B，是否算在同一個 funnel 內？時間窗口的設定取決於業務場景 — 電商結帳 funnel 通常是 30 分鐘，SaaS onboarding funnel 可能是 7 天。&lt;/p>
&lt;p>&lt;strong>完成條件&lt;/strong>：什麼算「完成」funnel。到達最後一步即完成，還是需要特定屬性值（&lt;code>purchase.complete&lt;/code> 且 &lt;code>status = success&lt;/code>）。&lt;/p>
&lt;h2 id="過度收集的成本">過度收集的成本&lt;/h2>
&lt;p>行為事件收集的邊界是「能回答已知的分析問題」。收集超出分析需求的事件有三個成本：&lt;/p>
&lt;p>&lt;strong>儲存成本&lt;/strong>：每個事件佔一行 JSONL。高頻事件（每次滾動、每次 hover）的資料量遠大於低頻事件（按鈕點擊、頁面瀏覽）。&lt;/p>
&lt;p>&lt;strong>隱私風險&lt;/strong>：收集的事件越多，包含可識別個人行為模式的風險越高（&lt;a href="https://tarrragon.github.io/blog/monitoring/07-security-privacy/" data-link-title="模組七：資安與隱私" data-link-desc="SDK redaction / transport 加密 / collector access control / 去識別化 — 蒐集的資料本身就是風險資產">模組七 資安與隱私&lt;/a>）。&lt;/p>
&lt;p>&lt;strong>噪音&lt;/strong>：分析時需要從大量事件中篩選出有意義的模式。事件越多，訊噪比越低。&lt;/p>
&lt;p>設計好的行為事件直接成為 &lt;a href="https://tarrragon.github.io/blog/monitoring/08-business-analytics/funnel-analysis/" data-link-title="Funnel Analysis" data-link-desc="使用者在哪一步流失 — 從事件序列計算每步轉換率、找出流失最嚴重的步驟、區分設計問題和技術問題">Funnel analysis&lt;/a> 的輸入 — funnel 的每一步對應一個行為事件。行為事件在四類事件分類中屬於 Event 類，完整的分類定義見&lt;a href="https://tarrragon.github.io/blog/monitoring/01-mental-model/four-event-types/" data-link-title="四類事件的完整定義" data-link-desc="Event / Error / Metric / Lifecycle 四類事件各自的語意、觸發時機和典型用途 — 分類是監控體系的統一語言">模組一 四類事件定義&lt;/a>。收集行為事件前必須完成&lt;a href="https://tarrragon.github.io/blog/monitoring/07-security-privacy/" data-link-title="模組七：資安與隱私" data-link-desc="SDK redaction / transport 加密 / collector access control / 去識別化 — 蒐集的資料本身就是風險資產">去識別化&lt;/a> — 使用者行為模式本身就是可識別資訊。&lt;/p></description><content:encoded><![CDATA[<p>行為事件是使用者操作的結構化記錄，每一筆事件回答「誰、在什麼時候、做了什麼、結果如何」。行為分析的品質上限由事件設計決定 — 事件粒度太粗無法回答細節問題，事件粒度太細讓儲存和查詢成本失控。</p>
<h2 id="事件命名">事件命名</h2>
<p>行為事件的命名遵循 <code>namespace.action</code> 格式（<a href="/blog/monitoring/01-mental-model/event-naming-convention/" data-link-title="事件命名規範" data-link-desc="namespace.action 格式的事件命名、命名一致性的工程價值、和商業方案命名慣例的對應">模組一 事件命名規範</a>）。行為分析場景對命名的額外要求是：同一個 funnel 內的事件要能用 namespace 前綴篩選。</p>
<p>例：註冊流程的事件用共同前綴 <code>signup</code>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">signup.page.view          使用者看到註冊頁
</span></span><span class="line"><span class="ln">2</span><span class="cl">signup.form.submit        使用者送出表單
</span></span><span class="line"><span class="ln">3</span><span class="cl">signup.email.verify       使用者點擊驗證信連結
</span></span><span class="line"><span class="ln">4</span><span class="cl">signup.complete           註冊完成</span></span></code></pre></div><p>用 <code>signup.*</code> 就能篩選出整個註冊流程的事件，不需要事先知道每一步的完整名稱。</p>
<h2 id="屬性設計">屬性設計</h2>
<p>每個事件除了名稱，還帶有屬性（properties / parameters）描述事件的 context。屬性分成三層：</p>
<h3 id="通用屬性每個事件都有">通用屬性（每個事件都有）</h3>
<ul>
<li><code>timestamp</code>：事件發生的時間（UTC，毫秒精度）</li>
<li><code>session_id</code>：當次使用的 session 識別碼</li>
<li><code>user_id</code>：使用者識別碼（去識別化後，見 <a href="/blog/monitoring/07-security-privacy/" data-link-title="模組七：資安與隱私" data-link-desc="SDK redaction / transport 加密 / collector access control / 去識別化 — 蒐集的資料本身就是風險資產">模組七</a>）</li>
<li><code>platform</code>：iOS / Android / Web</li>
<li><code>app_version</code>：app 版本號</li>
</ul>
<h3 id="事件類型屬性同類事件共有">事件類型屬性（同類事件共有）</h3>
<ul>
<li>頁面瀏覽事件：<code>page_name</code>、<code>referrer</code></li>
<li>按鈕點擊事件：<code>button_id</code>、<code>button_text</code></li>
<li>搜尋事件：<code>query</code>、<code>result_count</code></li>
</ul>
<h3 id="事件專屬屬性特定事件才有">事件專屬屬性（特定事件才有）</h3>
<ul>
<li><code>signup.form.submit</code>：<code>form_method</code>（email / Google / Apple）</li>
<li><code>purchase.complete</code>：<code>amount</code>、<code>currency</code>、<code>product_id</code></li>
</ul>
<p>屬性設計的判斷標準是：這個屬性是否用於回答一個分析問題。「註冊方式的轉換率差異」需要 <code>form_method</code> 屬性；如果沒有這個分析問題，就不需要這個屬性。</p>
<h2 id="funnel-定義">Funnel 定義</h2>
<p>Funnel 是一連串有順序的事件，代表使用者完成一個目標的步驟。Funnel 定義在事件設計階段完成 — 決定哪些事件構成一個 funnel、順序是什麼、每步之間的最大時間間隔。</p>
<p>定義一個 funnel 需要：</p>
<p><strong>步驟清單</strong>：funnel 包含哪些事件，順序是什麼。</p>
<p><strong>時間窗口</strong>：步驟之間的最大間隔。使用者在步驟 A 之後 30 天才做步驟 B，是否算在同一個 funnel 內？時間窗口的設定取決於業務場景 — 電商結帳 funnel 通常是 30 分鐘，SaaS onboarding funnel 可能是 7 天。</p>
<p><strong>完成條件</strong>：什麼算「完成」funnel。到達最後一步即完成，還是需要特定屬性值（<code>purchase.complete</code> 且 <code>status = success</code>）。</p>
<h2 id="過度收集的成本">過度收集的成本</h2>
<p>行為事件收集的邊界是「能回答已知的分析問題」。收集超出分析需求的事件有三個成本：</p>
<p><strong>儲存成本</strong>：每個事件佔一行 JSONL。高頻事件（每次滾動、每次 hover）的資料量遠大於低頻事件（按鈕點擊、頁面瀏覽）。</p>
<p><strong>隱私風險</strong>：收集的事件越多，包含可識別個人行為模式的風險越高（<a href="/blog/monitoring/07-security-privacy/" data-link-title="模組七：資安與隱私" data-link-desc="SDK redaction / transport 加密 / collector access control / 去識別化 — 蒐集的資料本身就是風險資產">模組七 資安與隱私</a>）。</p>
<p><strong>噪音</strong>：分析時需要從大量事件中篩選出有意義的模式。事件越多，訊噪比越低。</p>
<p>設計好的行為事件直接成為 <a href="/blog/monitoring/08-business-analytics/funnel-analysis/" data-link-title="Funnel Analysis" data-link-desc="使用者在哪一步流失 — 從事件序列計算每步轉換率、找出流失最嚴重的步驟、區分設計問題和技術問題">Funnel analysis</a> 的輸入 — funnel 的每一步對應一個行為事件。行為事件在四類事件分類中屬於 Event 類，完整的分類定義見<a href="/blog/monitoring/01-mental-model/four-event-types/" data-link-title="四類事件的完整定義" data-link-desc="Event / Error / Metric / Lifecycle 四類事件各自的語意、觸發時機和典型用途 — 分類是監控體系的統一語言">模組一 四類事件定義</a>。收集行為事件前必須完成<a href="/blog/monitoring/07-security-privacy/" data-link-title="模組七：資安與隱私" data-link-desc="SDK redaction / transport 加密 / collector access control / 去識別化 — 蒐集的資料本身就是風險資產">去識別化</a> — 使用者行為模式本身就是可識別資訊。</p>
]]></content:encoded></item><item><title>Funnel Analysis</title><link>https://tarrragon.github.io/blog/monitoring/08-business-analytics/funnel-analysis/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/monitoring/08-business-analytics/funnel-analysis/</guid><description>&lt;p>Funnel analysis 計算使用者在一連串步驟中每一步的轉換率，回答「使用者在哪一步離開」。流失最嚴重的步驟是優化投資報酬率最高的位置 — 修一個步驟的流失比優化所有步驟的體驗更有效。&lt;/p>
&lt;h2 id="基本計算">基本計算&lt;/h2>
&lt;p>Funnel 的每一步有兩個數字：進入人數和完成人數。轉換率 = 完成人數 / 進入人數。&lt;/p>
&lt;p>以四步註冊 funnel 為例：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>步驟&lt;/th>
 &lt;th>進入人數&lt;/th>
 &lt;th>完成人數&lt;/th>
 &lt;th>轉換率&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>看到註冊頁&lt;/td>
 &lt;td>1000&lt;/td>
 &lt;td>1000&lt;/td>
 &lt;td>100%&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>填寫表單&lt;/td>
 &lt;td>1000&lt;/td>
 &lt;td>620&lt;/td>
 &lt;td>62%&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>送出表單&lt;/td>
 &lt;td>620&lt;/td>
 &lt;td>580&lt;/td>
 &lt;td>93.5%&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>完成 email 驗證&lt;/td>
 &lt;td>580&lt;/td>
 &lt;td>310&lt;/td>
 &lt;td>53.4%&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>整體轉換率 = 310 / 1000 = 31%。但更有價值的資訊在每步的轉換率：步驟 2（填寫表單）流失 38%，步驟 4（email 驗證）流失 46.6%。這兩步是優化的優先目標。&lt;/p>
&lt;h2 id="流失原因的區分">流失原因的區分&lt;/h2>
&lt;p>Funnel analysis 指出「哪一步流失」，但不直接回答「為什麼流失」。流失原因需要結合其他資料推斷。&lt;/p>
&lt;h3 id="設計問題導致的流失">設計問題導致的流失&lt;/h3>
&lt;p>使用者看到表單但沒填寫（步驟 2 流失 38%）。可能原因：表單欄位太多、要求的資訊太敏感（信用卡號在註冊階段）、表單 UI 在特定裝置上有問題。&lt;/p>
&lt;p>判斷方式：按平台、裝置、螢幕尺寸細分轉換率。如果 iOS 轉換率 70% 但 Android 只有 45%，可能是 Android 的表單 UI 有問題。&lt;/p>
&lt;h3 id="技術問題導致的流失">技術問題導致的流失&lt;/h3>
&lt;p>使用者送出表單但 email 驗證沒完成（步驟 4 流失 46.6%）。可能原因：驗證信被歸到垃圾郵件、驗證連結過期太快、驗證頁面載入失敗。&lt;/p>
&lt;p>判斷方式：結合 error 事件。如果步驟 4 有大量 &lt;code>signup.email.verify.failed&lt;/code> error，是技術問題；如果沒有 error 但流失高，使用者可能沒收到信或沒看到信。&lt;/p>
&lt;h3 id="意圖問題導致的流失">意圖問題導致的流失&lt;/h3>
&lt;p>使用者到了註冊頁但根本沒打算註冊 — 只是瀏覽。這類流失不是問題，是正常的使用者行為。&lt;/p>
&lt;p>判斷方式：看使用者在流失步驟停留的時間。停留 &amp;lt; 3 秒就離開，可能是誤點或沒有註冊意圖；停留 &amp;gt; 30 秒但沒完成，可能是遇到障礙。&lt;/p>
&lt;h2 id="funnel-的時間窗口">Funnel 的時間窗口&lt;/h2>
&lt;p>同一個使用者在步驟 A 和步驟 B 之間隔了多久，仍算在同一個 funnel 內？時間窗口的設定影響 funnel 的計算結果。&lt;/p>
&lt;p>&lt;strong>窗口太短&lt;/strong>：使用者中途離開稍後回來完成，被計為流失。Funnel 的流失率被高估。&lt;/p>
&lt;p>&lt;strong>窗口太長&lt;/strong>：使用者今天瀏覽、一個月後被廣告重新帶回來完成，兩次獨立的意圖被合併成一個 funnel。轉換率被高估。&lt;/p>
&lt;p>合理的窗口依業務場景而定：電商結帳 funnel 用 30 分鐘到 1 小時，SaaS onboarding 用 7 天，B2B 銷售漏斗用 30-90 天。&lt;/p>
&lt;h2 id="畫面狀態矩陣和-funnel-的關係">畫面狀態矩陣和 funnel 的關係&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/ux-design/knowledge-cards/screen-state-matrix/" data-link-title="畫面狀態矩陣" data-link-desc="說明用四欄表格（顯示/可用操作/進入條件/退出路徑）系統性地暴露畫面導航缺口的設計工具">畫面狀態矩陣&lt;/a>（&lt;a href="https://tarrragon.github.io/blog/ux-design/01-screen-state-machine/" data-link-title="模組一：畫面狀態機設計" data-link-desc="畫面狀態矩陣（顯示 / 操作 / 進入 / 退出）— 退出路徑為空 = UX 死胡同">ux-design 模組一&lt;/a>）描述每個畫面的狀態和轉換。Funnel 描述使用者跨畫面的操作路徑。兩者的對應是：funnel 的每一步通常對應一個畫面狀態的進入事件。&lt;/p>
&lt;p>狀態矩陣中的退出路徑（back 按鈕、取消操作）就是 funnel 的流失點。狀態矩陣的退出路徑為空 = UX 死胡同，funnel 分析中表現為「使用者進入後既沒完成也沒退出 — session 中斷」。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>不同使用者群體的行為差異 → &lt;a href="https://tarrragon.github.io/blog/monitoring/08-business-analytics/cohort-analysis/" data-link-title="Cohort Analysis" data-link-desc="按共同特徵分群、比較不同群體的留存率和行為差異 — 從「平均值」到「誰在用、誰離開了」">Cohort analysis&lt;/a>&lt;/li>
&lt;li>行為事件的設計 → &lt;a href="https://tarrragon.github.io/blog/monitoring/08-business-analytics/behavior-event-design/" data-link-title="行為事件設計" data-link-desc="事件命名規範、屬性設計、funnel 定義 — 行為分析的品質取決於事件設計的品質">行為事件設計&lt;/a>&lt;/li>
&lt;li>自架方案做 funnel → &lt;a href="https://tarrragon.github.io/blog/monitoring/08-business-analytics/self-hosted-funnel/" data-link-title="從 collector 資料做基礎 funnel 分析" data-link-desc="SQLite 層能做什麼程度的 funnel、PostgreSQL 層提供什麼進階能力、JSONL 匯出後的臨時分析">從 collector 資料做基礎 funnel 分析&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Funnel analysis 計算使用者在一連串步驟中每一步的轉換率，回答「使用者在哪一步離開」。流失最嚴重的步驟是優化投資報酬率最高的位置 — 修一個步驟的流失比優化所有步驟的體驗更有效。</p>
<h2 id="基本計算">基本計算</h2>
<p>Funnel 的每一步有兩個數字：進入人數和完成人數。轉換率 = 完成人數 / 進入人數。</p>
<p>以四步註冊 funnel 為例：</p>
<table>
  <thead>
      <tr>
          <th>步驟</th>
          <th>進入人數</th>
          <th>完成人數</th>
          <th>轉換率</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>看到註冊頁</td>
          <td>1000</td>
          <td>1000</td>
          <td>100%</td>
      </tr>
      <tr>
          <td>填寫表單</td>
          <td>1000</td>
          <td>620</td>
          <td>62%</td>
      </tr>
      <tr>
          <td>送出表單</td>
          <td>620</td>
          <td>580</td>
          <td>93.5%</td>
      </tr>
      <tr>
          <td>完成 email 驗證</td>
          <td>580</td>
          <td>310</td>
          <td>53.4%</td>
      </tr>
  </tbody>
</table>
<p>整體轉換率 = 310 / 1000 = 31%。但更有價值的資訊在每步的轉換率：步驟 2（填寫表單）流失 38%，步驟 4（email 驗證）流失 46.6%。這兩步是優化的優先目標。</p>
<h2 id="流失原因的區分">流失原因的區分</h2>
<p>Funnel analysis 指出「哪一步流失」，但不直接回答「為什麼流失」。流失原因需要結合其他資料推斷。</p>
<h3 id="設計問題導致的流失">設計問題導致的流失</h3>
<p>使用者看到表單但沒填寫（步驟 2 流失 38%）。可能原因：表單欄位太多、要求的資訊太敏感（信用卡號在註冊階段）、表單 UI 在特定裝置上有問題。</p>
<p>判斷方式：按平台、裝置、螢幕尺寸細分轉換率。如果 iOS 轉換率 70% 但 Android 只有 45%，可能是 Android 的表單 UI 有問題。</p>
<h3 id="技術問題導致的流失">技術問題導致的流失</h3>
<p>使用者送出表單但 email 驗證沒完成（步驟 4 流失 46.6%）。可能原因：驗證信被歸到垃圾郵件、驗證連結過期太快、驗證頁面載入失敗。</p>
<p>判斷方式：結合 error 事件。如果步驟 4 有大量 <code>signup.email.verify.failed</code> error，是技術問題；如果沒有 error 但流失高，使用者可能沒收到信或沒看到信。</p>
<h3 id="意圖問題導致的流失">意圖問題導致的流失</h3>
<p>使用者到了註冊頁但根本沒打算註冊 — 只是瀏覽。這類流失不是問題，是正常的使用者行為。</p>
<p>判斷方式：看使用者在流失步驟停留的時間。停留 &lt; 3 秒就離開，可能是誤點或沒有註冊意圖；停留 &gt; 30 秒但沒完成，可能是遇到障礙。</p>
<h2 id="funnel-的時間窗口">Funnel 的時間窗口</h2>
<p>同一個使用者在步驟 A 和步驟 B 之間隔了多久，仍算在同一個 funnel 內？時間窗口的設定影響 funnel 的計算結果。</p>
<p><strong>窗口太短</strong>：使用者中途離開稍後回來完成，被計為流失。Funnel 的流失率被高估。</p>
<p><strong>窗口太長</strong>：使用者今天瀏覽、一個月後被廣告重新帶回來完成，兩次獨立的意圖被合併成一個 funnel。轉換率被高估。</p>
<p>合理的窗口依業務場景而定：電商結帳 funnel 用 30 分鐘到 1 小時，SaaS onboarding 用 7 天，B2B 銷售漏斗用 30-90 天。</p>
<h2 id="畫面狀態矩陣和-funnel-的關係">畫面狀態矩陣和 funnel 的關係</h2>
<p><a href="/blog/ux-design/knowledge-cards/screen-state-matrix/" data-link-title="畫面狀態矩陣" data-link-desc="說明用四欄表格（顯示/可用操作/進入條件/退出路徑）系統性地暴露畫面導航缺口的設計工具">畫面狀態矩陣</a>（<a href="/blog/ux-design/01-screen-state-machine/" data-link-title="模組一：畫面狀態機設計" data-link-desc="畫面狀態矩陣（顯示 / 操作 / 進入 / 退出）— 退出路徑為空 = UX 死胡同">ux-design 模組一</a>）描述每個畫面的狀態和轉換。Funnel 描述使用者跨畫面的操作路徑。兩者的對應是：funnel 的每一步通常對應一個畫面狀態的進入事件。</p>
<p>狀態矩陣中的退出路徑（back 按鈕、取消操作）就是 funnel 的流失點。狀態矩陣的退出路徑為空 = UX 死胡同，funnel 分析中表現為「使用者進入後既沒完成也沒退出 — session 中斷」。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>不同使用者群體的行為差異 → <a href="/blog/monitoring/08-business-analytics/cohort-analysis/" data-link-title="Cohort Analysis" data-link-desc="按共同特徵分群、比較不同群體的留存率和行為差異 — 從「平均值」到「誰在用、誰離開了」">Cohort analysis</a></li>
<li>行為事件的設計 → <a href="/blog/monitoring/08-business-analytics/behavior-event-design/" data-link-title="行為事件設計" data-link-desc="事件命名規範、屬性設計、funnel 定義 — 行為分析的品質取決於事件設計的品質">行為事件設計</a></li>
<li>自架方案做 funnel → <a href="/blog/monitoring/08-business-analytics/self-hosted-funnel/" data-link-title="從 collector 資料做基礎 funnel 分析" data-link-desc="SQLite 層能做什麼程度的 funnel、PostgreSQL 層提供什麼進階能力、JSONL 匯出後的臨時分析">從 collector 資料做基礎 funnel 分析</a></li>
</ul>
]]></content:encoded></item><item><title>Cohort Analysis</title><link>https://tarrragon.github.io/blog/monitoring/08-business-analytics/cohort-analysis/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/monitoring/08-business-analytics/cohort-analysis/</guid><description>&lt;p>&lt;a href="https://tarrragon.github.io/blog/monitoring/knowledge-cards/cohort-analysis/" data-link-title="Cohort Analysis" data-link-desc="說明把使用者按共同特徵分群、比較不同群組行為差異的分析方法">Cohort analysis&lt;/a> 把使用者按共同特徵分群（cohort），比較不同群體在同一個指標上的表現差異。整體平均留存率 40% 可能隱藏了「1 月註冊的使用者留存 60%、3 月註冊的留存 20%」的差異。Cohort analysis 揭露平均值遮蔽的趨勢。&lt;/p>
&lt;h2 id="cohort-的定義方式">Cohort 的定義方式&lt;/h2>
&lt;h3 id="時間-cohort最常用">時間 cohort（最常用）&lt;/h3>
&lt;p>按使用者完成某個動作的時間分群。「1 月份註冊的使用者」「第 12 週 onboarding 完成的使用者」。&lt;/p>
&lt;p>時間 cohort 回答的問題：產品的留存率是否隨時間改善？新版本上線後註冊的使用者留存是否比舊版本高？&lt;/p>
&lt;h3 id="行為-cohort">行為 cohort&lt;/h3>
&lt;p>按使用者的行為特徵分群。「首次使用就完成購買的使用者」「使用過搜尋功能的使用者」「連續 3 天登入的使用者」。&lt;/p>
&lt;p>行為 cohort 回答的問題：哪些行為和留存相關？做了 X 的使用者留存率是否比沒做 X 的高？&lt;/p>
&lt;h3 id="屬性-cohort">屬性 cohort&lt;/h3>
&lt;p>按使用者的固有屬性分群。「iOS 使用者」「企業方案使用者」「來自特定廣告渠道的使用者」。&lt;/p>
&lt;p>屬性 cohort 回答的問題：不同平台/方案/來源的使用者行為是否不同？&lt;/p>
&lt;h2 id="留存率矩陣">留存率矩陣&lt;/h2>
&lt;p>留存率矩陣是 cohort analysis 最常見的呈現方式。每行代表一個 cohort（例如某月註冊的使用者），每列代表註冊後的第 N 天/週/月，格中的值是該 cohort 在第 N 期仍活躍的比例。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Cohort&lt;/th>
 &lt;th>第 0 週&lt;/th>
 &lt;th>第 1 週&lt;/th>
 &lt;th>第 2 週&lt;/th>
 &lt;th>第 4 週&lt;/th>
 &lt;th>第 8 週&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>1 月&lt;/td>
 &lt;td>100%&lt;/td>
 &lt;td>45%&lt;/td>
 &lt;td>32%&lt;/td>
 &lt;td>22%&lt;/td>
 &lt;td>18%&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>2 月&lt;/td>
 &lt;td>100%&lt;/td>
 &lt;td>48%&lt;/td>
 &lt;td>35%&lt;/td>
 &lt;td>25%&lt;/td>
 &lt;td>20%&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>3 月&lt;/td>
 &lt;td>100%&lt;/td>
 &lt;td>52%&lt;/td>
 &lt;td>40%&lt;/td>
 &lt;td>30%&lt;/td>
 &lt;td>—&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>從這張矩陣可以看到：留存率逐月改善（1 月 → 3 月的第 1 週留存從 45% 升到 52%）。如果 2 月有產品改版，這個改善可能和改版相關。&lt;/p>
&lt;h2 id="cohort-analysis-的判讀">Cohort analysis 的判讀&lt;/h2>
&lt;h3 id="自然衰減-vs-產品問題">自然衰減 vs 產品問題&lt;/h3>
&lt;p>所有產品都有自然衰減 — 使用者隨時間減少是正常的。Cohort analysis 的價值在於區分「正常衰減」和「異常衰減」。&lt;/p>
&lt;p>如果所有 cohort 的衰減曲線形狀相似，衰減是產品層面的結構性問題（例如缺少持續使用的理由）。如果某個 cohort 的衰減明顯比其他 cohort 快，需要調查該 cohort 的特殊情況（當時的產品版本、市場環境、使用者來源）。&lt;/p>
&lt;h3 id="穩態留存">穩態留存&lt;/h3>
&lt;p>留存率通常在某個時間點後趨於穩定 — 留下來的使用者不再大量流失。穩態留存的百分比和到達穩態的時間是產品健康度的核心指標。&lt;/p>
&lt;p>穩態留存高但到達時間長 = 產品有價值但 onboarding 需要改善。穩態留存低 = 產品的持續使用價值不足。&lt;/p>
&lt;h2 id="和-funnel-的關係">和 funnel 的關係&lt;/h2>
&lt;p>Funnel analysis 回答「使用者在哪一步流失」（單次流程），cohort analysis 回答「使用者是否持續回來」（長期行為）。兩者互補：funnel 改善單次流程的轉換率，cohort 追蹤改善是否帶來長期留存的變化。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>使用者從哪來 → &lt;a href="https://tarrragon.github.io/blog/monitoring/08-business-analytics/attribution/" data-link-title="Attribution" data-link-desc="使用者從哪來、哪個渠道帶來轉換 — last-touch / first-touch / multi-touch 歸因模型的差異和選擇">Attribution&lt;/a>&lt;/li>
&lt;li>單次流程的流失分析 → &lt;a href="https://tarrragon.github.io/blog/monitoring/08-business-analytics/funnel-analysis/" data-link-title="Funnel Analysis" data-link-desc="使用者在哪一步流失 — 從事件序列計算每步轉換率、找出流失最嚴重的步驟、區分設計問題和技術問題">Funnel analysis&lt;/a>&lt;/li>
&lt;li>使用者分群的工程實作 → &lt;a href="https://tarrragon.github.io/blog/monitoring/08-business-analytics/rfm-segmentation/" data-link-title="RFM 分群" data-link-desc="Recency / Frequency / Monetary 三維度的使用者分群 — 從行為事件計算 RFM 分數、定義使用者群體、驅動差異化策略">RFM 分群&lt;/a>&lt;/li>
&lt;li>客戶終身價值 → &lt;a href="https://tarrragon.github.io/blog/business/knowledge-cards/ltv/" data-link-title="LTV" data-link-desc="說明客戶終身價值與其在估值中的作用">LTV&lt;/a>&lt;/li>
&lt;li>留存率 → &lt;a href="https://tarrragon.github.io/blog/business/knowledge-cards/retention/" data-link-title="Retention" data-link-desc="說明客戶留存率與其對單位經濟的決定作用">Retention&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p><a href="/blog/monitoring/knowledge-cards/cohort-analysis/" data-link-title="Cohort Analysis" data-link-desc="說明把使用者按共同特徵分群、比較不同群組行為差異的分析方法">Cohort analysis</a> 把使用者按共同特徵分群（cohort），比較不同群體在同一個指標上的表現差異。整體平均留存率 40% 可能隱藏了「1 月註冊的使用者留存 60%、3 月註冊的留存 20%」的差異。Cohort analysis 揭露平均值遮蔽的趨勢。</p>
<h2 id="cohort-的定義方式">Cohort 的定義方式</h2>
<h3 id="時間-cohort最常用">時間 cohort（最常用）</h3>
<p>按使用者完成某個動作的時間分群。「1 月份註冊的使用者」「第 12 週 onboarding 完成的使用者」。</p>
<p>時間 cohort 回答的問題：產品的留存率是否隨時間改善？新版本上線後註冊的使用者留存是否比舊版本高？</p>
<h3 id="行為-cohort">行為 cohort</h3>
<p>按使用者的行為特徵分群。「首次使用就完成購買的使用者」「使用過搜尋功能的使用者」「連續 3 天登入的使用者」。</p>
<p>行為 cohort 回答的問題：哪些行為和留存相關？做了 X 的使用者留存率是否比沒做 X 的高？</p>
<h3 id="屬性-cohort">屬性 cohort</h3>
<p>按使用者的固有屬性分群。「iOS 使用者」「企業方案使用者」「來自特定廣告渠道的使用者」。</p>
<p>屬性 cohort 回答的問題：不同平台/方案/來源的使用者行為是否不同？</p>
<h2 id="留存率矩陣">留存率矩陣</h2>
<p>留存率矩陣是 cohort analysis 最常見的呈現方式。每行代表一個 cohort（例如某月註冊的使用者），每列代表註冊後的第 N 天/週/月，格中的值是該 cohort 在第 N 期仍活躍的比例。</p>
<table>
  <thead>
      <tr>
          <th>Cohort</th>
          <th>第 0 週</th>
          <th>第 1 週</th>
          <th>第 2 週</th>
          <th>第 4 週</th>
          <th>第 8 週</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>1 月</td>
          <td>100%</td>
          <td>45%</td>
          <td>32%</td>
          <td>22%</td>
          <td>18%</td>
      </tr>
      <tr>
          <td>2 月</td>
          <td>100%</td>
          <td>48%</td>
          <td>35%</td>
          <td>25%</td>
          <td>20%</td>
      </tr>
      <tr>
          <td>3 月</td>
          <td>100%</td>
          <td>52%</td>
          <td>40%</td>
          <td>30%</td>
          <td>—</td>
      </tr>
  </tbody>
</table>
<p>從這張矩陣可以看到：留存率逐月改善（1 月 → 3 月的第 1 週留存從 45% 升到 52%）。如果 2 月有產品改版，這個改善可能和改版相關。</p>
<h2 id="cohort-analysis-的判讀">Cohort analysis 的判讀</h2>
<h3 id="自然衰減-vs-產品問題">自然衰減 vs 產品問題</h3>
<p>所有產品都有自然衰減 — 使用者隨時間減少是正常的。Cohort analysis 的價值在於區分「正常衰減」和「異常衰減」。</p>
<p>如果所有 cohort 的衰減曲線形狀相似，衰減是產品層面的結構性問題（例如缺少持續使用的理由）。如果某個 cohort 的衰減明顯比其他 cohort 快，需要調查該 cohort 的特殊情況（當時的產品版本、市場環境、使用者來源）。</p>
<h3 id="穩態留存">穩態留存</h3>
<p>留存率通常在某個時間點後趨於穩定 — 留下來的使用者不再大量流失。穩態留存的百分比和到達穩態的時間是產品健康度的核心指標。</p>
<p>穩態留存高但到達時間長 = 產品有價值但 onboarding 需要改善。穩態留存低 = 產品的持續使用價值不足。</p>
<h2 id="和-funnel-的關係">和 funnel 的關係</h2>
<p>Funnel analysis 回答「使用者在哪一步流失」（單次流程），cohort analysis 回答「使用者是否持續回來」（長期行為）。兩者互補：funnel 改善單次流程的轉換率，cohort 追蹤改善是否帶來長期留存的變化。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>使用者從哪來 → <a href="/blog/monitoring/08-business-analytics/attribution/" data-link-title="Attribution" data-link-desc="使用者從哪來、哪個渠道帶來轉換 — last-touch / first-touch / multi-touch 歸因模型的差異和選擇">Attribution</a></li>
<li>單次流程的流失分析 → <a href="/blog/monitoring/08-business-analytics/funnel-analysis/" data-link-title="Funnel Analysis" data-link-desc="使用者在哪一步流失 — 從事件序列計算每步轉換率、找出流失最嚴重的步驟、區分設計問題和技術問題">Funnel analysis</a></li>
<li>使用者分群的工程實作 → <a href="/blog/monitoring/08-business-analytics/rfm-segmentation/" data-link-title="RFM 分群" data-link-desc="Recency / Frequency / Monetary 三維度的使用者分群 — 從行為事件計算 RFM 分數、定義使用者群體、驅動差異化策略">RFM 分群</a></li>
<li>客戶終身價值 → <a href="/blog/business/knowledge-cards/ltv/" data-link-title="LTV" data-link-desc="說明客戶終身價值與其在估值中的作用">LTV</a></li>
<li>留存率 → <a href="/blog/business/knowledge-cards/retention/" data-link-title="Retention" data-link-desc="說明客戶留存率與其對單位經濟的決定作用">Retention</a></li>
</ul>
]]></content:encoded></item><item><title>Attribution</title><link>https://tarrragon.github.io/blog/monitoring/08-business-analytics/attribution/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/monitoring/08-business-analytics/attribution/</guid><description>&lt;p>Attribution（歸因）回答「使用者的轉換應該歸功於哪個渠道或觸點」。使用者可能先看到 Facebook 廣告、再 Google 搜尋、最後直接輸入網址完成購買 — 三個渠道都接觸了使用者，轉換功勞歸誰決定了行銷預算的分配。&lt;/p>
&lt;h2 id="歸因模型">歸因模型&lt;/h2>
&lt;h3 id="last-touch-attribution">Last-touch attribution&lt;/h3>
&lt;p>把轉換功勞全部歸給使用者轉換前最後接觸的渠道。上例中功勞歸「直接輸入網址」。&lt;/p>
&lt;p>優點：實作最簡單 — 只需要記錄轉換事件的 referrer 或 UTM 參數。&lt;/p>
&lt;p>缺點：忽略了前面渠道的貢獻。Facebook 廣告讓使用者第一次知道產品，但在 last-touch 模型中功勞為零。長期使用 last-touch 會導致行銷預算過度集中在「最後一步」渠道（品牌搜尋、直接訪問），低估「認知階段」渠道（展示廣告、社群媒體）。&lt;/p>
&lt;h3 id="first-touch-attribution">First-touch attribution&lt;/h3>
&lt;p>把轉換功勞全部歸給使用者第一次接觸的渠道。上例中功勞歸 Facebook 廣告。&lt;/p>
&lt;p>優點：強調「獲客」渠道的貢獻，適合評估品牌認知和獲客效率。&lt;/p>
&lt;p>缺點：忽略了後續渠道的推進作用。使用者第一次看到廣告但沒行動，可能是後續的 Google 搜尋才促成轉換。&lt;/p>
&lt;h3 id="multi-touch-attribution">Multi-touch attribution&lt;/h3>
&lt;p>把轉換功勞分配給使用者轉換路徑上的所有渠道。分配方式有多種：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>線性歸因&lt;/strong>：每個渠道平均分配。三個渠道各得 33.3%。&lt;/li>
&lt;li>&lt;strong>時間衰減&lt;/strong>：離轉換越近的渠道得到越多功勞。&lt;/li>
&lt;li>&lt;strong>Position-based（U 型）&lt;/strong>：第一個和最後一個渠道各得 40%，中間渠道分 20%。&lt;/li>
&lt;li>&lt;strong>資料驅動（data-driven）&lt;/strong>：用機器學習模型從歷史資料學習每個渠道的貢獻。需要大量資料。&lt;/li>
&lt;/ul>
&lt;h2 id="技術實作">技術實作&lt;/h2>
&lt;p>Attribution 的技術實作需要解決兩個問題：跨 session 的使用者識別，和觸點的記錄。&lt;/p>
&lt;h3 id="跨-session-識別">跨 session 識別&lt;/h3>
&lt;p>同一個使用者在不同 session、不同裝置、不同瀏覽器上的行為需要關聯到同一個人。&lt;/p>
&lt;p>Web 端用 cookie（first-party）或 login ID 關聯。Mobile 端用 device ID 或 login ID。跨裝置關聯需要使用者登入 — 未登入的使用者在不同裝置上是不同的匿名 ID。&lt;/p>
&lt;h3 id="觸點記錄">觸點記錄&lt;/h3>
&lt;p>每次使用者接觸產品的渠道需要記錄。Web 端記錄 referrer、UTM 參數（&lt;code>utm_source&lt;/code>、&lt;code>utm_medium&lt;/code>、&lt;code>utm_campaign&lt;/code>）。Mobile 端記錄 deep link 參數、app store 來源（需要 attribution SDK 如 AppsFlyer、Adjust）。&lt;/p>
&lt;h2 id="自架方案的歸因能力">自架方案的歸因能力&lt;/h2>
&lt;p>自架 collector 能做基礎的 last-touch attribution — 在轉換事件的屬性中記錄 referrer 和 UTM 參數。&lt;/p>
&lt;p>Multi-touch attribution 需要跨 session 的使用者行為歷史，實作複雜度顯著上升。如果 multi-touch 是核心需求，商業方案（GA4、Mixpanel、AppsFlyer）通常比自架更實用。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>A/B test 驗證渠道效果 → &lt;a href="https://tarrragon.github.io/blog/monitoring/08-business-analytics/ab-test-statistics/" data-link-title="A/B Test 的統計基礎" data-link-desc="假設檢定、樣本量計算、多重比較校正 — A/B test 不只是「比較兩個數字」，統計方法決定結論是否可靠">A/B test 的統計基礎&lt;/a>&lt;/li>
&lt;li>使用者分群 → &lt;a href="https://tarrragon.github.io/blog/monitoring/08-business-analytics/cohort-analysis/" data-link-title="Cohort Analysis" data-link-desc="按共同特徵分群、比較不同群體的留存率和行為差異 — 從「平均值」到「誰在用、誰離開了」">Cohort analysis&lt;/a>&lt;/li>
&lt;li>行為事件設計 → &lt;a href="https://tarrragon.github.io/blog/monitoring/08-business-analytics/behavior-event-design/" data-link-title="行為事件設計" data-link-desc="事件命名規範、屬性設計、funnel 定義 — 行為分析的品質取決於事件設計的品質">行為事件設計&lt;/a>&lt;/li>
&lt;li>客戶取得成本 → &lt;a href="https://tarrragon.github.io/blog/business/knowledge-cards/cac/" data-link-title="CAC" data-link-desc="說明獲客成本及其對商業模式可行性的決定作用">CAC&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Attribution（歸因）回答「使用者的轉換應該歸功於哪個渠道或觸點」。使用者可能先看到 Facebook 廣告、再 Google 搜尋、最後直接輸入網址完成購買 — 三個渠道都接觸了使用者，轉換功勞歸誰決定了行銷預算的分配。</p>
<h2 id="歸因模型">歸因模型</h2>
<h3 id="last-touch-attribution">Last-touch attribution</h3>
<p>把轉換功勞全部歸給使用者轉換前最後接觸的渠道。上例中功勞歸「直接輸入網址」。</p>
<p>優點：實作最簡單 — 只需要記錄轉換事件的 referrer 或 UTM 參數。</p>
<p>缺點：忽略了前面渠道的貢獻。Facebook 廣告讓使用者第一次知道產品，但在 last-touch 模型中功勞為零。長期使用 last-touch 會導致行銷預算過度集中在「最後一步」渠道（品牌搜尋、直接訪問），低估「認知階段」渠道（展示廣告、社群媒體）。</p>
<h3 id="first-touch-attribution">First-touch attribution</h3>
<p>把轉換功勞全部歸給使用者第一次接觸的渠道。上例中功勞歸 Facebook 廣告。</p>
<p>優點：強調「獲客」渠道的貢獻，適合評估品牌認知和獲客效率。</p>
<p>缺點：忽略了後續渠道的推進作用。使用者第一次看到廣告但沒行動，可能是後續的 Google 搜尋才促成轉換。</p>
<h3 id="multi-touch-attribution">Multi-touch attribution</h3>
<p>把轉換功勞分配給使用者轉換路徑上的所有渠道。分配方式有多種：</p>
<ul>
<li><strong>線性歸因</strong>：每個渠道平均分配。三個渠道各得 33.3%。</li>
<li><strong>時間衰減</strong>：離轉換越近的渠道得到越多功勞。</li>
<li><strong>Position-based（U 型）</strong>：第一個和最後一個渠道各得 40%，中間渠道分 20%。</li>
<li><strong>資料驅動（data-driven）</strong>：用機器學習模型從歷史資料學習每個渠道的貢獻。需要大量資料。</li>
</ul>
<h2 id="技術實作">技術實作</h2>
<p>Attribution 的技術實作需要解決兩個問題：跨 session 的使用者識別，和觸點的記錄。</p>
<h3 id="跨-session-識別">跨 session 識別</h3>
<p>同一個使用者在不同 session、不同裝置、不同瀏覽器上的行為需要關聯到同一個人。</p>
<p>Web 端用 cookie（first-party）或 login ID 關聯。Mobile 端用 device ID 或 login ID。跨裝置關聯需要使用者登入 — 未登入的使用者在不同裝置上是不同的匿名 ID。</p>
<h3 id="觸點記錄">觸點記錄</h3>
<p>每次使用者接觸產品的渠道需要記錄。Web 端記錄 referrer、UTM 參數（<code>utm_source</code>、<code>utm_medium</code>、<code>utm_campaign</code>）。Mobile 端記錄 deep link 參數、app store 來源（需要 attribution SDK 如 AppsFlyer、Adjust）。</p>
<h2 id="自架方案的歸因能力">自架方案的歸因能力</h2>
<p>自架 collector 能做基礎的 last-touch attribution — 在轉換事件的屬性中記錄 referrer 和 UTM 參數。</p>
<p>Multi-touch attribution 需要跨 session 的使用者行為歷史，實作複雜度顯著上升。如果 multi-touch 是核心需求，商業方案（GA4、Mixpanel、AppsFlyer）通常比自架更實用。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>A/B test 驗證渠道效果 → <a href="/blog/monitoring/08-business-analytics/ab-test-statistics/" data-link-title="A/B Test 的統計基礎" data-link-desc="假設檢定、樣本量計算、多重比較校正 — A/B test 不只是「比較兩個數字」，統計方法決定結論是否可靠">A/B test 的統計基礎</a></li>
<li>使用者分群 → <a href="/blog/monitoring/08-business-analytics/cohort-analysis/" data-link-title="Cohort Analysis" data-link-desc="按共同特徵分群、比較不同群體的留存率和行為差異 — 從「平均值」到「誰在用、誰離開了」">Cohort analysis</a></li>
<li>行為事件設計 → <a href="/blog/monitoring/08-business-analytics/behavior-event-design/" data-link-title="行為事件設計" data-link-desc="事件命名規範、屬性設計、funnel 定義 — 行為分析的品質取決於事件設計的品質">行為事件設計</a></li>
<li>客戶取得成本 → <a href="/blog/business/knowledge-cards/cac/" data-link-title="CAC" data-link-desc="說明獲客成本及其對商業模式可行性的決定作用">CAC</a></li>
</ul>
]]></content:encoded></item><item><title>A/B Test 的統計基礎</title><link>https://tarrragon.github.io/blog/monitoring/08-business-analytics/ab-test-statistics/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/monitoring/08-business-analytics/ab-test-statistics/</guid><description>&lt;p>A/B test 把使用者隨機分成兩組，一組看到原版（control），一組看到改版（treatment），比較兩組的指標差異。統計方法的角色是判斷「觀察到的差異是真實的還是隨機波動」。&lt;/p>
&lt;h2 id="假設檢定">假設檢定&lt;/h2>
&lt;h3 id="虛無假設和對立假設">虛無假設和對立假設&lt;/h3>
&lt;p>虛無假設（H0）：兩組沒有差異，觀察到的差異來自隨機波動。對立假設（H1）：兩組有真實差異。&lt;/p>
&lt;p>A/B test 的邏輯是：假設 H0 成立（兩組沒有差異），計算「在 H0 成立的前提下，觀察到目前這麼大的差異的機率」。如果這個機率（p-value）很小（通常 &amp;lt; 0.05），拒絕 H0，接受 H1。&lt;/p>
&lt;h3 id="p-value-的意義">p-value 的意義&lt;/h3>
&lt;p>p-value = 0.03 代表「假設兩組沒有差異，觀察到目前差異的機率是 3%」。這個機率足夠小，合理推斷差異是真實的。&lt;/p>
&lt;p>p-value 不代表「改版比原版好的機率是 97%」。p-value 是在 H0 成立的條件下計算的，不是改版效果的機率。&lt;/p>
&lt;h3 id="兩類錯誤">兩類錯誤&lt;/h3>
&lt;p>&lt;strong>Type I error（偽陽性）&lt;/strong>：實際上沒有差異，但統計結果判定有差異。機率由顯著性水準 α 控制，通常設 0.05。&lt;/p>
&lt;p>&lt;strong>Type II error（偽陰性）&lt;/strong>：實際上有差異，但統計結果判定沒有差異。機率由統計檢定力（power = 1 - β）控制，通常要求 power ≥ 0.8。&lt;/p>
&lt;h2 id="樣本量計算">樣本量計算&lt;/h2>
&lt;p>樣本量決定了 A/B test 能偵測到多小的差異。樣本量太小，即使改版有效果，test 也沒有足夠的統計檢定力偵測到。&lt;/p>
&lt;p>樣本量計算需要四個參數：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>基準轉換率&lt;/strong>：control 組目前的轉換率（例如 5%）&lt;/li>
&lt;li>&lt;strong>最小可偵測效果（MDE）&lt;/strong>：想偵測到的最小差異（例如 5% → 6%，相對提升 20%）&lt;/li>
&lt;li>&lt;strong>顯著性水準 α&lt;/strong>：通常 0.05&lt;/li>
&lt;li>&lt;strong>統計檢定力 1 - β&lt;/strong>：通常 0.8&lt;/li>
&lt;/ul>
&lt;p>以基準轉換率 5%、MDE 相對提升 20%（5% → 6%）、α = 0.05、power = 0.8 為例，每組需要約 14,500 個樣本。如果每天有 1,000 個使用者，需要跑 29 天。&lt;/p>
&lt;p>樣本量不足時的常見錯誤是「提早看結果」— 跑了 3 天看到 p &amp;lt; 0.05 就停止。提早停止會膨脹 Type I error 率，因為隨機波動在小樣本中更容易產生看似顯著的差異。&lt;/p>
&lt;h2 id="多重比較">多重比較&lt;/h2>
&lt;p>同時跑多個 A/B test 或測試多個變體（A/B/C/D）時，整體的 Type I error 率會膨脹。&lt;/p>
&lt;p>跑 20 個 test，即使所有 test 的 H0 都成立（沒有真實差異），預期有 1 個 test（20 × 0.05）會出現 p &amp;lt; 0.05 的偽陽性。&lt;/p>
&lt;h3 id="bonferroni-校正">Bonferroni 校正&lt;/h3>
&lt;p>最簡單的校正方式：把顯著性水準除以測試數量。跑 5 個 test，每個 test 的顯著性水準改為 0.05 / 5 = 0.01。&lt;/p>
&lt;p>Bonferroni 校正很保守 — 降低了偽陽性但也降低了統計檢定力，可能錯過真實的差異。&lt;/p>
&lt;h3 id="false-discovery-ratefdr">False Discovery Rate（FDR）&lt;/h3>
&lt;p>Benjamini-Hochberg 方法控制的是「被判為顯著的結果中偽陽性的比例」，比 Bonferroni 更寬鬆。適合探索性分析（同時測試多個指標，容許一些偽陽性）。&lt;/p>
&lt;h2 id="ab-test-在自架方案的可行性">A/B test 在自架方案的可行性&lt;/h2>
&lt;p>自架 collector 可以做基礎的 A/B test 分析 — 在行為事件中記錄使用者的分組（&lt;code>variant: &amp;quot;control&amp;quot;&lt;/code> / &lt;code>variant: &amp;quot;treatment&amp;quot;&lt;/code>），計算每組的轉換率，用統計檢定比較差異。&lt;/p>
&lt;p>統計計算（p-value、信賴區間）可以用 Python（scipy.stats）或 R 完成。不需要商業 A/B test 平台。&lt;/p>
&lt;p>商業 A/B test 平台（Optimizely、LaunchDarkly、Firebase Remote Config）額外提供的是：隨機分組管理、提早停止的統計保護（sequential testing）、多變體管理的 UI、和其他分析工具的整合。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>推薦系統概論 → &lt;a href="https://tarrragon.github.io/blog/monitoring/08-business-analytics/recommendation-overview/" data-link-title="推薦系統概論" data-link-desc="Collaborative filtering / content-based / 混合方法 — 推薦系統的三種基本架構和各自的資料需求">推薦系統概論&lt;/a>&lt;/li>
&lt;li>使用者分群 → &lt;a href="https://tarrragon.github.io/blog/monitoring/08-business-analytics/rfm-segmentation/" data-link-title="RFM 分群" data-link-desc="Recency / Frequency / Monetary 三維度的使用者分群 — 從行為事件計算 RFM 分數、定義使用者群體、驅動差異化策略">RFM 分群&lt;/a>&lt;/li>
&lt;li>行為事件設計 → &lt;a href="https://tarrragon.github.io/blog/monitoring/08-business-analytics/behavior-event-design/" data-link-title="行為事件設計" data-link-desc="事件命名規範、屬性設計、funnel 定義 — 行為分析的品質取決於事件設計的品質">行為事件設計&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>A/B test 把使用者隨機分成兩組，一組看到原版（control），一組看到改版（treatment），比較兩組的指標差異。統計方法的角色是判斷「觀察到的差異是真實的還是隨機波動」。</p>
<h2 id="假設檢定">假設檢定</h2>
<h3 id="虛無假設和對立假設">虛無假設和對立假設</h3>
<p>虛無假設（H0）：兩組沒有差異，觀察到的差異來自隨機波動。對立假設（H1）：兩組有真實差異。</p>
<p>A/B test 的邏輯是：假設 H0 成立（兩組沒有差異），計算「在 H0 成立的前提下，觀察到目前這麼大的差異的機率」。如果這個機率（p-value）很小（通常 &lt; 0.05），拒絕 H0，接受 H1。</p>
<h3 id="p-value-的意義">p-value 的意義</h3>
<p>p-value = 0.03 代表「假設兩組沒有差異，觀察到目前差異的機率是 3%」。這個機率足夠小，合理推斷差異是真實的。</p>
<p>p-value 不代表「改版比原版好的機率是 97%」。p-value 是在 H0 成立的條件下計算的，不是改版效果的機率。</p>
<h3 id="兩類錯誤">兩類錯誤</h3>
<p><strong>Type I error（偽陽性）</strong>：實際上沒有差異，但統計結果判定有差異。機率由顯著性水準 α 控制，通常設 0.05。</p>
<p><strong>Type II error（偽陰性）</strong>：實際上有差異，但統計結果判定沒有差異。機率由統計檢定力（power = 1 - β）控制，通常要求 power ≥ 0.8。</p>
<h2 id="樣本量計算">樣本量計算</h2>
<p>樣本量決定了 A/B test 能偵測到多小的差異。樣本量太小，即使改版有效果，test 也沒有足夠的統計檢定力偵測到。</p>
<p>樣本量計算需要四個參數：</p>
<ul>
<li><strong>基準轉換率</strong>：control 組目前的轉換率（例如 5%）</li>
<li><strong>最小可偵測效果（MDE）</strong>：想偵測到的最小差異（例如 5% → 6%，相對提升 20%）</li>
<li><strong>顯著性水準 α</strong>：通常 0.05</li>
<li><strong>統計檢定力 1 - β</strong>：通常 0.8</li>
</ul>
<p>以基準轉換率 5%、MDE 相對提升 20%（5% → 6%）、α = 0.05、power = 0.8 為例，每組需要約 14,500 個樣本。如果每天有 1,000 個使用者，需要跑 29 天。</p>
<p>樣本量不足時的常見錯誤是「提早看結果」— 跑了 3 天看到 p &lt; 0.05 就停止。提早停止會膨脹 Type I error 率，因為隨機波動在小樣本中更容易產生看似顯著的差異。</p>
<h2 id="多重比較">多重比較</h2>
<p>同時跑多個 A/B test 或測試多個變體（A/B/C/D）時，整體的 Type I error 率會膨脹。</p>
<p>跑 20 個 test，即使所有 test 的 H0 都成立（沒有真實差異），預期有 1 個 test（20 × 0.05）會出現 p &lt; 0.05 的偽陽性。</p>
<h3 id="bonferroni-校正">Bonferroni 校正</h3>
<p>最簡單的校正方式：把顯著性水準除以測試數量。跑 5 個 test，每個 test 的顯著性水準改為 0.05 / 5 = 0.01。</p>
<p>Bonferroni 校正很保守 — 降低了偽陽性但也降低了統計檢定力，可能錯過真實的差異。</p>
<h3 id="false-discovery-ratefdr">False Discovery Rate（FDR）</h3>
<p>Benjamini-Hochberg 方法控制的是「被判為顯著的結果中偽陽性的比例」，比 Bonferroni 更寬鬆。適合探索性分析（同時測試多個指標，容許一些偽陽性）。</p>
<h2 id="ab-test-在自架方案的可行性">A/B test 在自架方案的可行性</h2>
<p>自架 collector 可以做基礎的 A/B test 分析 — 在行為事件中記錄使用者的分組（<code>variant: &quot;control&quot;</code> / <code>variant: &quot;treatment&quot;</code>），計算每組的轉換率，用統計檢定比較差異。</p>
<p>統計計算（p-value、信賴區間）可以用 Python（scipy.stats）或 R 完成。不需要商業 A/B test 平台。</p>
<p>商業 A/B test 平台（Optimizely、LaunchDarkly、Firebase Remote Config）額外提供的是：隨機分組管理、提早停止的統計保護（sequential testing）、多變體管理的 UI、和其他分析工具的整合。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>推薦系統概論 → <a href="/blog/monitoring/08-business-analytics/recommendation-overview/" data-link-title="推薦系統概論" data-link-desc="Collaborative filtering / content-based / 混合方法 — 推薦系統的三種基本架構和各自的資料需求">推薦系統概論</a></li>
<li>使用者分群 → <a href="/blog/monitoring/08-business-analytics/rfm-segmentation/" data-link-title="RFM 分群" data-link-desc="Recency / Frequency / Monetary 三維度的使用者分群 — 從行為事件計算 RFM 分數、定義使用者群體、驅動差異化策略">RFM 分群</a></li>
<li>行為事件設計 → <a href="/blog/monitoring/08-business-analytics/behavior-event-design/" data-link-title="行為事件設計" data-link-desc="事件命名規範、屬性設計、funnel 定義 — 行為分析的品質取決於事件設計的品質">行為事件設計</a></li>
</ul>
]]></content:encoded></item><item><title>推薦系統概論</title><link>https://tarrragon.github.io/blog/monitoring/08-business-analytics/recommendation-overview/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/monitoring/08-business-analytics/recommendation-overview/</guid><description>&lt;p>推薦系統從使用者的歷史行為或物品的內容特徵推斷使用者可能感興趣的物品。三種基本方法各自依賴不同的資料：collaborative filtering 依賴使用者行為矩陣，content-based 依賴物品特徵，混合方法結合兩者。&lt;/p>
&lt;h2 id="collaborative-filtering">Collaborative Filtering&lt;/h2>
&lt;p>Collaborative filtering 的核心假設是「和你行為相似的人喜歡的東西，你也可能喜歡」。它不分析物品的內容，只看使用者的行為模式。&lt;/p>
&lt;h3 id="user-based">User-based&lt;/h3>
&lt;p>找到和目標使用者行為最相似的一群人（neighbors），把 neighbors 互動過但目標使用者沒互動過的物品推薦給目標使用者。&lt;/p>
&lt;p>資料需求：使用者-物品互動矩陣（user × item matrix），每格是評分、點擊、購買等互動訊號。&lt;/p>
&lt;p>挑戰：使用者數量增加時，計算使用者相似度的成本呈平方增長。冷啟動問題 — 新使用者沒有足夠的互動歷史來找到 neighbors。&lt;/p>
&lt;h3 id="item-based">Item-based&lt;/h3>
&lt;p>計算物品之間的相似度（根據哪些使用者同時互動過這兩個物品），推薦和使用者已互動物品相似的物品。&lt;/p>
&lt;p>資料需求：同上，但相似度計算的維度是物品而非使用者。&lt;/p>
&lt;p>優勢：物品的相似度矩陣比使用者相似度穩定（物品不會突然改變行為，使用者會），可以離線計算和快取。Amazon 的「購買此商品的人也買了」就是 item-based collaborative filtering。&lt;/p>
&lt;h2 id="content-based-filtering">Content-based Filtering&lt;/h2>
&lt;p>Content-based filtering 分析物品的內容特徵，推薦和使用者過去喜歡的物品內容相似的物品。&lt;/p>
&lt;p>資料需求：每個物品的特徵向量（genre、author、price range、keywords）和使用者的偏好 profile（從歷史互動推斷）。&lt;/p>
&lt;p>優勢：不依賴其他使用者的行為 — 單一使用者就能產生推薦。新物品只要有特徵描述就能被推薦（解決 collaborative filtering 的新物品冷啟動）。&lt;/p>
&lt;p>挑戰：推薦結果傾向和使用者歷史行為相似，缺乏意外發現（serendipity）。特徵工程的品質直接影響推薦品質 — 物品的特徵描述不完整或不準確，推薦就不準確。&lt;/p>
&lt;h2 id="混合方法">混合方法&lt;/h2>
&lt;p>結合 collaborative filtering 和 content-based 的優勢，減少各自的弱點。&lt;/p>
&lt;h3 id="加權混合">加權混合&lt;/h3>
&lt;p>兩種方法各自產生推薦清單，用加權分數合併。權重可以固定，也可以根據情境動態調整（新使用者偏重 content-based，老使用者偏重 collaborative filtering）。&lt;/p>
&lt;h3 id="特徵增強">特徵增強&lt;/h3>
&lt;p>用 content-based 的特徵增強 collaborative filtering 的矩陣。使用者-物品互動矩陣加上物品的內容特徵，讓相似度計算同時考慮行為和內容。&lt;/p>
&lt;h3 id="級聯">級聯&lt;/h3>
&lt;p>先用一種方法粗篩，再用另一種方法排序。Collaborative filtering 產生候選清單，content-based 根據使用者的內容偏好排序。&lt;/p>
&lt;h2 id="行為事件在推薦系統的角色">行為事件在推薦系統的角色&lt;/h2>
&lt;p>推薦系統的輸入是使用者的互動行為 — 瀏覽、點擊、加入購物車、購買、評分。這些互動行為就是行為事件（&lt;a href="https://tarrragon.github.io/blog/monitoring/01-mental-model/" data-link-title="模組一：監控心智模型" data-link-desc="四類事件（event / error / metric / lifecycle）的分類與收集策略">模組一 心智模型&lt;/a>）的 event 類型。&lt;/p>
&lt;p>行為事件的設計直接影響推薦系統的資料品質。事件的粒度決定了推薦的精細度 — 只記錄「頁面瀏覽」比記錄「頁面瀏覽 + 停留時間 + 滾動深度」的推薦信號弱。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>使用者分群的工程實作 → &lt;a href="https://tarrragon.github.io/blog/monitoring/08-business-analytics/rfm-segmentation/" data-link-title="RFM 分群" data-link-desc="Recency / Frequency / Monetary 三維度的使用者分群 — 從行為事件計算 RFM 分數、定義使用者群體、驅動差異化策略">RFM 分群&lt;/a>&lt;/li>
&lt;li>行為事件設計 → &lt;a href="https://tarrragon.github.io/blog/monitoring/08-business-analytics/behavior-event-design/" data-link-title="行為事件設計" data-link-desc="事件命名規範、屬性設計、funnel 定義 — 行為分析的品質取決於事件設計的品質">行為事件設計&lt;/a>&lt;/li>
&lt;li>自架方案的分析能力邊界 → &lt;a href="https://tarrragon.github.io/blog/monitoring/08-business-analytics/self-hosted-funnel/" data-link-title="從 collector 資料做基礎 funnel 分析" data-link-desc="SQLite 層能做什麼程度的 funnel、PostgreSQL 層提供什麼進階能力、JSONL 匯出後的臨時分析">從 collector 資料做基礎 funnel 分析&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>推薦系統從使用者的歷史行為或物品的內容特徵推斷使用者可能感興趣的物品。三種基本方法各自依賴不同的資料：collaborative filtering 依賴使用者行為矩陣，content-based 依賴物品特徵，混合方法結合兩者。</p>
<h2 id="collaborative-filtering">Collaborative Filtering</h2>
<p>Collaborative filtering 的核心假設是「和你行為相似的人喜歡的東西，你也可能喜歡」。它不分析物品的內容，只看使用者的行為模式。</p>
<h3 id="user-based">User-based</h3>
<p>找到和目標使用者行為最相似的一群人（neighbors），把 neighbors 互動過但目標使用者沒互動過的物品推薦給目標使用者。</p>
<p>資料需求：使用者-物品互動矩陣（user × item matrix），每格是評分、點擊、購買等互動訊號。</p>
<p>挑戰：使用者數量增加時，計算使用者相似度的成本呈平方增長。冷啟動問題 — 新使用者沒有足夠的互動歷史來找到 neighbors。</p>
<h3 id="item-based">Item-based</h3>
<p>計算物品之間的相似度（根據哪些使用者同時互動過這兩個物品），推薦和使用者已互動物品相似的物品。</p>
<p>資料需求：同上，但相似度計算的維度是物品而非使用者。</p>
<p>優勢：物品的相似度矩陣比使用者相似度穩定（物品不會突然改變行為，使用者會），可以離線計算和快取。Amazon 的「購買此商品的人也買了」就是 item-based collaborative filtering。</p>
<h2 id="content-based-filtering">Content-based Filtering</h2>
<p>Content-based filtering 分析物品的內容特徵，推薦和使用者過去喜歡的物品內容相似的物品。</p>
<p>資料需求：每個物品的特徵向量（genre、author、price range、keywords）和使用者的偏好 profile（從歷史互動推斷）。</p>
<p>優勢：不依賴其他使用者的行為 — 單一使用者就能產生推薦。新物品只要有特徵描述就能被推薦（解決 collaborative filtering 的新物品冷啟動）。</p>
<p>挑戰：推薦結果傾向和使用者歷史行為相似，缺乏意外發現（serendipity）。特徵工程的品質直接影響推薦品質 — 物品的特徵描述不完整或不準確，推薦就不準確。</p>
<h2 id="混合方法">混合方法</h2>
<p>結合 collaborative filtering 和 content-based 的優勢，減少各自的弱點。</p>
<h3 id="加權混合">加權混合</h3>
<p>兩種方法各自產生推薦清單，用加權分數合併。權重可以固定，也可以根據情境動態調整（新使用者偏重 content-based，老使用者偏重 collaborative filtering）。</p>
<h3 id="特徵增強">特徵增強</h3>
<p>用 content-based 的特徵增強 collaborative filtering 的矩陣。使用者-物品互動矩陣加上物品的內容特徵，讓相似度計算同時考慮行為和內容。</p>
<h3 id="級聯">級聯</h3>
<p>先用一種方法粗篩，再用另一種方法排序。Collaborative filtering 產生候選清單，content-based 根據使用者的內容偏好排序。</p>
<h2 id="行為事件在推薦系統的角色">行為事件在推薦系統的角色</h2>
<p>推薦系統的輸入是使用者的互動行為 — 瀏覽、點擊、加入購物車、購買、評分。這些互動行為就是行為事件（<a href="/blog/monitoring/01-mental-model/" data-link-title="模組一：監控心智模型" data-link-desc="四類事件（event / error / metric / lifecycle）的分類與收集策略">模組一 心智模型</a>）的 event 類型。</p>
<p>行為事件的設計直接影響推薦系統的資料品質。事件的粒度決定了推薦的精細度 — 只記錄「頁面瀏覽」比記錄「頁面瀏覽 + 停留時間 + 滾動深度」的推薦信號弱。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>使用者分群的工程實作 → <a href="/blog/monitoring/08-business-analytics/rfm-segmentation/" data-link-title="RFM 分群" data-link-desc="Recency / Frequency / Monetary 三維度的使用者分群 — 從行為事件計算 RFM 分數、定義使用者群體、驅動差異化策略">RFM 分群</a></li>
<li>行為事件設計 → <a href="/blog/monitoring/08-business-analytics/behavior-event-design/" data-link-title="行為事件設計" data-link-desc="事件命名規範、屬性設計、funnel 定義 — 行為分析的品質取決於事件設計的品質">行為事件設計</a></li>
<li>自架方案的分析能力邊界 → <a href="/blog/monitoring/08-business-analytics/self-hosted-funnel/" data-link-title="從 collector 資料做基礎 funnel 分析" data-link-desc="SQLite 層能做什麼程度的 funnel、PostgreSQL 層提供什麼進階能力、JSONL 匯出後的臨時分析">從 collector 資料做基礎 funnel 分析</a></li>
</ul>
]]></content:encoded></item><item><title>RFM 分群</title><link>https://tarrragon.github.io/blog/monitoring/08-business-analytics/rfm-segmentation/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/monitoring/08-business-analytics/rfm-segmentation/</guid><description>&lt;p>&lt;a href="https://tarrragon.github.io/blog/monitoring/knowledge-cards/rfm/" data-link-title="RFM" data-link-desc="說明用 Recency / Frequency / Monetary 三個維度把使用者分成可操作群組的分群方法">RFM&lt;/a> 分群用三個維度衡量使用者的價值：Recency（最近一次互動是多久前）、Frequency（互動的頻率）、Monetary（互動的金額或價值）。三個維度各自獨立評分，組合成使用者的 RFM profile，驅動差異化的營運策略。&lt;/p>
&lt;h2 id="三個維度">三個維度&lt;/h2>
&lt;h3 id="recency最近一次互動的時間距離">Recency：最近一次互動的時間距離&lt;/h3>
&lt;p>計算使用者最後一次有意義的互動到現在的天數。「有意義的互動」取決於業務定義 — 電商是最後一次購買，SaaS 是最後一次登入，媒體是最後一次內容消費。&lt;/p>
&lt;p>Recency 的價值在於「最近互動的使用者比很久沒來的使用者更可能再次互動」。Recency 高（最近才來）的使用者是活躍群體，Recency 低（很久沒來）的使用者是流失風險群體。&lt;/p>
&lt;h3 id="frequency互動的頻率">Frequency：互動的頻率&lt;/h3>
&lt;p>計算使用者在特定時間窗口內的互動次數。時間窗口取決於業務節奏 — 日用品電商看近 90 天的購買次數，SaaS 看近 30 天的登入次數。&lt;/p>
&lt;p>Frequency 區分「偶爾來的使用者」和「常客」。高頻使用者是產品的核心用戶群，他們的行為和需求代表產品的核心價值。&lt;/p>
&lt;h3 id="monetary互動的價值">Monetary：互動的價值&lt;/h3>
&lt;p>計算使用者在特定時間窗口內貢獻的總金額。適用於有直接收入的業務（電商、訂閱服務）。&lt;/p>
&lt;p>沒有直接收入的產品可以用替代指標：內容平台用消費的內容數量，社群平台用產生的內容數量，工具類產品用使用的功能數量。替代指標的選擇依據是「哪個行為最能代表使用者的投入程度」。&lt;/p>
&lt;h2 id="rfm-分數計算">RFM 分數計算&lt;/h2>
&lt;p>每個維度獨立評分，通常用 1-5 分。評分方式有兩種：&lt;/p>
&lt;h3 id="等距分割">等距分割&lt;/h3>
&lt;p>把每個維度的值域等分成 5 段。Recency 0-6 天 = 5 分、7-13 天 = 4 分、依此類推。&lt;/p>
&lt;p>優點是簡單直覺；缺點是不考慮使用者分佈 — 如果大部分使用者的 Recency 在 0-6 天，5 分的群體佔大多數，分群的鑑別度低。&lt;/p>
&lt;h3 id="等量分割分位數">等量分割（分位數）&lt;/h3>
&lt;p>用分位數確保每個分數段的使用者數量大致相等。前 20% 的 Recency = 5 分、次 20% = 4 分。&lt;/p>
&lt;p>優點是每個分數段有足夠的使用者數量做分析；缺點是分數的業務意義不固定 — 5 分代表的天數取決於使用者分佈，不是固定的閾值。&lt;/p>
&lt;h2 id="rfm-群體定義">RFM 群體定義&lt;/h2>
&lt;p>三個維度各 5 分，組合出 125 種 RFM profile（5 × 5 × 5）。實務上不需要 125 種策略，通常歸納成 5-8 個有業務意義的群體：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>群體&lt;/th>
 &lt;th>RFM 特徵&lt;/th>
 &lt;th>描述&lt;/th>
 &lt;th>策略方向&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>冠軍客戶&lt;/td>
 &lt;td>R5 F5 M5&lt;/td>
 &lt;td>最近才來、經常來、消費高&lt;/td>
 &lt;td>維持關係、VIP 待遇&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>忠實客戶&lt;/td>
 &lt;td>R4-5 F4-5 M3-5&lt;/td>
 &lt;td>經常來、消費中到高&lt;/td>
 &lt;td>交叉銷售、推薦&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>潛力客戶&lt;/td>
 &lt;td>R4-5 F1-2 M1-2&lt;/td>
 &lt;td>最近才來、但頻率和消費低&lt;/td>
 &lt;td>引導更多互動&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>沉睡客戶&lt;/td>
 &lt;td>R1-2 F3-5 M3-5&lt;/td>
 &lt;td>曾經活躍但很久沒來&lt;/td>
 &lt;td>挽回活動&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>流失客戶&lt;/td>
 &lt;td>R1 F1 M1&lt;/td>
 &lt;td>很久沒來、頻率低、消費低&lt;/td>
 &lt;td>評估挽回成本效益&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="工程實作">工程實作&lt;/h2>
&lt;p>RFM 計算的輸入是使用者的行為事件。從 collector 的 JSONL 資料計算 RFM：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>擷取&lt;/strong>：篩選目標事件（購買、登入、使用功能），按 user_id 分群&lt;/li>
&lt;li>&lt;strong>計算 R&lt;/strong>：每個 user_id 的最新事件時間到現在的天數&lt;/li>
&lt;li>&lt;strong>計算 F&lt;/strong>：每個 user_id 在時間窗口內的事件數量&lt;/li>
&lt;li>&lt;strong>計算 M&lt;/strong>：每個 user_id 在時間窗口內的 monetary 屬性加總&lt;/li>
&lt;li>&lt;strong>評分&lt;/strong>：對 R/F/M 各自用分位數或等距分割評分&lt;/li>
&lt;li>&lt;strong>分群&lt;/strong>：根據 RFM 分數組合定義群體&lt;/li>
&lt;/ol>
&lt;p>這個計算可以用 SQL（如果資料在資料庫）或 Python pandas（如果資料在 JSONL 檔案）完成。定期重算（每天或每週），產出使用者群體標籤。&lt;/p>
&lt;p>RFM 分群需要的資料可以從自架 collector 提取 — &lt;a href="https://tarrragon.github.io/blog/monitoring/08-business-analytics/self-hosted-funnel/" data-link-title="從 collector 資料做基礎 funnel 分析" data-link-desc="SQLite 層能做什麼程度的 funnel、PostgreSQL 層提供什麼進階能力、JSONL 匯出後的臨時分析">從 collector 資料做基礎 funnel 分析&lt;/a>展示了 grep + jq 在自架環境中的分析能力和邊界。RFM 分出的群體還可以用 &lt;a href="https://tarrragon.github.io/blog/monitoring/08-business-analytics/cohort-analysis/" data-link-title="Cohort Analysis" data-link-desc="按共同特徵分群、比較不同群體的留存率和行為差異 — 從「平均值」到「誰在用、誰離開了」">Cohort analysis&lt;/a> 追蹤留存趨勢，兩種分析互補。分群和分析的前提是正確的&lt;a href="https://tarrragon.github.io/blog/monitoring/08-business-analytics/behavior-event-design/" data-link-title="行為事件設計" data-link-desc="事件命名規範、屬性設計、funnel 定義 — 行為分析的品質取決於事件設計的品質">行為事件設計&lt;/a> — 事件的屬性決定了 R/F/M 能否被計算。&lt;/p></description><content:encoded><![CDATA[<p><a href="/blog/monitoring/knowledge-cards/rfm/" data-link-title="RFM" data-link-desc="說明用 Recency / Frequency / Monetary 三個維度把使用者分成可操作群組的分群方法">RFM</a> 分群用三個維度衡量使用者的價值：Recency（最近一次互動是多久前）、Frequency（互動的頻率）、Monetary（互動的金額或價值）。三個維度各自獨立評分，組合成使用者的 RFM profile，驅動差異化的營運策略。</p>
<h2 id="三個維度">三個維度</h2>
<h3 id="recency最近一次互動的時間距離">Recency：最近一次互動的時間距離</h3>
<p>計算使用者最後一次有意義的互動到現在的天數。「有意義的互動」取決於業務定義 — 電商是最後一次購買，SaaS 是最後一次登入，媒體是最後一次內容消費。</p>
<p>Recency 的價值在於「最近互動的使用者比很久沒來的使用者更可能再次互動」。Recency 高（最近才來）的使用者是活躍群體，Recency 低（很久沒來）的使用者是流失風險群體。</p>
<h3 id="frequency互動的頻率">Frequency：互動的頻率</h3>
<p>計算使用者在特定時間窗口內的互動次數。時間窗口取決於業務節奏 — 日用品電商看近 90 天的購買次數，SaaS 看近 30 天的登入次數。</p>
<p>Frequency 區分「偶爾來的使用者」和「常客」。高頻使用者是產品的核心用戶群，他們的行為和需求代表產品的核心價值。</p>
<h3 id="monetary互動的價值">Monetary：互動的價值</h3>
<p>計算使用者在特定時間窗口內貢獻的總金額。適用於有直接收入的業務（電商、訂閱服務）。</p>
<p>沒有直接收入的產品可以用替代指標：內容平台用消費的內容數量，社群平台用產生的內容數量，工具類產品用使用的功能數量。替代指標的選擇依據是「哪個行為最能代表使用者的投入程度」。</p>
<h2 id="rfm-分數計算">RFM 分數計算</h2>
<p>每個維度獨立評分，通常用 1-5 分。評分方式有兩種：</p>
<h3 id="等距分割">等距分割</h3>
<p>把每個維度的值域等分成 5 段。Recency 0-6 天 = 5 分、7-13 天 = 4 分、依此類推。</p>
<p>優點是簡單直覺；缺點是不考慮使用者分佈 — 如果大部分使用者的 Recency 在 0-6 天，5 分的群體佔大多數，分群的鑑別度低。</p>
<h3 id="等量分割分位數">等量分割（分位數）</h3>
<p>用分位數確保每個分數段的使用者數量大致相等。前 20% 的 Recency = 5 分、次 20% = 4 分。</p>
<p>優點是每個分數段有足夠的使用者數量做分析；缺點是分數的業務意義不固定 — 5 分代表的天數取決於使用者分佈，不是固定的閾值。</p>
<h2 id="rfm-群體定義">RFM 群體定義</h2>
<p>三個維度各 5 分，組合出 125 種 RFM profile（5 × 5 × 5）。實務上不需要 125 種策略，通常歸納成 5-8 個有業務意義的群體：</p>
<table>
  <thead>
      <tr>
          <th>群體</th>
          <th>RFM 特徵</th>
          <th>描述</th>
          <th>策略方向</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>冠軍客戶</td>
          <td>R5 F5 M5</td>
          <td>最近才來、經常來、消費高</td>
          <td>維持關係、VIP 待遇</td>
      </tr>
      <tr>
          <td>忠實客戶</td>
          <td>R4-5 F4-5 M3-5</td>
          <td>經常來、消費中到高</td>
          <td>交叉銷售、推薦</td>
      </tr>
      <tr>
          <td>潛力客戶</td>
          <td>R4-5 F1-2 M1-2</td>
          <td>最近才來、但頻率和消費低</td>
          <td>引導更多互動</td>
      </tr>
      <tr>
          <td>沉睡客戶</td>
          <td>R1-2 F3-5 M3-5</td>
          <td>曾經活躍但很久沒來</td>
          <td>挽回活動</td>
      </tr>
      <tr>
          <td>流失客戶</td>
          <td>R1 F1 M1</td>
          <td>很久沒來、頻率低、消費低</td>
          <td>評估挽回成本效益</td>
      </tr>
  </tbody>
</table>
<h2 id="工程實作">工程實作</h2>
<p>RFM 計算的輸入是使用者的行為事件。從 collector 的 JSONL 資料計算 RFM：</p>
<ol>
<li><strong>擷取</strong>：篩選目標事件（購買、登入、使用功能），按 user_id 分群</li>
<li><strong>計算 R</strong>：每個 user_id 的最新事件時間到現在的天數</li>
<li><strong>計算 F</strong>：每個 user_id 在時間窗口內的事件數量</li>
<li><strong>計算 M</strong>：每個 user_id 在時間窗口內的 monetary 屬性加總</li>
<li><strong>評分</strong>：對 R/F/M 各自用分位數或等距分割評分</li>
<li><strong>分群</strong>：根據 RFM 分數組合定義群體</li>
</ol>
<p>這個計算可以用 SQL（如果資料在資料庫）或 Python pandas（如果資料在 JSONL 檔案）完成。定期重算（每天或每週），產出使用者群體標籤。</p>
<p>RFM 分群需要的資料可以從自架 collector 提取 — <a href="/blog/monitoring/08-business-analytics/self-hosted-funnel/" data-link-title="從 collector 資料做基礎 funnel 分析" data-link-desc="SQLite 層能做什麼程度的 funnel、PostgreSQL 層提供什麼進階能力、JSONL 匯出後的臨時分析">從 collector 資料做基礎 funnel 分析</a>展示了 grep + jq 在自架環境中的分析能力和邊界。RFM 分出的群體還可以用 <a href="/blog/monitoring/08-business-analytics/cohort-analysis/" data-link-title="Cohort Analysis" data-link-desc="按共同特徵分群、比較不同群體的留存率和行為差異 — 從「平均值」到「誰在用、誰離開了」">Cohort analysis</a> 追蹤留存趨勢，兩種分析互補。分群和分析的前提是正確的<a href="/blog/monitoring/08-business-analytics/behavior-event-design/" data-link-title="行為事件設計" data-link-desc="事件命名規範、屬性設計、funnel 定義 — 行為分析的品質取決於事件設計的品質">行為事件設計</a> — 事件的屬性決定了 R/F/M 能否被計算。</p>
]]></content:encoded></item><item><title>從 collector 資料做基礎 funnel 分析</title><link>https://tarrragon.github.io/blog/monitoring/08-business-analytics/self-hosted-funnel/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/monitoring/08-business-analytics/self-hosted-funnel/</guid><description>&lt;p>自架 collector 收集的事件資料可以做基礎的 funnel 分析，不需要商業方案。分析的深度取決於 storage backend 的查詢能力 — SQLite 層能做每步事件計數，PostgreSQL 層能做 session 級轉換率分析。功能分層的完整定義見 &lt;a href="https://tarrragon.github.io/blog/monitoring/04-collector/feature-tier-boundary/" data-link-title="功能分層與 Backend 選擇" data-link-desc="SQLite 層和 PostgreSQL 層各自承載哪些功能 — 分界線是查詢模式而非資料量、觸發升級的是功能需求而非規模成長">功能分層與 Backend 選擇&lt;/a>。&lt;/p>
&lt;h2 id="定義-funnel-步驟">定義 funnel 步驟&lt;/h2>
&lt;p>Funnel 分析的第一步是列出每一步和對應的事件名稱。以一個透過 WebSocket 連接遠端終端機的 app 連線流程為例：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>步驟&lt;/th>
 &lt;th>事件名稱&lt;/th>
 &lt;th>意義&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>1&lt;/td>
 &lt;td>terminal.connect.start&lt;/td>
 &lt;td>使用者點擊連線&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>2&lt;/td>
 &lt;td>auth.biometric.success&lt;/td>
 &lt;td>生物辨識通過&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>3&lt;/td>
 &lt;td>terminal.connect.done&lt;/td>
 &lt;td>WebSocket 連線成功&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>4&lt;/td>
 &lt;td>terminal.input.submit&lt;/td>
 &lt;td>使用者開始打字&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="sqlite-層每步事件計數">SQLite 層：每步事件計數&lt;/h2>
&lt;p>SQLite backend 能做的 funnel 是「每步有多少事件觸發」— 單表 GROUP BY，不需要跨事件 JOIN。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">COUNT&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">count&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">events&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;terminal.connect.start&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;auth.biometric.success&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;terminal.connect.done&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;terminal.input.submit&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ts&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;gt;=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">datetime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;now&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;-7 days&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">GROUP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>步驟 N 的轉換率 = 步驟 N 的事件數 / 步驟 N-1 的事件數。流失率 = 1 - 轉換率。&lt;/p>
&lt;h3 id="能做的">能做的&lt;/h3>
&lt;ul>
&lt;li>每步事件計數（單表 GROUP BY）&lt;/li>
&lt;li>按 source.version 或 source.platform 分群（加 WHERE 條件）&lt;/li>
&lt;li>按天/按週看趨勢（strftime 分桶 + GROUP BY）&lt;/li>
&lt;/ul>
&lt;h3 id="做不到的">做不到的&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Session 級轉換率&lt;/strong>：「同一個 session 完成步驟 1 到步驟 4 的比例」需要 JOIN 同 session 的多個事件、跨所有 session 聚合。SQLite 能做這個 JOIN，但在大量 session 時效能不足。&lt;/li>
&lt;li>&lt;strong>步驟間耗時&lt;/strong>：「使用者在步驟 1 和步驟 2 之間等了多久」需要 self-join on session_id + timestamp 差值計算。&lt;/li>
&lt;li>&lt;strong>漏斗順序驗證&lt;/strong>：確認使用者是按 1→2→3→4 順序完成、不是跳步。&lt;/li>
&lt;/ul>
&lt;h2 id="postgresql-層session-級-funnel">PostgreSQL 層：Session 級 funnel&lt;/h2>
&lt;p>PostgreSQL backend 提供 window function 和高效 JOIN，能做完整的 session 級 funnel 分析。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="k">WITH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">session_steps&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">session_id&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">ROW_NUMBER&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">OVER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">PARTITION&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">session_id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ORDER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ts&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">step_order&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">events&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;terminal.connect.start&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;auth.biometric.success&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;terminal.connect.done&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;terminal.input.submit&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ts&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;gt;=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NOW&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">INTERVAL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;7 days&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">session_max_step&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">session_id&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">MAX&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">step_order&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">reached&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">session_steps&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">GROUP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">session_id&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">reached&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">COUNT&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sessions&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">session_max_step&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">GROUP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">reached&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">ORDER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">reached&lt;/span>&lt;span class="p">;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="新增能力">新增能力&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Session 級轉換率&lt;/strong>：每個 session 到達了哪一步、在哪一步流失&lt;/li>
&lt;li>&lt;strong>步驟間耗時&lt;/strong>：LAG window function 計算相鄰步驟的 timestamp 差值&lt;/li>
&lt;li>&lt;strong>漏斗順序驗證&lt;/strong>：用 ROW_NUMBER + CASE 確認步驟順序&lt;/li>
&lt;li>&lt;strong>Cohort 分群的 funnel&lt;/strong>：按使用者註冊日期 / 版本 / 平台分群看不同 cohort 的 funnel 差異&lt;/li>
&lt;/ul>
&lt;h2 id="jsonl-匯出後的臨時分析">JSONL 匯出後的臨時分析&lt;/h2>
&lt;p>Collector 的 &lt;code>monitor export --format=jsonl&lt;/code> 可以匯出事件為 JSONL 格式。匯出後用 grep + jq 做一次性的臨時分析：&lt;/p></description><content:encoded><![CDATA[<p>自架 collector 收集的事件資料可以做基礎的 funnel 分析，不需要商業方案。分析的深度取決於 storage backend 的查詢能力 — SQLite 層能做每步事件計數，PostgreSQL 層能做 session 級轉換率分析。功能分層的完整定義見 <a href="/blog/monitoring/04-collector/feature-tier-boundary/" data-link-title="功能分層與 Backend 選擇" data-link-desc="SQLite 層和 PostgreSQL 層各自承載哪些功能 — 分界線是查詢模式而非資料量、觸發升級的是功能需求而非規模成長">功能分層與 Backend 選擇</a>。</p>
<h2 id="定義-funnel-步驟">定義 funnel 步驟</h2>
<p>Funnel 分析的第一步是列出每一步和對應的事件名稱。以一個透過 WebSocket 連接遠端終端機的 app 連線流程為例：</p>
<table>
  <thead>
      <tr>
          <th>步驟</th>
          <th>事件名稱</th>
          <th>意義</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>1</td>
          <td>terminal.connect.start</td>
          <td>使用者點擊連線</td>
      </tr>
      <tr>
          <td>2</td>
          <td>auth.biometric.success</td>
          <td>生物辨識通過</td>
      </tr>
      <tr>
          <td>3</td>
          <td>terminal.connect.done</td>
          <td>WebSocket 連線成功</td>
      </tr>
      <tr>
          <td>4</td>
          <td>terminal.input.submit</td>
          <td>使用者開始打字</td>
      </tr>
  </tbody>
</table>
<h2 id="sqlite-層每步事件計數">SQLite 層：每步事件計數</h2>
<p>SQLite backend 能做的 funnel 是「每步有多少事件觸發」— 單表 GROUP BY，不需要跨事件 JOIN。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="ln">1</span><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="k">count</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w"></span><span class="k">FROM</span><span class="w"> </span><span class="n">events</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w"></span><span class="k">WHERE</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="k">IN</span><span class="w"> </span><span class="p">(</span><span class="s1">&#39;terminal.connect.start&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;auth.biometric.success&#39;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">               </span><span class="s1">&#39;terminal.connect.done&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;terminal.input.submit&#39;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">  </span><span class="k">AND</span><span class="w"> </span><span class="n">ts</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="n">datetime</span><span class="p">(</span><span class="s1">&#39;now&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;-7 days&#39;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="w"></span><span class="k">GROUP</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">name</span><span class="p">;</span></span></span></code></pre></div><p>步驟 N 的轉換率 = 步驟 N 的事件數 / 步驟 N-1 的事件數。流失率 = 1 - 轉換率。</p>
<h3 id="能做的">能做的</h3>
<ul>
<li>每步事件計數（單表 GROUP BY）</li>
<li>按 source.version 或 source.platform 分群（加 WHERE 條件）</li>
<li>按天/按週看趨勢（strftime 分桶 + GROUP BY）</li>
</ul>
<h3 id="做不到的">做不到的</h3>
<ul>
<li><strong>Session 級轉換率</strong>：「同一個 session 完成步驟 1 到步驟 4 的比例」需要 JOIN 同 session 的多個事件、跨所有 session 聚合。SQLite 能做這個 JOIN，但在大量 session 時效能不足。</li>
<li><strong>步驟間耗時</strong>：「使用者在步驟 1 和步驟 2 之間等了多久」需要 self-join on session_id + timestamp 差值計算。</li>
<li><strong>漏斗順序驗證</strong>：確認使用者是按 1→2→3→4 順序完成、不是跳步。</li>
</ul>
<h2 id="postgresql-層session-級-funnel">PostgreSQL 層：Session 級 funnel</h2>
<p>PostgreSQL backend 提供 window function 和高效 JOIN，能做完整的 session 級 funnel 分析。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">WITH</span><span class="w"> </span><span class="n">session_steps</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">  </span><span class="k">SELECT</span><span class="w"> </span><span class="n">session_id</span><span class="p">,</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">         </span><span class="n">ROW_NUMBER</span><span class="p">()</span><span class="w"> </span><span class="n">OVER</span><span class="w"> </span><span class="p">(</span><span class="n">PARTITION</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">session_id</span><span class="w"> </span><span class="k">ORDER</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">ts</span><span class="p">)</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">step_order</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">  </span><span class="k">FROM</span><span class="w"> </span><span class="n">events</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">  </span><span class="k">WHERE</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="k">IN</span><span class="w"> </span><span class="p">(</span><span class="s1">&#39;terminal.connect.start&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;auth.biometric.success&#39;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">                 </span><span class="s1">&#39;terminal.connect.done&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;terminal.input.submit&#39;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span><span class="k">AND</span><span class="w"> </span><span class="n">ts</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="n">NOW</span><span class="p">()</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="nb">INTERVAL</span><span class="w"> </span><span class="s1">&#39;7 days&#39;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w"></span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w"></span><span class="n">session_max_step</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">  </span><span class="k">SELECT</span><span class="w"> </span><span class="n">session_id</span><span class="p">,</span><span class="w"> </span><span class="k">MAX</span><span class="p">(</span><span class="n">step_order</span><span class="p">)</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">reached</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">  </span><span class="k">FROM</span><span class="w"> </span><span class="n">session_steps</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">  </span><span class="k">GROUP</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">session_id</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w"></span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w"></span><span class="k">SELECT</span><span class="w"> </span><span class="n">reached</span><span class="p">,</span><span class="w"> </span><span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">sessions</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w"></span><span class="k">FROM</span><span class="w"> </span><span class="n">session_max_step</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w"></span><span class="k">GROUP</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">reached</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w"></span><span class="k">ORDER</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">reached</span><span class="p">;</span></span></span></code></pre></div><h3 id="新增能力">新增能力</h3>
<ul>
<li><strong>Session 級轉換率</strong>：每個 session 到達了哪一步、在哪一步流失</li>
<li><strong>步驟間耗時</strong>：LAG window function 計算相鄰步驟的 timestamp 差值</li>
<li><strong>漏斗順序驗證</strong>：用 ROW_NUMBER + CASE 確認步驟順序</li>
<li><strong>Cohort 分群的 funnel</strong>：按使用者註冊日期 / 版本 / 平台分群看不同 cohort 的 funnel 差異</li>
</ul>
<h2 id="jsonl-匯出後的臨時分析">JSONL 匯出後的臨時分析</h2>
<p>Collector 的 <code>monitor export --format=jsonl</code> 可以匯出事件為 JSONL 格式。匯出後用 grep + jq 做一次性的臨時分析：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="k">for</span> step in terminal.connect.start auth.biometric.success terminal.connect.done terminal.input.submit<span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">  <span class="nv">count</span><span class="o">=</span><span class="k">$(</span>grep <span class="s2">&#34;\&#34;name\&#34;:\&#34;</span><span class="nv">$step</span><span class="s2">\&#34;&#34;</span> exported-events.jsonl <span class="p">|</span> wc -l<span class="k">)</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$step</span><span class="s2">: </span><span class="nv">$count</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="k">done</span></span></span></code></pre></div><p>JSONL 臨時分析適合「快速看一眼大概數字」的場景。持續性的 funnel 監控應該用 SQLite 或 PostgreSQL 的 SQL 查詢，結果穩定且可重現。</p>
<h2 id="自架-vs-商業方案">自架 vs 商業方案</h2>
<table>
  <thead>
      <tr>
          <th>需求</th>
          <th>自架能力</th>
          <th>商業方案</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>每步事件計數</td>
          <td>SQLite GROUP BY</td>
          <td>Mixpanel / Amplitude 內建</td>
      </tr>
      <tr>
          <td>Session 級轉換率</td>
          <td>PostgreSQL window function</td>
          <td>Mixpanel / Amplitude 內建</td>
      </tr>
      <tr>
          <td>視覺化 funnel 漏斗圖</td>
          <td>自建 dashboard</td>
          <td>商業方案內建、拖拉設定</td>
      </tr>
      <tr>
          <td>即時更新</td>
          <td>定期重算 + dashboard 刷新</td>
          <td>商業方案即時</td>
      </tr>
      <tr>
          <td>A/B test 分群 funnel</td>
          <td>PostgreSQL + feature flag</td>
          <td>Optimizely / LaunchDarkly 整合</td>
      </tr>
  </tbody>
</table>
<p>自用工具場景下，SQLite 層的每步事件計數通常足夠。商業產品需要 session 級分析時，PostgreSQL 層的 SQL 能力和商業方案的分析能力在功能上對等，差異在 UI 和設定便利性。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>Funnel 分析的完整方法論 → <a href="/blog/monitoring/08-business-analytics/funnel-analysis/" data-link-title="Funnel Analysis" data-link-desc="使用者在哪一步流失 — 從事件序列計算每步轉換率、找出流失最嚴重的步驟、區分設計問題和技術問題">Funnel analysis</a></li>
<li>事件設計如何影響分析品質 → <a href="/blog/monitoring/08-business-analytics/behavior-event-design/" data-link-title="行為事件設計" data-link-desc="事件命名規範、屬性設計、funnel 定義 — 行為分析的品質取決於事件設計的品質">行為事件設計</a></li>
<li>功能分層定義 → <a href="/blog/monitoring/04-collector/feature-tier-boundary/" data-link-title="功能分層與 Backend 選擇" data-link-desc="SQLite 層和 PostgreSQL 層各自承載哪些功能 — 分界線是查詢模式而非資料量、觸發升級的是功能需求而非規模成長">功能分層與 Backend 選擇</a></li>
<li>去識別化是分析的入場條件 → <a href="/blog/monitoring/07-security-privacy/" data-link-title="模組七：資安與隱私" data-link-desc="SDK redaction / transport 加密 / collector access control / 去識別化 — 蒐集的資料本身就是風險資產">模組七 資安與隱私</a></li>
</ul>
]]></content:encoded></item></channel></rss>