
Polars Parquet 读写与扫描实战指南从read_parquet到惰性查询优化【免费下载链接】polarsExtremely fast Query Engine for DataFrames, written in Rust项目地址: https://gitcode.com/GitHub_Trending/po/polars本指南对应仓库docs/source/user-guide/io/parquet.md以 Python / Rust 双语言示例贯穿 Parquet 的读取、写入与惰性扫描三大主题。Polars 以 Rust 实现的高性能列式引擎其内存中的DataFrame布局与磁盘上的 Parquet 列式文件布局高度相似因此读写 Parquet 非常高效。读完本文你将掌握read_parquet/scan_parquet/write_parquet的完整用法与关键参数理解扫描为何比直接读取更适合大数据量与云上场景并能在工程中正确选择读与扫。Parquet 为什么与 Polars 如此契合Parquet 与 CSV 这类面向行的文本格式存在本质差异Parquet 是列式存储格式columnar format。数据按列而非按行组织在文件中这种布局带来两方面的直接收益更好的压缩率同一列中的数据往往具有相似的类型与取值分布压缩算法可以对同构数据发挥更强作用更快的数据访问当查询只关心少数几列时引擎无需读取无关列的数据。更进一步Polars 之所以读写 Parquet 快文档给出的核心解释是PolarsDataFrame在内存中的布局与 Parquet 文件在磁盘上的布局在许多方面是镜像对应的。列式内存布局 列式磁盘文件意味着数据几乎无需做破坏性重排即可完成编解码与转换这正是 Parquet 在 Polars 生态中成为默认高性能交换格式的根本原因。在仓库中这一能力的底层支撑分布在两个层面Python 侧对外 API 位于 py-polars/src/polars/io/parquet/functions.pyRust 侧则集中在 crates/polars-io/src/parquet/mod.rs 及其read/、write/子目录并借助crates/polars-parquet/完成 Arrow 数组与 Parquet 页数据之间的转换。读取read_parquet将本地 Parquet 文件读入DataFrame直接调用read_parquet即可 :fontawesome-brands-python: Pythonimport polars as pl df pl.read_parquet(docs/assets/data/path.parquet)完整可运行示例见 docs/source/src/python/user-guide/io/parquet.py。 :fontawesome-brands-rust: Rustuse polars::prelude::*; fn main() - Result(), Boxdyn std::error::Error { let mut file std::fs::File::open(docs/assets/data/path.parquet)?; let df ParquetReader::new(mut file).finish()?; println!({df}); Ok(()) }Rust 侧对应示例完整代码见 docs/source/src/rust/user-guide/io/parquet.rs。常用参数速查从 functions.py 的签名看read_parquet在工程实战中常用的参数包括参数默认值说明source必填本地/云上路径支持 glob也接受具有read()方法的文件对象如open()句柄、BytesIOcolumnsNone只读取指定列接受列名列表或从 0 起始的列索引列表n_rowsNone仅读取前n_rows行仅在use_pyarrowFalse时有效parallelauto并行策略可选auto、columns、row_groups、noneauto自动选择最优方向use_statisticsTrue利用文件页脚中的统计信息如 min/max跳过无需读取的数据页hive_partitioningNone是否从 Hive 风格分区路径推断并裁剪传入单个目录时自动启用否则默认关闭try_parse_hive_datesTrue是否尝试把 Hive 分区值解析为 date/datetimelow_memoryFalse以部分性能换更低内存占用use_pyarrowFalse切换为 PyArrow 读取器官方描述为更稳定代价是失去部分原生功能storage_optionsNone云存储连接配置AWS/GCP/Azure/Hugging Face缺省时尝试从环境变量推断credential_providerauto提供云凭证的函数或CredentialProvider*工具类memory_mapTrue内存映射底层文件通常提升性能仅在use_pyarrowTrue时使用row_index_name/row_index_offsetNone/0在结果首列插入行号列可指定起始偏移值得注意的兼容性细节源码中已用RenamedParameter标注0.20.4 起row_count_name/row_count_offset更名为row_index_name/row_index_offset旧名在 2.0 中被移除同样被移除的还有rechunk、retries后者需改为通过storage_options{max_retries: n}传递。进阶辅助函数若只想获取元数据而不加载数据同文件 还提供了两个轻量入口read_parquet_schema(source)仅返回列名到数据类型的 schema 字典内部实现即scan_parquet(source).collect_schema()read_parquet_metadata(source)读取文件级自定义元数据字典该 API 标注为实验性。写入write_parquet写入 Parquet 与读取同样直观先在内存中构建一个DataFrame再调用write_parquet指定目标路径 :fontawesome-brands-python: Pythonimport polars as pl df pl.DataFrame({foo: [1, 2, 3], bar: [None, bak, baz]}) df.write_parquet(docs/assets/data/path.parquet) :fontawesome-brands-rust: Rustuse polars::prelude::*; fn main() - Result(), Boxdyn std::error::Error { let mut df df!( foo [1, 2, 3], bar [None, Some(bak), Some(baz)], )?; let mut file std::fs::File::create(docs/assets/data/path.parquet)?; ParquetWriter::new(mut file).finish(mut df)?; Ok(()) }上例中的示例数据刻意包含了None空值列用以说明 Parquet 对缺失值的表达能力——空值被编码在独立的有效性位图中不会像 CSV 那样引入解析歧义。从 Rust 代码可见写入的底层入口是ParquetWriter::new(mut file).finish(mut df)对应实现位于 crates/polars-io/src/parquet/write/。此外与本地路径一致write_parquet同样支持传入s3://...等云存储 URL 直接上云详见下文云端场景。扫描scan_parquet与惰性执行与read_parquet立即解析不同scan_parquet返回的是一个惰性计算持有者LazyFrame调用扫描时文件的实际解析并不会立刻发生查询计划被推迟到collect时才真正执行。 :fontawesome-brands-python: Pythonimport polars as pl df pl.scan_parquet(docs/assets/data/path.parquet) :fontawesome-brands-rust: Rustuse polars::prelude::*; fn main() - Result(), Boxdyn std::error::Error { let args ScanArgsParquet::default(); let lf LazyFrame::scan_parquet(PlRefPath::new(docs/assets/data/path.parquet), args)?; println!({}, lf.collect()?); Ok(()) }为什么扫描更值得推荐扫描的价值在于LazyFrame允许查询优化器在真正读取数据之前做全局优化。Polars 的核心优化手段——谓词下推predicate pushdown与投影下推projection pushdown——能够被下推到扫描层从而让文件读取阶段就只取出真正需要的行与列典型效果是既提升速度又降低内存占用。关于这些优化为何值得期待的完整解释参见 用户指南 · 惰性 API 概念 与 优化项说明。反模式警示read_parquet().lazy()一个在源码文档字符串中被明确点名的反模式是# 反模式先物化整个文件再转惰性无法把优化下推进读取器 df pl.read_parquet(path.parquet).lazy()由于read_parquet已把文件完整物化为 eagerDataFrame后续任何谓词/投影都无法再推入读取层。官方建议凡是最终要以LazyFrame工作的场景一律直接使用scan_parquet。事实上从实现上可以印证这一点在use_pyarrowFalse的默认路径下read_parquet内部就是先构造scan_parquet(...)再立即执行_collect_eager()见 functions.py。换句话说eager 读 扫描后立刻收集。扫描的关键优化参数scan_parquet签名 中除与read_parquet重合的参数外以下参数直接影响扫描性能参数默认值说明use_statisticsTrue使用文件统计信息裁剪需读取的数据页跳过不满足谓词的行组/页parallelauto除auto、columns、row_groups、none外还可选prefiltered策略globTrue是否对路径执行 glob 规则展开一次扫描多个分片文件cacheTrue是否缓存扫描结果供同一逻辑计划中的多处使用复用hive_partitioningNone是否启用 Hive 分区推断传入单个目录时自动开启hidden_file_prefixNone指定作为隐藏文件前缀的字符串用于扫描目录时过滤文件low_memoryFalse降低内存压力换取部分性能其中parallelprefiltered是一个值得在大型文件上尝试的新策略它先在并行条件下评估下推后的谓词得到哪些行需要读取的掩码随后再对列与行组同时并行、并在读取时过滤掉无需的行。官方注释给出的适用判断是对于含大量行组 谓词能明显过滤聚集行或过滤比例高的文件prefiltered可能带来显著加速反之则可能拖慢扫描。并且当没有谓词可下推时该策略会自动回退到auto。这些说明均可在 functions.py 的parallel参数文档中查到。云端扫描把优化推进到数据下载之前当 Parquet 存放在云对象存储时扫描的优势会被进一步放大。以 S3 为例import polars as pl source s3://bucket/*.parquet df ( pl.scan_parquet(source) .filter(pl.col(id) 100) .select(id, value) .collect() )由于谓词与投影被下推进scan_parquetPolars 会先裁剪出真正需要的字节范围再发起下载从而显著减少网络传输量真正的查询求值由collect()触发。这就是 cloud-storage.md 中 Scanning from cloud storage with query optimisation 一节所强调的核心收益——若改用 eager 读取整份文件必须先被下载到本地云端的网络优势将荡然无存。云端场景还支持认证配置通过storage_options传入访问密钥如aws_access_key_id、aws_secret_access_key、aws_region或用pl.CredentialProviderAWS等工具类选择 profile / 承担 IAM 角色也可以自定义返回凭证字典与过期时间的函数并通过pl.Config.set_default_credential_provider(...)设为全局默认重试配置storage_options支持max_retries、retry_init_backoff_ms、retry_max_backoff_ms、retry_timeout_ms等键精细化控制重试与退避行为PyArrow 数据集扫描scan_pyarrow_dataset(ds.dataset(s3://..., formatparquet))适合 Hive 分区等复杂数据集该功能依赖 PyArrow。完整的云端读写代码清单见 docs/source/src/python/user-guide/io/cloud-storage.py。何时选择读、写还是扫把三种操作放在一起对照决策就非常清晰操作返回类型行为适用场景read_parquetDataFrame立即解析并物化小文件、单次性分析、交互式探索write_parquet无写文件将内存中的DataFrame落盘/上云结果持久化、ETL 落盘、构建分析数据集scan_parquetLazyFrame延迟解析构建惰性查询计划大文件、多文件 glob、云端读取、需要谓词/投影下推的复杂查询链小结Polars 之所以将 Parquet 视为一等公民格式根源在于两种列式布局的高度同构而scan_parquet又把这份效率优势延伸到惰性查询优化与云端场景中。实践中请记住三条原则需要懒执行就用scan_parquet而非read_parquet().lazy()大文件与多分片场景优先扫描并利用use_statistics、hive_partitioning、glob 与prefiltered并行策略云上数据务必让谓词与投影下推进读取层让下载量只覆盖查询真正需要的字节。【免费下载链接】polarsExtremely fast Query Engine for DataFrames, written in Rust项目地址: https://gitcode.com/GitHub_Trending/po/polars创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考