登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  python教程

Python Polars扫描CSV与读取CSV有什么区别

来源:17golang原创

时间:2026-10-13 20:10:01 407浏览 收藏

在 Polars 里,read_csv 和 scan_csv 都能处理 CSV,但它们把“读取”和“执行”放在了不同位置:read_csv 直接返回已经读取的 DataFrame;scan_csv 返回描述查询的 LazyFrame,真正读取通常等到 collect()。如果后面还要继续筛选列、过滤行或聚合,优先考虑扫描;如果正在探索数据、需要马上查看中间结果,直接读取更顺手。

官方地址:https://docs.pola.rs/user-guide/io/csv/

可以把两者理解成两种调用方契约:read_csv承诺“现在给我一张表”,scan_csv承诺“先把查询描述好,最后统一执行”。差别不在 CSV 格式,而在 LazyFrame 是否保留了优化查询的机会。

先区分返回对象和执行时机

我第一次把 Polars 用在批量 CSV 清洗时,最容易混淆的是两个函数都看起来像“打开文件”。实际上,read_csv 的返回值是 DataFrame,调用点就会开始解析文件;scan_csv 的返回值是 LazyFrame,它先保存数据源和后续表达式。

import polars as pl

# 直接读取:调用结束时已经得到内存中的 DataFrame。
eager_df = pl.read_csv("sales.csv")
print(eager_df.head())

# 延迟扫描:这里只建立查询描述,不把结果收集成 DataFrame。
lazy_query = pl.scan_csv("sales.csv")

# collect 才是执行边界,返回最终的 DataFrame。
result_df = lazy_query.select(["region", "amount"]).collect()
print(result_df.head())

因此,scan_csv 并不是“把整个 CSV 读进来后换一种对象包装”。它延后了解析和查询执行,让后续的 select、filter、group_by 可以一起交给查询计划。

Polars read_csv 与 scan_csv 返回对象和执行边界的静态结构图
图1:Polars 的读取接口边界说明图,展示 DataFrame、LazyFrame、collect 与 CSV 数据源的关系;这是原创说明图,不是截图或运行证据。

scan_csv 的价值在于把查询一起交给优化器

官方文档把扫描 CSV 描述为延迟解析,并指出查询优化器可以把谓词和投影下推到扫描层。简单说,过滤条件可能尽量提前应用,列选择也可以让扫描只关注需要的列,从而减少不必要的读取、内存和计算。

import polars as pl

# 先描述完整查询:只保留后续真正需要的列和行。
query = (
    pl.scan_csv("sales.csv")
    .select(["region", "amount"])
    .filter(pl.col("amount") > 1000)
    .group_by("region")
    .agg(pl.col("amount").sum().alias("total_amount"))
)

# 统一执行查询;此前的链式调用仍然只是 LazyFrame 描述。
summary = query.collect()
print(summary)

这里的收益有一个边界:只有仍处于 LazyFrame 查询中的操作,优化器才有机会统一安排。若中途已经 collect(),后续操作就是针对 DataFrame 的新一轮处理,前面的扫描范围不会再被后面的条件缩小。

Polars LazyFrame 查询计划中的列选择、过滤下推和聚合关系静态结构图
图2:LazyFrame 查询计划说明图,展示 CSV 扫描、列选择、过滤条件和聚合之间的静态关系;这是原创说明图,不是截图或运行证据。

按调用方需求选择 read_csv 还是 scan_csv

如果任务是临时查看文件、交互式探索,或者你必须在每一步观察中间表,read_csv 的直接反馈更符合调用方预期。它少一个执行边界,代码也更短。

如果任务是固定的数据管道,例如读取后只留几列、过滤一部分行再聚合,scan_csv 更适合。它的核心不是保证任何数据集都更快,而是保留优化器重新安排查询的空间。

需求建议原因
马上查看列名和样例read_csv调用后直接得到 DataFrame
多步过滤、选列、聚合scan_csv可以统一形成 LazyFrame 查询
需要中间结果参与分支判断read_csv 或提前 collect调用方确实需要立即拿到数据
想确认优化是否生效scan_csv(...).filter(...).explain()直接查看查询计划文本

用 explain 复查计划,而不是凭感觉猜性能

扫描模式的性能优势不能只靠函数名判断。把查询交给 explain(),可以查看计划中是否出现需要的列数、过滤条件和 CSV scan 节点:

import polars as pl

# explain 只展示计划,适合检查优化器看到的查询形状。
query = (
    pl.scan_csv("sales.csv")
    .filter(pl.col("amount") > 1000)
    .select(["region", "amount"])
)
print(query.explain())

# 真正需要结果时再执行,并把执行边界集中在这里。
result = query.collect()

常见误区有三个:把 scan_csv 当成自动分块读取;在很早的位置调用 collect(),让后面的过滤失去下推机会;为了“更快”而忽略必须立即查看中间结果的业务需求。正确选择应由查询形状和调用方时机共同决定。

相关问题

scan_csv 调用后是不是已经把文件读进内存了?

不应这样理解。它先返回 LazyFrame 并延后实际解析,通常在 collect() 等执行动作发生时才运行查询。

所有 CSV 都应该改成 scan_csv 吗?

不是。探索数据、需要中间结果或查询很简单时,read_csv 更直接;固定的多步查询才更适合先扫描再统一收集。

怎样知道过滤和选列是否被下推?

对 LazyFrame 调用 explain() 查看查询计划,关注扫描节点、投影列数和过滤条件,而不是只比较函数名称。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>