pl.read_parquet()比pd.read_parquet()快近5倍,因polars默认多线程io、列式预读、免类型推断,而pandas需整列内存拷贝与dtype转换;惰性模式pl.scan_parquet()可减少39%耗时。

读取10GB Parquet时,pl.read_parquet() 比 pd.read_parquet() 快近5倍
不是所有“快”都发生在计算阶段——IO瓶颈常被低估。实测240M行Parquet(约10GB)在MacBook Pro M2 Max上:pl.read_parquet() 耗时8.7秒,pd.read_parquet()(Pandas 2.2.1 + PyArrow 15.0.2)耗时41.2秒。关键差异不在代码写法,而在底层行为:
- Polars默认启用多线程IO和列式预读,只加载元数据+所需列schema,跳过类型推断
- Pandas即使使用PyArrow后端,仍需将Arrow表转为NumPy数组,触发整列内存拷贝与dtype转换
- 若数据含大量字符串或嵌套结构,Pandas的object列构造开销会进一步放大延迟
实操建议:对Parquet文件,直接用pl.read_parquet();若后续必须进pandas,用.to_pandas()而非先转DataFrame再调pd.DataFrame()构造器。
过滤+聚合链式操作中,filter() + group_by().agg() 在Polars里是单次扫描
常见错误是把Polars当pandas写:df.filter(...).group_by(...).agg(...) 看似合理,但若没启用惰性模式,它仍是立即执行——而真正省时间的是pl.scan_parquet()构建的计划树。对比实测(240M行):
- 立即模式:
pl.read_parquet().filter(...).group_by(...).agg(...)—— 1.8秒 - 惰性模式:
pl.scan_parquet().filter(...).group_by(...).agg(...).collect()—— 1.1秒(减少39%) - pandas等价链:
df[df.x > 100].groupby('y').z.agg(['mean', 'std'])—— 18.4秒
原因:惰性模式下Polars能合并filter与agg谓词,跳过不满足条件的row group;pandas每次操作都生成新DataFrame,中间结果全驻内存。注意:.collect()前所有操作不触发计算,调试时别误以为“没运行”。
小数据量(pd.read_csv() 反而比 pl.read_csv() 更快
Polars启动有Rust运行时编译与线程池初始化开销。在百万行CSV测试中(约120MB),pd.read_csv()平均耗时0.41秒,pl.read_csv()为0.53秒——慢了29%。这不是bug,是设计取舍:
- Polars为并行IO优化了chunk分发逻辑,但小文件无法摊薄调度成本
- Pandas对小CSV做了多年缓存与Cython路径特化,冷启动更轻量
- 若后续要接
matplotlib或scikit-learn,pandas原生兼容免转换,反而省去.to_pandas()的序列化开销
实操建议:自动分流——用os.path.getsize()预估文件大小,
to_pandas() 不等于零拷贝,大表转换时内存峰值可能翻倍
很多人以为df_pl.to_pandas()只是换个壳,实际它是深拷贝:Arrow内存布局 → NumPy数组 → pandas BlockManager重构。实测10GB Polars DataFrame转pandas,内存峰值达22GB(+120%)。
- 根本原因是pandas的object dtype存储字符串时用Python str对象,每个值带引用头;Polars用紧凑UTF-8 slice共享内存
- 若只需部分列进pandas,用
.select([col1, col2]).to_pandas(),避免加载无关列 - 极端情况可考虑
pl.DataFrame.to_numpy()+ 手动构造pandas DataFrame,绕过object列陷阱
真正省内存的协作方式不是“转来转去”,而是让Polars做完ETL、pandas只接手最终特征表——这个边界划在哪,比选库更重要。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











