快轉到主要內容
  1. 教學文章/

Polars:比 Pandas 快 10 倍的 DataFrame 新選擇

·6 分鐘· loading · loading · ·
Python Polars Dataframe 資料分析 Rust
每日拍拍
作者
每日拍拍
科學家 X 科技宅宅
目錄
Python 學習 - 本文屬於一個選集。
§ 21: 本文

一. 前言
#

嗨,大家好!我是拍拍君 🎉

做資料分析的 Python 開發者,應該都跟 Pandas 相處過很長一段時間吧?Pandas 功能強大、生態完整,可以說是資料科學的基石。

但你有沒有遇過這些問題:

  • 📊 資料量超過幾百萬筆,Pandas 開始卡得要命
  • 🧠 記憶體直接爆掉,MemoryError 跟你說掰掰
  • 🐌 一個簡單的 groupby 操作等了好幾分鐘
  • 🤯 API 有太多奇怪的陷阱(inplace=True 到底有沒有用?)

如果你也有這些困擾,那一定要認識 Polars

Polars 是用 Rust 寫的高效能 DataFrame 函式庫,主打:

  • ⚡ 速度比 Pandas 快 10-100 倍(不是吹的,真的有 benchmark)
  • 💾 記憶體使用更少(Apache Arrow 格式)
  • 🧩 Lazy Evaluation 自動優化查詢計畫
  • 🔒 表達式 API 設計一致,不容易踩坑

今天就讓拍拍君帶你從零開始學 Polars,保證學完就回不去了 🚀

二. 安裝
#

用 pip 安裝超簡單:

pip install polars

如果你用 uv(拍拍君大推!),那就更快:

uv pip install polars

想要完整功能(包含讀取 Excel、連接資料庫等):

pip install 'polars[all]'

安裝完,確認一下版本:

import polars as pl
print(pl.__version__)
# 1.24.0(或更新的版本)

💡 Polars 的慣例 import 名稱是 pl,就像 Pandas 用 pd 一樣。

三. 基礎操作:建立 DataFrame
#

3.1 從字典建立
#

import polars as pl

df = pl.DataFrame({
    "name": ["拍拍君", "拍拍醬", "chatPTT", "小明"],
    "age": [25, 30, 28, 22],
    "city": ["台北", "東京", "舊金山", "台北"],
    "score": [95.5, 88.0, 92.3, 78.5],
})

print(df)

輸出:

shape: (4, 4)
┌─────────┬─────┬────────┬───────┐
│ name    ┆ age ┆ city   ┆ score │
│ ---     ┆ --- ┆ ---    ┆ ---   │
│ str     ┆ i64 ┆ str    ┆ f64   │
╞═════════╪═════╪════════╪═══════╡
│ 拍拍君  ┆ 25  ┆ 台北   ┆ 95.5  │
│ 拍拍醬  ┆ 30  ┆ 東京   ┆ 88.0  │
│ chatPTT ┆ 28  ┆ 舊金山 ┆ 92.3  │
│ 小明    ┆ 22  ┆ 台北   ┆ 78.5  │
└─────────┴─────┴────────┴───────┘

有沒有覺得這個表格輸出超漂亮?比 Pandas 清楚多了 ✨

3.2 從 CSV 讀取
#

df = pl.read_csv("data.csv")

# 也支援 parquet(推薦,更快更小!)
df = pl.read_parquet("data.parquet")

3.3 型別系統
#

Polars 的型別系統比 Pandas 嚴格得多,不會把整數悄悄變成浮點數:

Polars 型別 說明 Pandas 對應
Int64 64 位整數 int64
Float64 64 位浮點數 float64
Utf8 / String 字串 object
Boolean 布林 bool
Date 日期 datetime64
Datetime 日期時間 datetime64
Duration 時間差 timedelta64
List 列表(巢狀) 不支援

💡 Polars 原生支援巢狀的 List 型別,這在 Pandas 裡要用 object 硬塞,超不方便。

四. 選取與篩選:Expression API 的威力
#

Polars 最大的特色就是它的 Expression API——所有操作都是用「表達式」組合起來的,可讀性高、效能也好。

4.1 選取欄位
#

# 選取特定欄位
df.select("name", "score")

# 用表達式做計算
df.select(
    pl.col("name"),
    pl.col("score").round(0).alias("rounded_score"),
    (pl.col("age") * 2).alias("double_age"),
)

4.2 篩選資料
#

# 篩選年齡大於 25 的人
df.filter(pl.col("age") > 25)

# 多重條件
df.filter(
    (pl.col("age") > 22) & (pl.col("city") == "台北")
)

4.3 排序
#

# 按分數降序排列
df.sort("score", descending=True)

# 多欄位排序
df.sort(["city", "score"], descending=[False, True])

4.4 新增欄位
#

df = df.with_columns(
    (pl.col("score") / 100 * pl.col("age")).alias("weighted"),
    pl.col("name").str.len_chars().alias("name_length"),
    pl.lit("🐍").alias("emoji"),
)

🔑 Polars 的 DataFrame 是 immutable(不可變的),所有操作都回傳新的 DataFrame。不像 Pandas 有 inplace=True 這種讓人困惑的選項。

五. GroupBy 與聚合
#

GroupBy 是資料分析最常用的操作,Polars 在這裡的效能優勢特別明顯。

5.1 基本聚合
#

df.group_by("city").agg(
    pl.col("score").mean().alias("avg_score"),
    pl.col("age").min().alias("youngest"),
    pl.col("name").count().alias("count"),
)

輸出:

shape: (3, 4)
┌────────┬───────────┬──────────┬───────┐
│ city   ┆ avg_score ┆ youngest ┆ count │
│ ---    ┆ ---       ┆ ---      ┆ ---   │
│ str    ┆ f64       ┆ i64      ┆ u32   │
╞════════╪═══════════╪══════════╪═══════╡
│ 台北   ┆ 87.0      ┆ 22       ┆ 2     │
│ 東京   ┆ 88.0      ┆ 30       ┆ 1     │
│ 舊金山 ┆ 92.3      ┆ 28       ┆ 1     │
└────────┴───────────┴──────────┴───────┘

5.2 Window Functions
#

Pandas 裡要用 transform 才能做到的事,Polars 用 over 就搞定:

df.with_columns(
    pl.col("score").mean().over("city").alias("city_avg"),
    pl.col("score").rank().over("city").alias("city_rank"),
)

💡 over() 就是 SQL 的 OVER (PARTITION BY ...),語義更直覺。

六. Lazy Evaluation:Polars 的殺手鐧
#

這是 Polars 最強大的功能之一——Lazy Evaluation(惰性求值)。

6.1 觀念
#

Eager 模式(預設),每一行操作都會立刻執行。但在 Lazy 模式,Polars 會先把所有操作記錄下來,形成一個查詢計畫,然後一次性優化並執行。

# Eager 模式 — 每步都立刻計算
result = (
    df
    .filter(pl.col("age") > 22)
    .select("name", "score")
    .sort("score", descending=True)
)

# Lazy 模式 — 先建立計畫,最後才計算
result = (
    df.lazy()
    .filter(pl.col("age") > 22)
    .select("name", "score")
    .sort("score", descending=True)
    .collect()  # 這裡才真正執行!
)

6.2 Lazy 的好處
#

Polars 的查詢優化器會自動幫你:

  • Predicate Pushdown:把 filter 儘早執行,減少處理的資料量
  • Projection Pushdown:只讀取需要的欄位,省記憶體
  • Common Subexpression Elimination:避免重複計算
  • Parallel Execution:自動利用多核心平行處理

6.3 查看查詢計畫
#

lazy_df = (
    pl.scan_csv("big_data.csv")  # scan 而非 read,直接進入 lazy 模式
    .filter(pl.col("amount") > 1000)
    .group_by("category")
    .agg(pl.col("amount").sum())
)

# 查看優化前的計畫
print(lazy_df.explain())

# 查看優化後的計畫
print(lazy_df.explain(optimized=True))

# 執行
result = lazy_df.collect()

🚀 對於大資料集,用 pl.scan_csv() + .collect()pl.read_csv() 快非常多,因為它可以做 projection pushdown——只讀需要的欄位。

6.4 實戰:處理大型 CSV
#

假設你有一個 10 GB 的 CSV,用 Pandas 根本讀不進記憶體,但 Polars 可以:

result = (
    pl.scan_csv("huge_file.csv")
    .filter(pl.col("status") == "active")
    .select("user_id", "amount", "created_at")
    .group_by("user_id")
    .agg(
        pl.col("amount").sum().alias("total"),
        pl.col("created_at").max().alias("last_active"),
    )
    .sort("total", descending=True)
    .head(100)
    .collect()
)

print(result)

這段程式碼不管檔案多大都不會 OOM,因為 Polars 會串流處理!

七. Polars vs Pandas 快速比較
#

操作 Pandas Polars
Import import pandas as pd import polars as pl
建立 DataFrame pd.DataFrame(dict) pl.DataFrame(dict)
選取欄位 df[["a", "b"]] df.select("a", "b")
篩選 df[df["age"] > 25] df.filter(pl.col("age") > 25)
新增欄位 df["new"] = ... df.with_columns(...)
GroupBy df.groupby("x").agg(...) df.group_by("x").agg(...)
排序 df.sort_values("x") df.sort("x")
Lazy ❌ 不支援 df.lazy() + .collect()
多執行緒 ❌ GIL 限制 ✅ 原生多核心
記憶體格式 NumPy Apache Arrow

💡 Polars 不是要「取代」Pandas——如果你的資料量小、用 Pandas 跑得順,那完全沒問題。但當你碰到效能瓶頸,Polars 會是你的救星。

八. 實用小技巧
#

8.1 與 Pandas 互轉
#

# Polars → Pandas
pandas_df = polars_df.to_pandas()

# Pandas → Polars
polars_df = pl.from_pandas(pandas_df)

8.2 字串操作
#

df.with_columns(
    pl.col("name").str.to_uppercase().alias("upper_name"),
    pl.col("city").str.contains("台").alias("has_tai"),
    pl.col("name").str.replace("拍拍", "嗶嗶").alias("replaced"),
)

8.3 日期處理
#

df = pl.DataFrame({
    "date": ["2026-01-15", "2026-02-20", "2026-03-10"],
    "value": [100, 200, 300],
}).with_columns(
    pl.col("date").str.to_date().alias("date"),
)

df.with_columns(
    pl.col("date").dt.month().alias("month"),
    pl.col("date").dt.weekday().alias("weekday"),
    pl.col("date").dt.strftime("%Y/%m/%d").alias("formatted"),
)

8.4 處理缺失值
#

df = pl.DataFrame({
    "a": [1, None, 3, None, 5],
    "b": ["x", "y", None, "w", None],
})

df.with_columns(
    pl.col("a").fill_null(0).alias("a_filled"),
    pl.col("b").fill_null("unknown").alias("b_filled"),
    pl.col("a").is_null().alias("a_is_null"),
)

結語
#

今天我們學了 Polars 的核心功能:

  • ✅ 建立和讀取 DataFrame
  • ✅ Expression API:select、filter、sort、with_columns
  • ✅ GroupBy 與 Window Functions
  • ✅ Lazy Evaluation 和查詢優化
  • ✅ 與 Pandas 的比較和互轉

Polars 的設計哲學就是「又快又乾淨」——API 一致、效能爆表、記憶體友善。拍拍君自從用了 Polars,處理大資料集的時候再也不用等那麼久了 ⚡

如果你覺得 Pandas 用得好好的也不急著換,完全沒問題!但建議你在下一個需要處理大資料的專案裡試試 Polars,相信你會愛上它的 🐻‍❄️

我們下次見!👋

延伸閱讀
#

Python 學習 - 本文屬於一個選集。
§ 21: 本文

相關文章

超快速 Python 套件管理:uv 完全教學
·6 分鐘· loading · loading
Python Uv Package Manager Rust
PyTorch 神經網路入門:從零開始建立你的第一個模型
·5 分鐘· loading · loading
Python Pytorch Neural-Network Deep-Learning Machine-Learning
Ruff:用 Rust 寫的 Python Linter,快到你會懷疑人生
·4 分鐘· loading · loading
Python Ruff Linter Formatter Code-Quality
少寫一半程式碼:dataclasses 讓你的 Python 類別煥然一新
·6 分鐘· loading · loading
Python Dataclasses Oop 標準庫
Python asyncio 非同步程式設計入門:讓你的程式不再傻等
·8 分鐘· loading · loading
Python Asyncio 非同步 並行
用 Typer 打造專業 CLI 工具:Python 命令列框架教學
·10 分鐘· loading · loading
Python Typer Cli 開發工具