Chapter 03

表达式 Expression API

表达式是 Polars 的灵魂。理解 pl.col / pl.lit、select 与 with_columns、算术比较运算与 alias,你就掌握了 90% 的 Polars 用法。

3.1 表达式:Polars 的核心抽象

pandas 里你写 df["a"] + df["b"],是对具体数据的操作。Polars 引入了一层表达式(Expression)——它不是数据,而是"要做什么运算"的蓝图。表达式在 select/with_columns/filter/group_by().agg() 等上下文里被"执行",Polars 可以并行运行多个独立表达式,并在 Lazy 模式下优化它们。

Expression(表达式)
描述"对某些列做什么运算"的对象,惰性、可组合、无副作用。如 pl.col("a") * 2 本身不算任何东西,直到放进上下文。
pl.col()
引用一列或多列。pl.col("a") 选一列,pl.col("a", "b") 选多列,pl.col(pl.Int64) 按类型选,pl.col("*")pl.all() 选全部。
pl.lit()
字面量(literal)表达式,把一个标量常数包装成表达式,用于与列运算,如 pl.col("x") * pl.lit(2)。多数时候可直接写 * 2,Polars 自动包装。
select
上下文:产出一个 DataFrame,只包含表达式指定的列。原有列若没选就丢掉。
with_columns
上下文:在保留原有所有列的基础上,新增或覆盖列。最常用于派生新特征。
alias
给表达式结果重命名。(pl.col("a") + pl.col("b")).alias("sum"),否则结果列名默认取第一个引用列的名字。
表达式扩展(Expression Expansion)
一个表达式引用多列时,会自动对每列展开成一组表达式。如 pl.col("a", "b") * 2 等价于分别对 a、b 乘 2。

3.2 select:投影出新表

import polars as pl

df = pl.DataFrame({
    "a": [1, 2, 3],
    "b": [10, 20, 30],
})

# 选列 + 计算 + 重命名
out = df.select(
    pl.col("a"),
    (pl.col("a") + pl.col("b")).alias("total"),
    (pl.col("b") / 10).alias("b_scaled"),
)
# 结果只有 a / total / b_scaled 三列

3.3 with_columns:新增列

# 保留 a、b,再加两列
out = df.with_columns(
    (pl.col("a") * 100).alias("a100"),
    (pl.col("a") > 1).alias("a_gt_1"),
)
# 结果有 a / b / a100 / a_gt_1 四列

# 覆盖已有列:同名即覆盖
out = df.with_columns(pl.col("a") * 2)
select vs with_columns 记忆法

select = "只要这些列"(做减法/投影);with_columns = "在原表上再添几列"(做加法)。二者接受的表达式语法完全一致。

3.4 算术与比较

df.select(
    (pl.col("a") + pl.col("b")),      # 加
    (pl.col("b") - pl.col("a")),      # 减
    (pl.col("a") * pl.col("b")),      # 乘
    (pl.col("b") / pl.col("a")),      # 除
    (pl.col("b") % 7),                 # 取模
)

df.select(
    (pl.col("a") > 1),                 # 大于 → Boolean 列
    (pl.col("a") == 2),                # 等于
    (pl.col("a").is_in([1, 3])),    # 成员判断
)

3.5 逻辑组合

多个布尔表达式用 &(与)、|(或)、~(非)组合,务必给每个条件加括号。

expr = (pl.col("a") > 1) & (pl.col("b") < 30)
df.filter(expr)

# 也可以链式写法(更清晰)
expr = (pl.col("a") > 1).and_(pl.col("b") < 30)
括号陷阱

Python 里 & 的优先级高于比较运算符,所以 pl.col("a") > 1 & pl.col("b") < 30 会被误解析。永远给每个比较条件套括号:(...) & (...)

3.6 表达式扩展与选择器

# 对多列一次性操作
df.select(pl.col("a", "b") * 2)          # a、b 都乘 2
df.select(pl.all() * 2)                 # 所有列乘 2
df.select(pl.col(pl.Int64).sum())        # 所有 Int64 列求和

# selectors:更强大的列选择(推荐)
import polars.selectors as cs
df.select(cs.numeric())              # 所有数值列
df.select(cs.starts_with("a"))       # 名字以 a 开头的列

3.7 一个综合例子

df = pl.DataFrame({
    "qty": [2, 5, 1],
    "price": [9.9, 19.9, 99.0],
})

result = df.with_columns(
    (pl.col("qty") * pl.col("price")).alias("amount"),
    (pl.col("price") > 50).alias("is_expensive"),
).select(
    pl.col("amount").round(2),
    pl.col("amount").sum().alias("total"),  # 聚合与逐行混用
)

3.8 小结与下一步