iT邦幫忙

2025 iThome 鐵人賽

DAY 2
0
Software Development

Polars熊霸天下系列 第 2

[Day02] - 行前準備

  • 分享至 

  • xImage
  •  

今天我們來做一些學習前的準備工作。

本日大綱如下:

  1. 本日引入模組及準備工作
  2. 安裝Polars
  3. 工作環境
  4. 程式風格
  5. Config設定
  6. Config存取
  7. Context與expression
  8. Lazy模式
  9. API docs AI chatbot

0. 本日引入模組及準備工作

from datetime import date

import polars as pl


df = pl.DataFrame(
    {
        "col1": [1, 2, 3],
        "col2": ["x", "y", "z"],
        "col3": pl.date_range(
            date(2025, 1, 1), date(2025, 1, 3), eager=True
        ),
    }
)
shape: (3, 3)
┌──────┬──────┬────────────┐
│ col1 ┆ col2 ┆ col3       │
│ ---  ┆ ---  ┆ ---        │
│ i64  ┆ str  ┆ date       │
╞══════╪══════╪════════════╡
│ 1    ┆ x    ┆ 2025-01-01 │
│ 2    ┆ y    ┆ 2025-01-02 │
│ 3    ┆ z    ┆ 2025-01-03 │
└──────┴──────┴────────────┘

1. 安裝Polars

使用pip一鍵安裝:

pip install polars

2. 工作環境

建議使用傳統的Jupyter Notebook

但是如果您有著勇敢冒險精神的話,marimo絕對值得一試,而我會試著在[Day29]中簡單介紹marimo。

3. 程式風格

全部程式碼皆使用Ruff排版。針對引入模組,為節省版面空間,可能使用isort排版。

多數情況,我會使用Fluent API的方式來編寫query,也就是將操作串接在一起。相對於在每行行末加上\,用以接續多行,如:

df\
.select(pl.col("col1"))\
.filter(pl.col("col1").gt(0).and_(pl.col("col1").lt(3)))\
.sort(by="col1", descending=True)

我比較習慣在外頭包上一個(),如:

(
    df.select(pl.col("col1"))
    .filter(pl.col("col1").gt(0).and_(pl.col("col1").lt(3)))
    .sort(by="col1", descending=True)
)

最後,下面這種將不同操作指定給多個暫時變數的寫法,我會視為是一種anti-pattern。

selected_df = df.select(pl.col("col1"))
filtered_df = selected_df.filter(
    pl.col("col1").gt(0).and_(pl.col("col1").lt(3))
)
sorted_df = filtered_df.sort(by="col1", descending=True)

原因是定義多個暫時變數會增加記憶體消耗。

如果這些暫時變數在之後的程式中不會用到,僅是做為中間的計算過程,我會建議使用Fluent API的寫法。

4. Config設定

pl.Config可以幫助我們依照使用者喜好,在全域或是局部,調整Polars的參數。

以下舉一個我最常使用的例子,使用pl.Config.set_tbl_cols()調整顯示的列數。

全域設定

使用pl.Config.set_tbl_cols(2)在全域範圍內,將顯式列數設定為兩列。

pl.Config.set_tbl_cols(2)
df
shape: (3, 3)
┌──────┬───┬────────────┐
│ col1 ┆ … ┆ col3       │
│ ---  ┆   ┆ ---        │
│ i64  ┆   ┆ date       │
╞══════╪═══╪════════════╡
│ 1    ┆ … ┆ 2025-01-01 │
│ 2    ┆ … ┆ 2025-01-02 │
│ 3    ┆ … ┆ 2025-01-03 │
└──────┴───┴────────────

之後可以使用pl.Config.restore_defaults()在全域範圍內,恢復原始設定。

pl.Config.restore_defaults()  # global
df
shape: (3, 3)
┌──────┬──────┬────────────┐
│ col1 ┆ col2 ┆ col3       │
│ ---  ┆ ---  ┆ ---        │
│ i64  ┆ str  ┆ date       │
╞══════╪══════╪════════════╡
│ 1    ┆ x    ┆ 2025-01-01 │
│ 2    ┆ y    ┆ 2025-01-02 │
│ 3    ┆ z    ┆ 2025-01-03 │
└──────┴──────┴────────────┘

局部設定

可以使用context manager的型式,在局部設定顯示列數為兩列,如:

with pl.Config() as cfg:
    cfg.set_tbl_cols(2)
    print(df)
shape: (3, 3)
┌──────┬───┬────────────┐
│ col1 ┆ … ┆ col3       │
│ ---  ┆   ┆ ---        │
│ i64  ┆   ┆ date       │
╞══════╪═══╪════════════╡
│ 1    ┆ … ┆ 2025-01-01 │
│ 2    ┆ … ┆ 2025-01-02 │
│ 3    ┆ … ┆ 2025-01-03 │
└──────┴───┴────────────┘

此外,Polars還提供了另一種比較方便的寫法,讓使用者可以將參數設定於pl.Config中,如:

with pl.Config(tbl_cols=2):
    print(df)
shape: (3, 3)
┌──────┬───┬────────────┐
│ col1 ┆ … ┆ col3       │
│ ---  ┆   ┆ ---        │
│ i64  ┆   ┆ date       │
╞══════╪═══╪════════════╡
│ 1    ┆ … ┆ 2025-01-01 │
│ 2    ┆ … ┆ 2025-01-02 │
│ 3    ┆ … ┆ 2025-01-03 │
└──────┴───┴────────────┘

請留意,如果使用此種方法時,需要記得將開頭的「"set_"」去掉以做為參數。

5. Config存取

可以使用pl.Config.save_to_file()將常用的config存為JSON格式。

pl.Config.save_to_file("pl_config.json")
{
    "environment": {
        "POLARS_AUTO_STRUCTIFY": null,
        "POLARS_ENGINE_AFFINITY": null,
        "POLARS_FMT_MAX_COLS": null,
        "POLARS_FMT_MAX_ROWS": null,
        "POLARS_FMT_NUM_DECIMAL": null,
        "POLARS_FMT_NUM_GROUP_SEPARATOR": null,
        "POLARS_FMT_NUM_LEN": null,
        "POLARS_FMT_STR_LEN": null,
        "POLARS_FMT_TABLE_CELL_ALIGNMENT": null,
        "POLARS_FMT_TABLE_CELL_LIST_LEN": null,
        "POLARS_FMT_TABLE_CELL_NUMERIC_ALIGNMENT": null,
        "POLARS_FMT_TABLE_DATAFRAME_SHAPE_BELOW": null,
        "POLARS_FMT_TABLE_FORMATTING": null,
        "POLARS_FMT_TABLE_HIDE_COLUMN_DATA_TYPES": null,
        "POLARS_FMT_TABLE_HIDE_COLUMN_NAMES": null,
        "POLARS_FMT_TABLE_HIDE_COLUMN_SEPARATOR": null,
        "POLARS_FMT_TABLE_HIDE_DATAFRAME_SHAPE_INFORMATION": null,
        "POLARS_FMT_TABLE_INLINE_COLUMN_DATA_TYPE": null,
        "POLARS_FMT_TABLE_ROUNDED_CORNERS": null,
        "POLARS_MAX_EXPR_DEPTH": null,
        "POLARS_STREAMING_CHUNK_SIZE": null,
        "POLARS_TABLE_WIDTH": null,
        "POLARS_VERBOSE": null,
        "POLARS_WARN_UNSTABLE": null
    },
    "direct": {
        "set_fmt_float": "mixed",
        "set_float_precision": null,
        "set_thousands_separator": "",
        "set_decimal_separator": ".",
        "set_trim_decimal_zeros": false
    }
}

之後可以使用pl.Config.load_from_file()將config讀入。這邊需留意,Polars讀取config後,會接著進行config內的設定。

pl.Config.load_from_file("pl_config.json")

6. Context與expression

我認為Polars的核心思想為:挑選正確的context並搭配合適的expression來達成目的。

Context為Polars表達操作意圖的稱呼,可以分為三類操作:

  • 欄:新增或修改欄位(DataFrame.with_columns()DataFrame.select())。
  • 行:選取行數(DataFrame.filter())。
  • 分組聚合:依據某些條件進行分組後,以統計值或某種資料結構來代表各個分組(DataFrame.group_by().agg())。

Expression則是Polars表達具體操作的稱呼,同一個expression搭配不同的context將會產生不同的結果。

例如有一個expression為pl.col("col1").add(1),當其用在DataFrame.select()中僅會得到一個單列的dataframe:

df.select(pl.col("col1").add(1))
shape: (3, 1)
┌──────┐
│ col1 │
│ ---  │
│ i64  │
╞══════╡
│ 2    │
│ 3    │
│ 4    │
└──────┘

可以想成選擇出「"col1"」列後加1。

但當其用在DataFrame.with_columns()中,則會得到三列的dataframe:

df.with_columns(pl.col("col1").add(1))
shape: (3, 3)
┌──────┬──────┬────────────┐
│ col1 ┆ col2 ┆ col3       │
│ ---  ┆ ---  ┆ ---        │
│ i64  ┆ str  ┆ date       │
╞══════╪══════╪════════════╡
│ 2    ┆ x    ┆ 2025-01-01 │
│ 3    ┆ y    ┆ 2025-01-02 │
│ 4    ┆ z    ┆ 2025-01-03 │
└──────┴──────┴────────────┘

可以想成在原先dataframe的最後新增一列。但因為新增的列名與原先的列名一樣皆為「"col1"」,所以會將計算後的結果「貼」至原先「"col1"」列的位置。

必須特別注意的一點是,所有的操作都不會mutate原先的dataframe。每一個context在操作後,都會返回一個新的dataframe。

7. Lazy模式

Lazy模式是Polars提升效率的利器,但是為方便系列文說明,在基本介紹([Day02]~[Day18])部份,我將採用eager模式編寫範例。而Lazy模式將會在[Day19]介紹,並於[Day20]~[Day24]中應用。

8. API docs AI chatbot

在Polars的API文件,建置有AI chatbot,對尚未熟悉Polars操作的朋友們,是個方便的輔助工具。

Code

本日程式碼傳送門


上一篇
[Day01] - 緣起
下一篇
[Day03] - Polars帶來了什麼便利
系列文
Polars熊霸天下8
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言