本文目录导读:

是的,Polars 支持延迟计算(Lazy Evaluation),并且这是 Polars 的核心优势之一。
它通过 LazyFrame(惰性 DataFrame)来实现,你可以先构建一个查询计划,直到你显式调用 .collect() 方法时,Polars 才会真正执行计算。
核心概念:Eager vs Lazy
- Eager(即时模式):普通的 DataFrame 操作(如
df.filter()、df.with_columns())会立即执行并返回结果,每次操作都要遍历数据,性能较差,且无法进行全局优化。 - Lazy(惰性模式):通过
.lazy()方法将 DataFrame 转换为LazyFrame,之后的所有操作(filter、select、join等)都只是“记录”下来,形成一个查询计划,只有在调用.collect()时,Polars 才会对整个查询计划进行优化(如谓词下推、投影下推、消除冗余计算等),然后一次性高效执行。
如何使用延迟计算
import polars as pl
# 1. 创建普通 DataFrame (Eager)
df = pl.DataFrame({
"name": ["Alice", "Bob", "Charlie"],
"age": [25, 30, 35],
"score": [85, 90, 95]
})
# 2. 转换为 LazyFrame (惰性)
lazy_df = df.lazy() # 或者 pl.scan_csv("file.csv") 直接读取为 LazyFrame
# 3. 构建查询计划(此时并没有实际计算)
query = (lazy_df
.filter(pl.col("age") > 28)
.select(["name", "score"])
.with_columns((pl.col("score") * 1.1).alias("adjusted_score"))
)
# 4. 执行计算 (触发优化和实际计算)
result = query.collect()
print(result)
# shape: (2, 3)
# ┌─────────┬───────┬─────────────────┐
# │ name ┆ score ┆ adjusted_score │
# │ --- ┆ --- ┆ --- │
# │ str ┆ i64 ┆ f64 │
# ╞═════════╪═══════╪═════════════════╡
# │ Bob ┆ 90 ┆ 99.0 │
# │ Charlie ┆ 95 ┆ 104.5 │
# └─────────┴───────┴─────────────────┘
关键点:
lazy_df.filter()不会立即执行,它只是返回一个新的LazyFrame。- 所有惰性操作都链接在一起,直到
.collect()才触发计算。
延迟计算的核心优势
- 查询优化(Query Optimization)
Polars 的优化器会自动应用以下优化:
- 谓词下推:将过滤条件尽可能地推后执行(例如在读取数据时就过滤掉不必要的行)。
- 投影下推:只读取需要的列,减少内存占用和 I/O。
- 爆炸消除:避免不必要的中间结果展开。
- 重排操作:以更高效的顺序执行操作(如先过滤后 join,而不是先 join 后过滤)。
- 内存效率 延迟执行允许 Polars 以流式或批处理方式处理数据,避免生成多个中间副本,这对于处理大型数据集(甚至超过内存大小的数据集)至关重要。
- 多核并行 延迟计算允许 Polars 在优化后的查询计划中更好地利用所有 CPU 核心,实现自动并行化(例如将过滤和聚合分配到不同的线程)。
- 流式查询(Streaming)
对于超大数据集(如 100GB 的 CSV 文件),你可以使用
.collect(streaming=True),Polars 会以流式方式处理数据,一次只加载一小块数据,而不是将整个数据集加载到内存中。
何时使用延迟计算?
| 场景 | 推荐使用 Lazy? | 原因 |
|---|---|---|
| 小型数据集 (< 1GB) | 可选,非必须 | Eager API 足够快,且代码更直观,Lazy 的优势不明显。 |
| 大型数据集 (> 1GB) 或复杂操作 | 强烈推荐 | 延迟计算能大幅减少内存使用和运行时间,特别是涉及多步过滤、聚合、Join时。 |
| 流式处理超大文件 | 必须 | 使用 pl.scan_csv() 结合 .collect(streaming=True) 处理无法装入内存的数据。 |
| 链式多步数据处理 | 推荐 | 延迟计算能在链条尾部一次性优化整个流程,避免中间结果多次缓存。 |
如何切换到延迟模式?
- 从文件读取:直接使用
pl.scan_csv()、pl.scan_parquet()、pl.scan_ndjson()等函数,它们默认返回 LazyFrame。 - 从已有 DataFrame:使用
df.lazy()转换。 - 从 LazyFrame 回到 Eager:使用
.collect()。
Polars 不仅支持延迟计算,而且它的设计哲学就是鼓励你使用延迟计算,对于任何非 trivial 的数据处理任务,尤其是数据量大或操作复杂时,使用 Lazy API 是获得高性能和高内存效率的关键,如果你之前习惯使用 Pandas(纯 Eager),切换到 Polars 后,建议优先思考能否用 Lazy API 实现,以充分利用其优化能力。