圖:“Rust 的吉祥物 Ferris the Crab 雙鉗狂寫卷軸文書”,gemini-2.5-flash-preview,2025年09月19日。
身為資料科學家、AI 工程師出生,長期泡在 Python 生態系中,一定知道 pandas
這個套件,但面對逐漸龐大的資料流量,一次性大量、大量且頻繁查詢或處理,pandas
實在是越來越吃不消了。大筆資料檢索、複雜邏輯操作,都會耗費掉大量的計算資源與記憶體,進而壓縮單位時間能夠處理的流量與請求,成本也增加,地獄螺旋,煩不完啊....
polars
:我來解救你!
polars
是一個從頭開始用 Rust 撰寫的、極度快速的 DataFrame 函數庫,專為高效能的記憶體內 (in-memory) 資料處理與分析而設計。Polars 官方網站 強大的專案大概率都有強大技術文件,實際上他們的技術文件已經非常完整、淺顯易懂了,實作面也已經相當成熟,除了原生 Rust 生態,也有以 python wrapper 的方式推出 python 的函數庫,已成為當下資料處理界不能不知道的角色之一,pip install polars
就可使用。今天這篇文章,會揭露 polars
如此高效能的幾點核心原因。
這是理解 Polars 一切高效能的基石。
傳統的「列式儲存」(Row-based) 就像通訊錄:
Polars 的「行式儲存」(Columnar) 就像分頁的通訊錄:
單指令多資料流 (Single Instruction, Multiple Data) 可以想像成一個「超寬的計算通道」。
基於 python 的 pandas
,就受限於 Python 的全域直譯器鎖 (GIL),即使在多核心 CPU 上,同一個時間點也只有一個執行緒能真正執行 Python 位元組碼,這使得 Pandas 在並行化上舉步維艱。
Rust 就不同了,因為:
Apache Arrow 格式整理好數據,讓資料適合被分析 -> SIMD 在單一核心內進行向量化加速 -> 多核心並行,將任務分配到所有核心。三核心相輔相成,Polars 的極致效能是必然的。
[dependencies]
polars = { version = "0.51.0", features = [
"lazy", # 啟用懶載入功能
"csv", # CSV 檔案處理
"dtype-full" # 完整資料類型支援
] }
use polars::prelude::*;
fn main() -> Result<(), PolarsError> {
demo_lazy()?; // 範例一:Lazy API
demo_aggregation()?; // 範例二:資料聚合操作
demo_with_columns()?; // 範例三:列操作
demo_groupby_and_window()?; // 範例四:分組與視窗操作
Ok(())
}
demo_lazy()
- Lazy APIfn demo_lazy() -> Result<(), PolarsError> {
// 建立以 polars 的 df! 宏建立 DataFrame
let df = df![
"category" => &["A", "A", "B", "C", "B", "A"],
"values" => &[10, 20, 5, 40, 50, 60],
"counts" => &[1, 2, 3, 1, 5, 3]
]?;
// 建立查詢計算(尚未執行任何計算)
let lazy_plan = df.lazy()
.filter(col("category").eq(lit("A"))) // 過濾條件一:取得等於 “A” 的資料
.filter(col("counts").gt(lit(1))) // 過濾條件二:取得大於 1 的資料
.select([col("values").sum()]); // 選擇條件:“values” 欄位進行累加
// Polars 會對上面定義好的鏈路邏輯進行重新排列等優化操作,
// 亦不會建立中間結果,所以能夠取得最佳化效能(計算+記憶體)
let result_lazy = lazy_plan.collect()?;
let sum_lazy = result_lazy.column("values")?.get(0)?.try_extract::<i32>()?;
println!("[Lazy 模式] 'A' 類別且 'counts' > 1 的 'values' 總和: {:?}", sum_lazy);
Ok(())
}
[Lazy 模式] 'A' 類別且 'counts' > 1 的 'values' 總和: 80
demo_aggregation()
- 資料聚合操作fn demo_aggregation() -> Result<(), PolarsError> {
// 建立以 polars 的 df! 宏建立 DataFrame
let df = df![
"category" => &["A", "A", "B", "B", "C", "C"],
"value" => &[10, 20, 30, 40, 50, 60]
]?;
// 使用 Lazy API 進行分組聚合(定義->優化->一次執行)
let result = df.lazy()
.group_by(["category"]) // 按照 “category” 欄位進行分組
.agg([ // 開始聚合操作,針對同類別進行一下邏輯操作
col("value").sum().alias("total"), // 加總後欄位名為 “total”
col("value").mean().alias("average"), // 平均後欄位名為 “average”
col("value").count().alias("count") // 計數後欄位名為 “count”
])
.sort(["category"], Default::default()) // 排序
.collect()?; // 優化,一次執行
println!("\n[Aggregation] 分組統計結果:\n{}", result);
Ok(())
}
[Aggregation] 分組統計結果:
shape: (3, 4)
┌──────────┬───────┬─────────┬───────┐
│ category ┆ total ┆ average ┆ count │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ i32 ┆ f64 ┆ u32 │
╞══════════╪═══════╪═════════╪═══════╡
│ A ┆ 30 ┆ 15.0 ┆ 2 │
│ B ┆ 70 ┆ 35.0 ┆ 2 │
│ C ┆ 110 ┆ 55.0 ┆ 2 │
└──────────┴───────┴─────────┴───────┘
demo_with_columns()
- 列操作fn demo_with_columns() -> Result<(), PolarsError> {
// 建立以 polars 的 df! 宏建立 DataFrame
let df = df![
"A" => &[1.0, 2.0, 3.0, 4.0, 5.0],
"B" => &[5.0, 4.0, 3.0, 2.0, 1.0],
"text" => &["hello", "world", "polars", "is", "fast"]
]?;
// 在一個 with_columns 上下文中,同時定義多個新欄位的產生邏輯
let df_transformed = df.lazy()
.with_columns(&[
// 算術操作
(col("A") + col("B")).alias("A_plus_B"),
// 條件邏輯
when(col("A").gt(lit(3.0)))
.then(lit("high"))
.otherwise(lit("low"))
.alias("A_category"),
])
.collect()?;
println!("\n[with_columns] 一次性新增多個欄位:\n{}", df_transformed);
Ok(())
}
[with_columns] 一次性新增多個欄位:
shape: (5, 5)
┌─────┬─────┬────────┬──────────┬────────────┐
│ A ┆ B ┆ text ┆ A_plus_B ┆ A_category │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ f64 ┆ f64 ┆ str ┆ f64 ┆ str │
╞═════╪═════╪════════╪══════════╪════════════╡
│ 1.0 ┆ 5.0 ┆ hello ┆ 6.0 ┆ low │
│ 2.0 ┆ 4.0 ┆ world ┆ 6.0 ┆ low │
│ 3.0 ┆ 3.0 ┆ polars ┆ 6.0 ┆ low │
│ 4.0 ┆ 2.0 ┆ is ┆ 6.0 ┆ high │
│ 5.0 ┆ 1.0 ┆ fast ┆ 6.0 ┆ high │
└─────┴─────┴────────┴──────────┴────────────┘
demo_groupby_and_window()
- 分組與視窗操作fn demo_groupby_and_window() -> Result<(), PolarsError> {
// 建立以 polars 的 df! 宏建立 DataFrame
let df = df![
"department" => &["HR", "IT", "IT", "HR", "Sales", "Sales", "IT"],
"employee" => &["Alice", "Bob", "Charlie", "David", "Eve", "Frank", "Grace"],
"salary" => &[60000, 80000, 95000, 75000, 120000, 110000, 150000]
]?;
// --- GroupBy 操作 ---
// 對 'department' 進行分組,並一次性計算多個聚合指標
let aggregated_df = df.clone().lazy()
.group_by(["department"])
.agg(&[
col("salary").sum().alias("total_salary"),
col("salary").mean().alias("average_salary"),
col("employee").count().alias("employee_count"),
])
.sort(["department"], Default::default())
.collect()?;
println!("\n[GroupBy] 按部門聚合計算:\n{}", aggregated_df);
// --- Window Function 操作 ---
// 在不改變 DataFrame 形狀的情況下,計算每個員工薪水佔其部門總薪水的百分比
let window_df = df.lazy()
.with_columns(&[
// over([..]) 定義了計算的 "視窗" 或 "分區"
(col("salary") * lit(100.0) / col("salary").sum().over(["department"]))
.alias("salary_%_of_department"),
// 簡化排名計算 - 移除複雜的排名功能
(col("salary") / col("salary").max().over(["department"]))
.alias("salary_ratio_in_department"),
])
.sort(["department", "salary"], Default::default())
.collect()?;
println!("\n[Window Function] 在部門內計算薪水佔比與排名:\n{}", window_df);
Ok(())
}
[GroupBy] 按部門聚合計算:
shape: (3, 4)
┌────────────┬──────────────┬────────────────┬────────────────┐
│ department ┆ total_salary ┆ average_salary ┆ employee_count │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ i32 ┆ f64 ┆ u32 │
╞════════════╪══════════════╪════════════════╪════════════════╡
│ HR ┆ 135000 ┆ 67500.0 ┆ 2 │
│ IT ┆ 325000 ┆ 108333.333333 ┆ 3 │
│ Sales ┆ 230000 ┆ 115000.0 ┆ 2 │
└────────────┴──────────────┴────────────────┴────────────────┘
[Window Function] 在部門內計算薪水佔比與排名:
shape: (7, 5)
┌────────────┬──────────┬────────┬────────────────────────┬───────────────────────┐
│ department ┆ employee ┆ salary ┆ salary_%_of_department ┆ salary_ratio_in_depar │
│ --- ┆ --- ┆ --- ┆ --- ┆ tment │
│ str ┆ str ┆ i32 ┆ f64 ┆ --- │
│ ┆ ┆ ┆ ┆ i32 │
╞════════════╪══════════╪════════╪════════════════════════╪═══════════════════════╡
│ HR ┆ Alice ┆ 60000 ┆ 44.444444 ┆ 0 │
│ HR ┆ David ┆ 75000 ┆ 55.555556 ┆ 1 │
│ IT ┆ Bob ┆ 80000 ┆ 24.615385 ┆ 0 │
│ IT ┆ Charlie ┆ 95000 ┆ 29.230769 ┆ 0 │
│ IT ┆ Grace ┆ 150000 ┆ 46.153846 ┆ 1 │
│ Sales ┆ Frank ┆ 110000 ┆ 47.826087 ┆ 0 │
│ Sales ┆ Eve ┆ 120000 ┆ 52.173913 ┆ 1 │
└────────────┴──────────┴────────┴────────────────────────┴───────────────────────┘
Rust 開發者工具箱累積的第一個超實用工具,用了幾個簡單的範例初探了 polars
的新型態的資料處理邏輯,身為資料科學家、AI 工程師這一定不能錯過,是當代處理大數據的一大基石工具。持續增加我們手中的工具吧!
https://github.com/liren0907/rust_one