一. 前言 #
嗨,大家好!我是拍拍君 🎉
做資料分析的 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,相信你會愛上它的 🐻❄️
我們下次見!👋
延伸閱讀 #
- Polars 官方文件
- Polars User Guide
- Polars vs Pandas Benchmark
- Python httpx 完全攻略 — 拍拍君的 HTTP 客戶端教學