<?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>Data-Analysis on Tarragon</title><link>https://tarrragon.github.io/blog/tags/data-analysis/</link><description>Recent content in Data-Analysis on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Mon, 31 Aug 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/data-analysis/index.xml" rel="self" type="application/rss+xml"/><item><title>8.1 Python 的幾種工作與資料進入程式的單位：Web 後端、資料工程與分析、機器學習、自動化與系統工具</title><link>https://tarrragon.github.io/blog/programming/python/basic/08-data-analysis/data-unit-splits-work/</link><pubDate>Mon, 31 Aug 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/programming/python/basic/08-data-analysis/data-unit-splits-work/</guid><description>&lt;p>分開 Python 底下幾種工作的，是資料以什麼單位進入程式。一次進來一筆、還是一次進來一整批，決定了要解的問題是什麼，也決定了工具鏈長什麼樣子。同一個語言標籤底下因此裝著幾組彼此不太相干的技術。&lt;/p>
&lt;h2 id="web-後端以一次請求用到的幾筆資料為單位">Web 後端：以一次請求用到的幾筆資料為單位&lt;/h2>
&lt;p>資料的單位是這次請求要用到的那幾筆。使用者按下一個按鈕，程式取出對應的幾列、改幾個欄位、寫回去，然後這次請求就結束了。&lt;/p>
&lt;p>工具解的是把那幾筆安全地取出與改回去：&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/orm/" data-link-title="ORM（Object-Relational Mapping）" data-link-desc="看到程式用類別與方法描述查詢時，查它交出去的是什麼、以及哪些行為在程式碼上讀不出來">ORM&lt;/a> 把資料列包成物件、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/connection-pool/" data-link-title="Connection Pool" data-link-desc="說明連線池如何限制下游資源並影響服務容量">連線池&lt;/a>管理與資料庫之間的連線、遷移工具讓表結構跟著程式一起演進。Django 與 FastAPI 這類框架的周邊生態幾乎都繞著這件事。&lt;/p>
&lt;p>Web 後端的失敗形態跟著單位走。一次請求裡不小心打了一百次資料庫（每筆各查一次），或是把&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/transaction/" data-link-title="Transaction" data-link-desc="說明 transaction 如何讓一組資料變更一起成功或一起回復">交易&lt;/a>的範圍包得太大而讓鎖持有太久（一段時間內不讓別人動同一批列，範圍越大擋得越久），都是「一次應該只取幾筆」這個前提被破壞的結果。&lt;/p>
&lt;h2 id="資料工程與分析以整張表或整批事件為單位">資料工程與分析：以整張表或整批事件為單位&lt;/h2>
&lt;p>資料的單位是整張表，或某段期間的全部事件。沒有「這次請求」這種邊界，取而代之的是「這批」。&lt;/p>
&lt;p>資料工程與分析的工具鏈因此分成三組，各解一件事：整批資料怎麼算（pandas 在單機記憶體裡算，Spark 把同一件事拆到多台機器上），轉換邏輯怎麼版本控管（dbt 把每一步轉換寫成可以被審閱與測試的模型），以及什麼時候算、失敗了怎麼重跑（Airflow 這類排程器）。&lt;/p>
&lt;p>失敗形態也整組換掉：這批裝不裝得進記憶體、同一批會不會因為重跑而被算兩次、上游的欄位改了名字而下游整條流程靜默算出錯的結果。&lt;/p>
&lt;h2 id="機器學習接在批次處理後面的數值矩陣">機器學習：接在批次處理後面的數值矩陣&lt;/h2>
&lt;p>模型收的是一個數值矩陣。原始資料很少長成那個樣子，所以把它整理成矩陣的那一段仍然是批次處理——合併幾張表、把類別欄位轉成數值、補掉缺值、切出訓練與驗證。&lt;/p>
&lt;p>pandas 在這裡是前置作業，PyTorch 這類框架接在它後面。機器學習與資料工程共用工具鏈的前半段，分歧在後半段。&lt;/p>
&lt;h2 id="自動化與系統工具以一個檔案或一次執行為單位">自動化與系統工具：以一個檔案或一次執行為單位&lt;/h2>
&lt;p>自動化腳本處理的是檔案、程序與外部命令。單位是一個檔案或一次執行，資料本身是被搬運的內容。工具是 &lt;code>pathlib&lt;/code>、&lt;code>subprocess&lt;/code>、&lt;code>argparse&lt;/code> 這一組，也就是 &lt;a href="https://tarrragon.github.io/blog/programming/python/basic/03-stdlib/" data-link-title="模組三：標準庫實戰" data-link-desc="Python 標準庫的常用模組實戰應用">模組三&lt;/a> 的內容。&lt;/p>
&lt;p>系統與工具開發再往外一層：產出是被別人 import 或執行的程式，資料由使用者帶來。關心的是介面設計、相容性與封裝。&lt;/p>
&lt;h2 id="各組工作的失敗形態判斷材料與檢索關鍵字的差異">各組工作的失敗形態、判斷材料與檢索關鍵字的差異&lt;/h2>
&lt;p>單位決定了什麼事情會出錯，而這幾組的單位不同，出錯的形態就沒有交集。&lt;/p>
&lt;p>把一次請求的查詢次數壓下來，靠的是理解物件與資料列之間的對應關係；判斷一批資料裝不裝得進記憶體，靠的是估算列數乘欄數乘每格佔幾個位元組。這兩種判斷用的材料完全不同，前一種做得再熟也不會讓後一種變容易。&lt;/p>
&lt;p>檢索的結果跟著同一條軸分開。以物件關聯對映與請求生命週期為關鍵字找到的內容，與以 DataFrame 和排程為關鍵字找到的內容幾乎不重疊，因為它們描述的是不同的問題。一個人如果只用其中一組關鍵字搜尋，另一組的內容不會被結果帶到他面前——要看到它得先知道該用哪些詞。&lt;/p>
&lt;p>這幾組工作在職務名稱上也各自分開，Python 只是它們共用的語言。&lt;/p>
&lt;h2 id="延伸閱讀運算發生的位置web-後端的資料庫議題商業分析指標標準庫的檔案與外部命令工具">延伸閱讀：運算發生的位置、Web 後端的資料庫議題、商業分析指標、標準庫的檔案與外部命令工具&lt;/h2>
&lt;p>&lt;strong>批次處理再往下切一刀&lt;/strong>：整批資料要嘛留在資料庫裡算，要嘛搬進程式的記憶體算。&lt;a href="https://tarrragon.github.io/blog/programming/python/basic/08-data-analysis/where-computation-runs/" data-link-title="8.2 運算發生的位置：資料庫端與記憶體端的工具、分工條件與 DuckDB" data-link-desc="運算留在資料庫端或搬進記憶體端這條分界，怎麼決定工具的形狀、容量上限與失敗形態">8.2 運算發生的位置&lt;/a> 寫這條分界怎麼決定工具的形狀，以及 Python 存取資料庫的三種寫法為什麼全部落在資料庫那一側。&lt;/p>
&lt;p>&lt;strong>Web 後端的資料庫議題&lt;/strong>：一次請求該打幾次資料庫、交易邊界包到哪裡、ORM 的延遲載入什麼時候變成問題。&lt;a href="https://tarrragon.github.io/blog/backend/01-database/" data-link-title="模組一：資料庫與持久化" data-link-desc="整理 SQL、transaction、migration 與 repository adapter 的後端實務">backend 模組一&lt;/a> 從這些形態進去，並給出每請求的查詢次數預算當判讀基準。&lt;/p>
&lt;p>&lt;strong>批次算出來的結果拿去做什麼&lt;/strong>：&lt;a href="https://tarrragon.github.io/blog/devops/monitoring/08-business-analytics/" data-link-title="模組八：行為資料的商業利用" data-link-desc="Funnel / Cohort / Attribution / A/B test / 推薦系統 / RFM — 從 debug 工具到商業資產的翻轉">monitoring 模組八&lt;/a> 寫留存、分群與轉換率各自怎麼定義，以及事件當初的屬性設計決定了哪些指標事後算得出來。&lt;/p>
&lt;p>&lt;strong>自動化的工具&lt;/strong>：&lt;a href="https://tarrragon.github.io/blog/programming/python/basic/03-stdlib/" data-link-title="模組三：標準庫實戰" data-link-desc="Python 標準庫的常用模組實戰應用">模組三&lt;/a> 收的是處理檔案與外部命令的標準庫，單位是一個檔案或一次執行。&lt;/p></description><content:encoded><![CDATA[<p>分開 Python 底下幾種工作的，是資料以什麼單位進入程式。一次進來一筆、還是一次進來一整批，決定了要解的問題是什麼，也決定了工具鏈長什麼樣子。同一個語言標籤底下因此裝著幾組彼此不太相干的技術。</p>
<h2 id="web-後端以一次請求用到的幾筆資料為單位">Web 後端：以一次請求用到的幾筆資料為單位</h2>
<p>資料的單位是這次請求要用到的那幾筆。使用者按下一個按鈕，程式取出對應的幾列、改幾個欄位、寫回去，然後這次請求就結束了。</p>
<p>工具解的是把那幾筆安全地取出與改回去：<a href="/blog/backend/knowledge-cards/orm/" data-link-title="ORM（Object-Relational Mapping）" data-link-desc="看到程式用類別與方法描述查詢時，查它交出去的是什麼、以及哪些行為在程式碼上讀不出來">ORM</a> 把資料列包成物件、<a href="/blog/backend/knowledge-cards/connection-pool/" data-link-title="Connection Pool" data-link-desc="說明連線池如何限制下游資源並影響服務容量">連線池</a>管理與資料庫之間的連線、遷移工具讓表結構跟著程式一起演進。Django 與 FastAPI 這類框架的周邊生態幾乎都繞著這件事。</p>
<p>Web 後端的失敗形態跟著單位走。一次請求裡不小心打了一百次資料庫（每筆各查一次），或是把<a href="/blog/backend/knowledge-cards/transaction/" data-link-title="Transaction" data-link-desc="說明 transaction 如何讓一組資料變更一起成功或一起回復">交易</a>的範圍包得太大而讓鎖持有太久（一段時間內不讓別人動同一批列，範圍越大擋得越久），都是「一次應該只取幾筆」這個前提被破壞的結果。</p>
<h2 id="資料工程與分析以整張表或整批事件為單位">資料工程與分析：以整張表或整批事件為單位</h2>
<p>資料的單位是整張表，或某段期間的全部事件。沒有「這次請求」這種邊界，取而代之的是「這批」。</p>
<p>資料工程與分析的工具鏈因此分成三組，各解一件事：整批資料怎麼算（pandas 在單機記憶體裡算，Spark 把同一件事拆到多台機器上），轉換邏輯怎麼版本控管（dbt 把每一步轉換寫成可以被審閱與測試的模型），以及什麼時候算、失敗了怎麼重跑（Airflow 這類排程器）。</p>
<p>失敗形態也整組換掉：這批裝不裝得進記憶體、同一批會不會因為重跑而被算兩次、上游的欄位改了名字而下游整條流程靜默算出錯的結果。</p>
<h2 id="機器學習接在批次處理後面的數值矩陣">機器學習：接在批次處理後面的數值矩陣</h2>
<p>模型收的是一個數值矩陣。原始資料很少長成那個樣子，所以把它整理成矩陣的那一段仍然是批次處理——合併幾張表、把類別欄位轉成數值、補掉缺值、切出訓練與驗證。</p>
<p>pandas 在這裡是前置作業，PyTorch 這類框架接在它後面。機器學習與資料工程共用工具鏈的前半段，分歧在後半段。</p>
<h2 id="自動化與系統工具以一個檔案或一次執行為單位">自動化與系統工具：以一個檔案或一次執行為單位</h2>
<p>自動化腳本處理的是檔案、程序與外部命令。單位是一個檔案或一次執行，資料本身是被搬運的內容。工具是 <code>pathlib</code>、<code>subprocess</code>、<code>argparse</code> 這一組，也就是 <a href="/blog/programming/python/basic/03-stdlib/" data-link-title="模組三：標準庫實戰" data-link-desc="Python 標準庫的常用模組實戰應用">模組三</a> 的內容。</p>
<p>系統與工具開發再往外一層：產出是被別人 import 或執行的程式，資料由使用者帶來。關心的是介面設計、相容性與封裝。</p>
<h2 id="各組工作的失敗形態判斷材料與檢索關鍵字的差異">各組工作的失敗形態、判斷材料與檢索關鍵字的差異</h2>
<p>單位決定了什麼事情會出錯，而這幾組的單位不同，出錯的形態就沒有交集。</p>
<p>把一次請求的查詢次數壓下來，靠的是理解物件與資料列之間的對應關係；判斷一批資料裝不裝得進記憶體，靠的是估算列數乘欄數乘每格佔幾個位元組。這兩種判斷用的材料完全不同，前一種做得再熟也不會讓後一種變容易。</p>
<p>檢索的結果跟著同一條軸分開。以物件關聯對映與請求生命週期為關鍵字找到的內容，與以 DataFrame 和排程為關鍵字找到的內容幾乎不重疊，因為它們描述的是不同的問題。一個人如果只用其中一組關鍵字搜尋，另一組的內容不會被結果帶到他面前——要看到它得先知道該用哪些詞。</p>
<p>這幾組工作在職務名稱上也各自分開，Python 只是它們共用的語言。</p>
<h2 id="延伸閱讀運算發生的位置web-後端的資料庫議題商業分析指標標準庫的檔案與外部命令工具">延伸閱讀：運算發生的位置、Web 後端的資料庫議題、商業分析指標、標準庫的檔案與外部命令工具</h2>
<p><strong>批次處理再往下切一刀</strong>：整批資料要嘛留在資料庫裡算，要嘛搬進程式的記憶體算。<a href="/blog/programming/python/basic/08-data-analysis/where-computation-runs/" data-link-title="8.2 運算發生的位置：資料庫端與記憶體端的工具、分工條件與 DuckDB" data-link-desc="運算留在資料庫端或搬進記憶體端這條分界，怎麼決定工具的形狀、容量上限與失敗形態">8.2 運算發生的位置</a> 寫這條分界怎麼決定工具的形狀，以及 Python 存取資料庫的三種寫法為什麼全部落在資料庫那一側。</p>
<p><strong>Web 後端的資料庫議題</strong>：一次請求該打幾次資料庫、交易邊界包到哪裡、ORM 的延遲載入什麼時候變成問題。<a href="/blog/backend/01-database/" data-link-title="模組一：資料庫與持久化" data-link-desc="整理 SQL、transaction、migration 與 repository adapter 的後端實務">backend 模組一</a> 從這些形態進去，並給出每請求的查詢次數預算當判讀基準。</p>
<p><strong>批次算出來的結果拿去做什麼</strong>：<a href="/blog/devops/monitoring/08-business-analytics/" data-link-title="模組八：行為資料的商業利用" data-link-desc="Funnel / Cohort / Attribution / A/B test / 推薦系統 / RFM — 從 debug 工具到商業資產的翻轉">monitoring 模組八</a> 寫留存、分群與轉換率各自怎麼定義，以及事件當初的屬性設計決定了哪些指標事後算得出來。</p>
<p><strong>自動化的工具</strong>：<a href="/blog/programming/python/basic/03-stdlib/" data-link-title="模組三：標準庫實戰" data-link-desc="Python 標準庫的常用模組實戰應用">模組三</a> 收的是處理檔案與外部命令的標準庫，單位是一個檔案或一次執行。</p>
]]></content:encoded></item><item><title>8.2 運算發生的位置：資料庫端與記憶體端的工具、分工條件與 DuckDB</title><link>https://tarrragon.github.io/blog/programming/python/basic/08-data-analysis/where-computation-runs/</link><pubDate>Mon, 31 Aug 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/programming/python/basic/08-data-analysis/where-computation-runs/</guid><description>&lt;p>整批資料有兩個地方可以算：留在資料庫裡，或搬進程式的記憶體。這條分界決定了工具的形狀、它的容量上限，以及它的失敗形態。本模組其餘各篇的判斷標準都折算回這一條。&lt;/p>
&lt;h2 id="資料庫端db-api查詢建構器與-orm-的寫法">資料庫端：DB-API、查詢建構器與 ORM 的寫法&lt;/h2>
&lt;p>程式送出的是一段描述要算什麼的文字，資料庫收下之後自己完成篩選、合併與彙總，把結果送回來。原始資料從頭到尾沒有進入程式。&lt;/p>
&lt;p>Python 這一側有三種寫法，差別只在那段 SQL 由誰組出來。&lt;/p>
&lt;p>&lt;strong>自己寫 SQL 字串&lt;/strong>：&lt;code>sqlite3&lt;/code>、&lt;code>psycopg2&lt;/code> 這類套件遵循 DB-API 這個標準介面，程式把 SQL 交給它，它負責送出與取回。&lt;/p>
&lt;p>&lt;strong>用方法串出 SQL&lt;/strong>：SQLAlchemy Core 這類查詢建構器提供 Python 的方法對應 SQL 的子句，組出來的仍然是一段 SQL。&lt;/p>
&lt;p>&lt;strong>用類別描述表&lt;/strong>：&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/orm/" data-link-title="ORM（Object-Relational Mapping）" data-link-desc="看到程式用類別與方法描述查詢時，查它交出去的是什麼、以及哪些行為在程式碼上讀不出來">ORM&lt;/a> 這一類工具（SQLAlchemy、Django ORM）把資料表對應成類別、把資料列對應成物件，篩選條件寫成屬性比較，框架據此組出 SQL，並把回來的每一列包成物件。&lt;/p>
&lt;p>三者的共同點比差異重要：&lt;strong>運算在資料庫那一端發生&lt;/strong>，程式收到的是算完的結果。編譯出來的 SQL 可以直接印出來看：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">sqlalchemy&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">select&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="n">stmt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">select&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Customer&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">where&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Customer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">age&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">18&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">stmt&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">compile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">engine&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># SELECT customer.id, customer.name, customer.age FROM customer WHERE customer.age &amp;gt; ?&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="記憶體端pandas-與-dataframe">記憶體端：pandas 與 DataFrame&lt;/h2>
&lt;p>搬進記憶體端的做法是把資料整批載進程式，運算由程式自己完成。pandas 屬於這一側，它的容器是 &lt;a href="https://tarrragon.github.io/blog/backend/01-database/sql/knowledge-cards/dataframe/" data-link-title="DataFrame（資料框）" data-link-desc="SQL 教材拿 pandas 或資料框當對照時，查 DataFrame 是什麼、它與資料表差在哪">DataFrame&lt;/a>。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="n">df&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">df&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;age&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">18&lt;/span>&lt;span class="p">]&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>這一行沒有連線、沒有查詢語句、沒有資料庫。&lt;code>df&lt;/code> 是一張已經在記憶體裡的表格，這一行做的是對一個布林陣列取值。資料的來源可以是資料庫，也可以是 CSV、日誌檔或某個 API 回傳的內容——載進來之後，接下來的每一步運算都寫成一樣的形式，來源是哪一種不再影響寫法。&lt;/p>
&lt;h2 id="兩端分工的條件資料量sql-的表達能力算完之後的用途">兩端分工的條件：資料量、SQL 的表達能力、算完之後的用途&lt;/h2>
&lt;p>&lt;strong>資料量。&lt;/strong> 記憶體端要把資料整批放進來，所以容量是硬上限；資料庫端沒有這個限制，它一次只需要把符合條件的部分送回來。&lt;/p>
&lt;p>&lt;strong>要算的運算 SQL 表達得出來嗎。&lt;/strong> 篩選、合併、分組彙總、排名，SQL 都寫得出來。統計檢定、時間序列的滑動窗口、把資料餵給模型，SQL 寫起來吃力或根本沒有對應語法，這些落在記憶體端。&lt;/p>
&lt;p>&lt;strong>算完之後還要做什麼。&lt;/strong> 結果如果只是要回傳給使用者或寫回另一張表，留在資料庫端的路徑最短——資料不必先進記憶體再送出去；如果後面還要接繪圖、統計或模型訓練，資料終究要進記憶體，那就沒有理由分兩段做。&lt;/p>
&lt;p>實務上的常見形態是兩端接力：用 SQL 把資料量先壓下來，再把壓縮過的結果搬進記憶體做後面的部分。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="n">df&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">pd&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">read_sql&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;SELECT * FROM orders WHERE year = 2024&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">conn&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c1"># 這一行之後全部在記憶體裡，與資料庫無關&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="duckdb在同一個程序裡用-sql-查-dataframe-與檔案">DuckDB：在同一個程序裡用 SQL 查 DataFrame 與檔案&lt;/h2>
&lt;p>DuckDB 兩邊都站：它接受 SQL，而查詢的對象可以是記憶體裡的 DataFrame，也可以是磁碟上的檔案。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="n">duckdb&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">sql&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;SELECT dept, count(*) c FROM emp GROUP BY dept&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># emp 是一個 DataFrame&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>它把「資料庫」這個角色從一台獨立的伺服器換成同一個程序裡的運算引擎，而軸本身仍在——差別只在資料要不要複製一份進 Python 的記憶體。&lt;/p>
&lt;h2 id="延伸閱讀orm-與-dataframe-的分辨sql-與-pandas-的寫法對應pandas-的記憶體邊界資料庫端的工程議題">延伸閱讀：ORM 與 DataFrame 的分辨、SQL 與 pandas 的寫法對應、pandas 的記憶體邊界、資料庫端的工程議題&lt;/h2>
&lt;p>&lt;strong>從回傳值認出手上是哪一類&lt;/strong>：資料庫端的工具回傳的是物件或資料列，記憶體端的工具回傳的是一張新的表。&lt;a href="https://tarrragon.github.io/blog/programming/python/basic/08-data-analysis/orm-and-dataframe/" data-link-title="8.3 ORM 與 DataFrame 的分辨：回傳值的形狀、連線物件與查詢送出的時機" data-link-desc="ORM 產生查詢、DataFrame 執行運算，以及外觀相似的方法串接底下差了什麼">8.3 ORM 與 DataFrame 的分辨：回傳值的形狀、連線物件與查詢送出的時機&lt;/a> 寫兩者各自產出什麼，以及外觀相似的方法串接底下差了什麼。&lt;/p>
&lt;p>&lt;strong>兩端共用的那套抽象&lt;/strong>：篩選、合併、分組彙總在兩端都有，而且對應得起來。&lt;a href="https://tarrragon.github.io/blog/programming/python/basic/08-data-analysis/same-relational-algebra/" data-link-title="8.4 SQL 子句與 pandas 方法的對應：篩選、連接、分組彙總、視窗函數，以及空值、索引與順序的語意差異" data-link-desc="SQL 與 DataFrame 共用的關聯代數、同一份資料上兩邊寫法的實測比對，以及照著逐句翻譯會靜默失準的位置">8.4 SQL 子句與 pandas 方法的對應：篩選、連接、分組彙總、視窗函數，以及空值、索引與順序的語意差異&lt;/a> 給篩選、連接、分組彙總與視窗函數在兩邊的實測比對，以及空值、索引與順序這幾處對應斷掉的位置。&lt;/p>
&lt;p>&lt;strong>記憶體端的容量上限&lt;/strong>：&lt;a href="https://tarrragon.github.io/blog/programming/python/basic/08-data-analysis/memory-is-the-boundary/" data-link-title="8.5 pandas 的記憶體邊界：資料與運算過程的佔用估算，推回資料庫、換引擎與分散式的代價" data-link-desc="數值欄與字串欄的佔用怎麼得出、合併這類操作的中間結果佔多少，以及記憶體不足與跑得慢這兩種失敗怎麼分辨">8.5 pandas 的記憶體邊界：資料與運算過程的佔用估算，推回資料庫、換引擎與分散式的代價&lt;/a> 給資料量的估算方法、運算過程額外要的空間，以及越過之後三個方向各自付什麼代價。&lt;/p>
&lt;p>&lt;strong>資料庫端那一側的工程議題&lt;/strong>：連線池、交易邊界、一次請求該打幾次資料庫，在 &lt;a href="https://tarrragon.github.io/blog/backend/01-database/" data-link-title="模組一：資料庫與持久化" data-link-desc="整理 SQL、transaction、migration 與 repository adapter 的後端實務">backend 模組一&lt;/a>。那個模組從 N+1 與延遲載入這些形態進去，並給出每請求查詢次數預算當判讀基準。&lt;/p></description><content:encoded><![CDATA[<p>整批資料有兩個地方可以算：留在資料庫裡，或搬進程式的記憶體。這條分界決定了工具的形狀、它的容量上限，以及它的失敗形態。本模組其餘各篇的判斷標準都折算回這一條。</p>
<h2 id="資料庫端db-api查詢建構器與-orm-的寫法">資料庫端：DB-API、查詢建構器與 ORM 的寫法</h2>
<p>程式送出的是一段描述要算什麼的文字，資料庫收下之後自己完成篩選、合併與彙總，把結果送回來。原始資料從頭到尾沒有進入程式。</p>
<p>Python 這一側有三種寫法，差別只在那段 SQL 由誰組出來。</p>
<p><strong>自己寫 SQL 字串</strong>：<code>sqlite3</code>、<code>psycopg2</code> 這類套件遵循 DB-API 這個標準介面，程式把 SQL 交給它，它負責送出與取回。</p>
<p><strong>用方法串出 SQL</strong>：SQLAlchemy Core 這類查詢建構器提供 Python 的方法對應 SQL 的子句，組出來的仍然是一段 SQL。</p>
<p><strong>用類別描述表</strong>：<a href="/blog/backend/knowledge-cards/orm/" data-link-title="ORM（Object-Relational Mapping）" data-link-desc="看到程式用類別與方法描述查詢時，查它交出去的是什麼、以及哪些行為在程式碼上讀不出來">ORM</a> 這一類工具（SQLAlchemy、Django ORM）把資料表對應成類別、把資料列對應成物件，篩選條件寫成屬性比較，框架據此組出 SQL，並把回來的每一列包成物件。</p>
<p>三者的共同點比差異重要：<strong>運算在資料庫那一端發生</strong>，程式收到的是算完的結果。編譯出來的 SQL 可以直接印出來看：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln">1</span><span class="cl"><span class="kn">from</span> <span class="nn">sqlalchemy</span> <span class="kn">import</span> <span class="n">select</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">stmt</span> <span class="o">=</span> <span class="n">select</span><span class="p">(</span><span class="n">Customer</span><span class="p">)</span><span class="o">.</span><span class="n">where</span><span class="p">(</span><span class="n">Customer</span><span class="o">.</span><span class="n">age</span> <span class="o">&gt;</span> <span class="mi">18</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">stmt</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="n">engine</span><span class="p">))</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># SELECT customer.id, customer.name, customer.age FROM customer WHERE customer.age &gt; ?</span></span></span></code></pre></div><h2 id="記憶體端pandas-與-dataframe">記憶體端：pandas 與 DataFrame</h2>
<p>搬進記憶體端的做法是把資料整批載進程式，運算由程式自己完成。pandas 屬於這一側，它的容器是 <a href="/blog/backend/01-database/sql/knowledge-cards/dataframe/" data-link-title="DataFrame（資料框）" data-link-desc="SQL 教材拿 pandas 或資料框當對照時，查 DataFrame 是什麼、它與資料表差在哪">DataFrame</a>。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">df</span><span class="p">[</span><span class="n">df</span><span class="p">[</span><span class="s2">&#34;age&#34;</span><span class="p">]</span> <span class="o">&gt;</span> <span class="mi">18</span><span class="p">]</span></span></span></code></pre></div><p>這一行沒有連線、沒有查詢語句、沒有資料庫。<code>df</code> 是一張已經在記憶體裡的表格，這一行做的是對一個布林陣列取值。資料的來源可以是資料庫，也可以是 CSV、日誌檔或某個 API 回傳的內容——載進來之後，接下來的每一步運算都寫成一樣的形式，來源是哪一種不再影響寫法。</p>
<h2 id="兩端分工的條件資料量sql-的表達能力算完之後的用途">兩端分工的條件：資料量、SQL 的表達能力、算完之後的用途</h2>
<p><strong>資料量。</strong> 記憶體端要把資料整批放進來，所以容量是硬上限；資料庫端沒有這個限制，它一次只需要把符合條件的部分送回來。</p>
<p><strong>要算的運算 SQL 表達得出來嗎。</strong> 篩選、合併、分組彙總、排名，SQL 都寫得出來。統計檢定、時間序列的滑動窗口、把資料餵給模型，SQL 寫起來吃力或根本沒有對應語法，這些落在記憶體端。</p>
<p><strong>算完之後還要做什麼。</strong> 結果如果只是要回傳給使用者或寫回另一張表，留在資料庫端的路徑最短——資料不必先進記憶體再送出去；如果後面還要接繪圖、統計或模型訓練，資料終究要進記憶體，那就沒有理由分兩段做。</p>
<p>實務上的常見形態是兩端接力：用 SQL 把資料量先壓下來，再把壓縮過的結果搬進記憶體做後面的部分。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="o">.</span><span class="n">read_sql</span><span class="p">(</span><span class="s2">&#34;SELECT * FROM orders WHERE year = 2024&#34;</span><span class="p">,</span> <span class="n">conn</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># 這一行之後全部在記憶體裡，與資料庫無關</span></span></span></code></pre></div><h2 id="duckdb在同一個程序裡用-sql-查-dataframe-與檔案">DuckDB：在同一個程序裡用 SQL 查 DataFrame 與檔案</h2>
<p>DuckDB 兩邊都站：它接受 SQL，而查詢的對象可以是記憶體裡的 DataFrame，也可以是磁碟上的檔案。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">duckdb</span><span class="o">.</span><span class="n">sql</span><span class="p">(</span><span class="s2">&#34;SELECT dept, count(*) c FROM emp GROUP BY dept&#34;</span><span class="p">)</span>  <span class="c1"># emp 是一個 DataFrame</span></span></span></code></pre></div><p>它把「資料庫」這個角色從一台獨立的伺服器換成同一個程序裡的運算引擎，而軸本身仍在——差別只在資料要不要複製一份進 Python 的記憶體。</p>
<h2 id="延伸閱讀orm-與-dataframe-的分辨sql-與-pandas-的寫法對應pandas-的記憶體邊界資料庫端的工程議題">延伸閱讀：ORM 與 DataFrame 的分辨、SQL 與 pandas 的寫法對應、pandas 的記憶體邊界、資料庫端的工程議題</h2>
<p><strong>從回傳值認出手上是哪一類</strong>：資料庫端的工具回傳的是物件或資料列，記憶體端的工具回傳的是一張新的表。<a href="/blog/programming/python/basic/08-data-analysis/orm-and-dataframe/" data-link-title="8.3 ORM 與 DataFrame 的分辨：回傳值的形狀、連線物件與查詢送出的時機" data-link-desc="ORM 產生查詢、DataFrame 執行運算，以及外觀相似的方法串接底下差了什麼">8.3 ORM 與 DataFrame 的分辨：回傳值的形狀、連線物件與查詢送出的時機</a> 寫兩者各自產出什麼，以及外觀相似的方法串接底下差了什麼。</p>
<p><strong>兩端共用的那套抽象</strong>：篩選、合併、分組彙總在兩端都有，而且對應得起來。<a href="/blog/programming/python/basic/08-data-analysis/same-relational-algebra/" data-link-title="8.4 SQL 子句與 pandas 方法的對應：篩選、連接、分組彙總、視窗函數，以及空值、索引與順序的語意差異" data-link-desc="SQL 與 DataFrame 共用的關聯代數、同一份資料上兩邊寫法的實測比對，以及照著逐句翻譯會靜默失準的位置">8.4 SQL 子句與 pandas 方法的對應：篩選、連接、分組彙總、視窗函數，以及空值、索引與順序的語意差異</a> 給篩選、連接、分組彙總與視窗函數在兩邊的實測比對，以及空值、索引與順序這幾處對應斷掉的位置。</p>
<p><strong>記憶體端的容量上限</strong>：<a href="/blog/programming/python/basic/08-data-analysis/memory-is-the-boundary/" data-link-title="8.5 pandas 的記憶體邊界：資料與運算過程的佔用估算，推回資料庫、換引擎與分散式的代價" data-link-desc="數值欄與字串欄的佔用怎麼得出、合併這類操作的中間結果佔多少，以及記憶體不足與跑得慢這兩種失敗怎麼分辨">8.5 pandas 的記憶體邊界：資料與運算過程的佔用估算，推回資料庫、換引擎與分散式的代價</a> 給資料量的估算方法、運算過程額外要的空間，以及越過之後三個方向各自付什麼代價。</p>
<p><strong>資料庫端那一側的工程議題</strong>：連線池、交易邊界、一次請求該打幾次資料庫，在 <a href="/blog/backend/01-database/" data-link-title="模組一：資料庫與持久化" data-link-desc="整理 SQL、transaction、migration 與 repository adapter 的後端實務">backend 模組一</a>。那個模組從 N+1 與延遲載入這些形態進去，並給出每請求查詢次數預算當判讀基準。</p>
]]></content:encoded></item><item><title>模組八：整批資料的處理與分析</title><link>https://tarrragon.github.io/blog/programming/python/basic/08-data-analysis/</link><pubDate>Mon, 31 Aug 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/programming/python/basic/08-data-analysis/</guid><description>&lt;p>本模組處理的是資料整批進入程式之後的工具選擇。前七個模組寫的是程式碼本身的結構，這一個模組的軸換成資料：一次進來多少、運算發生在哪一端、超過記憶體之後往哪裡走。&lt;/p>
&lt;h2 id="讀者定位">讀者定位&lt;/h2>
&lt;p>有程式經驗、SQL 大致能寫，但沒有做過批次資料處理的工程師。Web 後端背景的讀者會發現這一側的工具跟熟悉的那一套幾乎沒有交集，而模組的第一篇就在解釋這件事。&lt;/p>
&lt;h2 id="章節">章節&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>篇&lt;/th>
 &lt;th>交付&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/programming/python/basic/08-data-analysis/data-unit-splits-work/" data-link-title="8.1 Python 的幾種工作與資料進入程式的單位：Web 後端、資料工程與分析、機器學習、自動化與系統工具" data-link-desc="資料以一筆還是一整批進入程式，各組工作的工具鏈與失敗形態怎麼跟著這個單位分開">8.1 Python 的幾種工作與資料進入程式的單位&lt;/a>&lt;/td>
 &lt;td>分開幾種 Python 工作的那條軸，以及各自的工具鏈由它推導出來&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/programming/python/basic/08-data-analysis/where-computation-runs/" data-link-title="8.2 運算發生的位置：資料庫端與記憶體端的工具、分工條件與 DuckDB" data-link-desc="運算留在資料庫端或搬進記憶體端這條分界，怎麼決定工具的形狀、容量上限與失敗形態">8.2 運算發生的位置&lt;/a>&lt;/td>
 &lt;td>資料庫端與記憶體端的分界，本模組其餘各篇的推導源頭&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/programming/python/basic/08-data-analysis/orm-and-dataframe/" data-link-title="8.3 ORM 與 DataFrame 的分辨：回傳值的形狀、連線物件與查詢送出的時機" data-link-desc="ORM 產生查詢、DataFrame 執行運算，以及外觀相似的方法串接底下差了什麼">8.3 ORM 與 DataFrame 的分辨：回傳值的形狀、連線物件與查詢送出的時機&lt;/a>&lt;/td>
 &lt;td>兩者各自產出什麼，以及從回傳值認出手上是哪一類的方法&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/programming/python/basic/08-data-analysis/same-relational-algebra/" data-link-title="8.4 SQL 子句與 pandas 方法的對應：篩選、連接、分組彙總、視窗函數，以及空值、索引與順序的語意差異" data-link-desc="SQL 與 DataFrame 共用的關聯代數、同一份資料上兩邊寫法的實測比對，以及照著逐句翻譯會靜默失準的位置">8.4 SQL 子句與 pandas 方法的對應&lt;/a>&lt;/td>
 &lt;td>SQL 與 DataFrame 四組對應的實測，以及對應斷掉的位置&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/programming/python/basic/08-data-analysis/memory-is-the-boundary/" data-link-title="8.5 pandas 的記憶體邊界：資料與運算過程的佔用估算，推回資料庫、換引擎與分散式的代價" data-link-desc="數值欄與字串欄的佔用怎麼得出、合併這類操作的中間結果佔多少，以及記憶體不足與跑得慢這兩種失敗怎麼分辨">8.5 pandas 的記憶體邊界&lt;/a>&lt;/td>
 &lt;td>資料量的估算法，與越過之後三個方向各自付什麼代價&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="推導源頭">推導源頭&lt;/h2>
&lt;p>各篇的判斷標準都折算回 8.2 的那條軸：運算發生在資料庫端還是程式的記憶體端。8.3 是這條軸兩側的產物比對，8.4 是兩側共用的抽象，8.5 是記憶體端那一側的容量上限。8.1 在軸之前，負責說明為什麼會有這條軸要分。&lt;/p>
&lt;h2 id="跨分類引用">跨分類引用&lt;/h2>
&lt;ul>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/backend/01-database/" data-link-title="模組一：資料庫與持久化" data-link-desc="整理 SQL、transaction、migration 與 repository adapter 的後端實務">backend 模組一 資料庫&lt;/a>：運算留在資料庫端那一側的工程議題，N+1 與交易邊界都在那裡&lt;/li>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/backend/01-database/sql/" data-link-title="SQL：這個語言為什麼長這樣" data-link-desc="SQL 的設計而不是它的語法表：子句為什麼這樣切、一個查詢該怎麼讀，以及代價落在哪裡">SQL：這個語言為什麼長這樣&lt;/a>：資料庫端那一側用的語言本身——子句怎麼切、連接的左右決定誰被保護、代價為什麼不在查詢的文字裡&lt;/li>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/devops/monitoring/08-business-analytics/" data-link-title="模組八：行為資料的商業利用" data-link-desc="Funnel / Cohort / Attribution / A/B test / 推薦系統 / RFM — 從 debug 工具到商業資產的翻轉">monitoring 模組八 行為資料的商業利用&lt;/a>：批次算出來的指標怎麼定義，以及事件屬性決定哪些指標算得出來&lt;/li>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/programming/python/basic/03-stdlib/" data-link-title="模組三：標準庫實戰" data-link-desc="Python 標準庫的常用模組實戰應用">python 模組三 標準庫實戰&lt;/a>：自動化那一格用到的工具，處理的單位是檔案與程序而不是資料集&lt;/li>
&lt;/ul>
&lt;h2 id="backlog">Backlog&lt;/h2>
&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>Polars 與 pandas 的 API 差異對照&lt;/td>
 &lt;td>主章&lt;/td>
 &lt;td>8.5 完成&lt;/td>
 &lt;td>中&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>dbt 與 Airflow 在批次流程裡的位置&lt;/td>
 &lt;td>主章&lt;/td>
 &lt;td>需先確定與 backend 的分界&lt;/td>
 &lt;td>中&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>髒資料處理：型別、缺值、重複列&lt;/td>
 &lt;td>主章&lt;/td>
 &lt;td>無&lt;/td>
 &lt;td>中&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table></description><content:encoded><![CDATA[<p>本模組處理的是資料整批進入程式之後的工具選擇。前七個模組寫的是程式碼本身的結構，這一個模組的軸換成資料：一次進來多少、運算發生在哪一端、超過記憶體之後往哪裡走。</p>
<h2 id="讀者定位">讀者定位</h2>
<p>有程式經驗、SQL 大致能寫，但沒有做過批次資料處理的工程師。Web 後端背景的讀者會發現這一側的工具跟熟悉的那一套幾乎沒有交集，而模組的第一篇就在解釋這件事。</p>
<h2 id="章節">章節</h2>
<table>
  <thead>
      <tr>
          <th>篇</th>
          <th>交付</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/programming/python/basic/08-data-analysis/data-unit-splits-work/" data-link-title="8.1 Python 的幾種工作與資料進入程式的單位：Web 後端、資料工程與分析、機器學習、自動化與系統工具" data-link-desc="資料以一筆還是一整批進入程式，各組工作的工具鏈與失敗形態怎麼跟著這個單位分開">8.1 Python 的幾種工作與資料進入程式的單位</a></td>
          <td>分開幾種 Python 工作的那條軸，以及各自的工具鏈由它推導出來</td>
      </tr>
      <tr>
          <td><a href="/blog/programming/python/basic/08-data-analysis/where-computation-runs/" data-link-title="8.2 運算發生的位置：資料庫端與記憶體端的工具、分工條件與 DuckDB" data-link-desc="運算留在資料庫端或搬進記憶體端這條分界，怎麼決定工具的形狀、容量上限與失敗形態">8.2 運算發生的位置</a></td>
          <td>資料庫端與記憶體端的分界，本模組其餘各篇的推導源頭</td>
      </tr>
      <tr>
          <td><a href="/blog/programming/python/basic/08-data-analysis/orm-and-dataframe/" data-link-title="8.3 ORM 與 DataFrame 的分辨：回傳值的形狀、連線物件與查詢送出的時機" data-link-desc="ORM 產生查詢、DataFrame 執行運算，以及外觀相似的方法串接底下差了什麼">8.3 ORM 與 DataFrame 的分辨：回傳值的形狀、連線物件與查詢送出的時機</a></td>
          <td>兩者各自產出什麼，以及從回傳值認出手上是哪一類的方法</td>
      </tr>
      <tr>
          <td><a href="/blog/programming/python/basic/08-data-analysis/same-relational-algebra/" data-link-title="8.4 SQL 子句與 pandas 方法的對應：篩選、連接、分組彙總、視窗函數，以及空值、索引與順序的語意差異" data-link-desc="SQL 與 DataFrame 共用的關聯代數、同一份資料上兩邊寫法的實測比對，以及照著逐句翻譯會靜默失準的位置">8.4 SQL 子句與 pandas 方法的對應</a></td>
          <td>SQL 與 DataFrame 四組對應的實測，以及對應斷掉的位置</td>
      </tr>
      <tr>
          <td><a href="/blog/programming/python/basic/08-data-analysis/memory-is-the-boundary/" data-link-title="8.5 pandas 的記憶體邊界：資料與運算過程的佔用估算，推回資料庫、換引擎與分散式的代價" data-link-desc="數值欄與字串欄的佔用怎麼得出、合併這類操作的中間結果佔多少，以及記憶體不足與跑得慢這兩種失敗怎麼分辨">8.5 pandas 的記憶體邊界</a></td>
          <td>資料量的估算法，與越過之後三個方向各自付什麼代價</td>
      </tr>
  </tbody>
</table>
<h2 id="推導源頭">推導源頭</h2>
<p>各篇的判斷標準都折算回 8.2 的那條軸：運算發生在資料庫端還是程式的記憶體端。8.3 是這條軸兩側的產物比對，8.4 是兩側共用的抽象，8.5 是記憶體端那一側的容量上限。8.1 在軸之前，負責說明為什麼會有這條軸要分。</p>
<h2 id="跨分類引用">跨分類引用</h2>
<ul>
<li>→ <a href="/blog/backend/01-database/" data-link-title="模組一：資料庫與持久化" data-link-desc="整理 SQL、transaction、migration 與 repository adapter 的後端實務">backend 模組一 資料庫</a>：運算留在資料庫端那一側的工程議題，N+1 與交易邊界都在那裡</li>
<li>→ <a href="/blog/backend/01-database/sql/" data-link-title="SQL：這個語言為什麼長這樣" data-link-desc="SQL 的設計而不是它的語法表：子句為什麼這樣切、一個查詢該怎麼讀，以及代價落在哪裡">SQL：這個語言為什麼長這樣</a>：資料庫端那一側用的語言本身——子句怎麼切、連接的左右決定誰被保護、代價為什麼不在查詢的文字裡</li>
<li>→ <a href="/blog/devops/monitoring/08-business-analytics/" data-link-title="模組八：行為資料的商業利用" data-link-desc="Funnel / Cohort / Attribution / A/B test / 推薦系統 / RFM — 從 debug 工具到商業資產的翻轉">monitoring 模組八 行為資料的商業利用</a>：批次算出來的指標怎麼定義，以及事件屬性決定哪些指標算得出來</li>
<li>→ <a href="/blog/programming/python/basic/03-stdlib/" data-link-title="模組三：標準庫實戰" data-link-desc="Python 標準庫的常用模組實戰應用">python 模組三 標準庫實戰</a>：自動化那一格用到的工具，處理的單位是檔案與程序而不是資料集</li>
</ul>
<h2 id="backlog">Backlog</h2>
<table>
  <thead>
      <tr>
          <th>項目</th>
          <th>類型</th>
          <th>前置條件</th>
          <th>規模</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Polars 與 pandas 的 API 差異對照</td>
          <td>主章</td>
          <td>8.5 完成</td>
          <td>中</td>
      </tr>
      <tr>
          <td>dbt 與 Airflow 在批次流程裡的位置</td>
          <td>主章</td>
          <td>需先確定與 backend 的分界</td>
          <td>中</td>
      </tr>
      <tr>
          <td>髒資料處理：型別、缺值、重複列</td>
          <td>主章</td>
          <td>無</td>
          <td>中</td>
      </tr>
  </tbody>
</table>
]]></content:encoded></item></channel></rss>