数据量超过 500 万行或文件大于 1GB 时,Polars 在读写速度上通常明显优于 Pandas;小数据量场景下两者差距不大,Pandas 的生态优势更值得保留。
先说结论:Polars 在大数据集读写场景下性能优势显著,但迁移成本和学习曲线需要考虑,不建议盲目替换现有 Pandas 代码。
- 适合:单次操作超过 500 万行、CPU 长期单核满载、读取超过 1GB 的 CSV/Parquet 文件
- 重点看:分组聚合、链式过滤、流式处理超内存数据这三类操作的性能收益
- 别忽略:小数据量场景下 Pandas 的语法直觉和机器学习生态优势仍然明显
测试环境与版本说明
性能测试结果高度依赖硬件配置和库版本,以下建议基于主流工程环境:
- Python 版本:建议 3.9 及以上
- Pandas 版本:2.0+ (性能有所优化)
- Polars 版本:0.19+ (API 相对稳定,支持 thread_pool_size)
- 硬件建议:Polars 的多核优势在 4 vCPU 以上环境更明显,建议使用 SSD 以减少 I/O 瓶颈
- 安装命令:
pip install pandas polars pyarrow
可复现的性能测试脚本
如果你正在评估是否迁移,先用以下完整代码快速测试自己场景的实际收益。注意替换文件路径并确保环境一致:
import pandas as pd
import polars as pl
import time
import os
file_path = 'your_file.parquet'
# 检查文件是否存在
if not os.path.exists(file_path):
print(f'文件 {file_path} 不存在,请替换为真实路径')
exit()
# 测试 Pandas 读取速度
start = time.time()
df_pd = pd.read_parquet(file_path)
pandas_time = time.time() - start
print(f'Pandas 读取耗时:{pandas_time:.2f}秒')
# 测试 Polars 读取速度
start = time.time()
df_pl = pl.read_parquet(file_path)
polars_time = time.time() - start
print(f'Polars 读取耗时:{polars_time:.2f}秒')
# 检查 Polars 线程数是否匹配实际 vCPU
# 注意:API 随版本变化,0.19+ 使用 thread_pool_size
try:
print(f'当前线程池大小:{pl.thread_pool_size()}')
except AttributeError:
print('当前 Polars 版本不支持 thread_pool_size 查询,请查阅对应版本文档')
print(f'性能提升倍数:{pandas_time / polars_time:.2f}x' if polars_time > 0 else 'Polars 耗时过短')核心差异解析
两者速度差异主要来自三个底层设计不同:
1. 语言和执行模式
Pandas 基于 Python+NumPy,受 GIL 锁限制,默认单线程执行。Polars 用 Rust 编写,支持多核并行,CPU 利用率可以跑满多个核心。
2. 内存存储结构
Pandas 采用行式存储,读取一列数据需要加载整行。Polars 基于 Apache Arrow 列式存储,只加载需要的列,内存占用更低,I/O 开销更小。
3. 求值策略
Pandas 每行代码立即执行,中间结果会生成新 DataFrame。Polars 支持惰性求值,先构建执行计划,自动优化操作序列后再执行,减少不必要的计算和内存拷贝。
迁移实操步骤
第一步:确认你的数据规模
用以下代码快速检查当前数据量,避免盲目迁移:
import pandas as pd
# 仅读取前 1000 行进行估算,避免大文件加载过慢
df = pd.read_csv('your_file.csv', nrows=1000)
# 基于采样推算总行数,实际值可能有偏差
estimated_rows = len(df) * 1000
print(f'预估总行数:{estimated_rows} (基于前 1000 行推算)')
print(f'列数:{len(df.columns)}')如果行数超过 500 万或文件超过 1GB,可以考虑测试 Polars。
第二步:小范围迁移测试
不要一次性替换全部代码,先选一个读取或聚合操作做对比:
# Pandas 原代码
df = pd.read_csv('data.csv')
result = df[df['value'] > 100].groupby('category').sum()
# Polars 等价代码
import polars as pl
df = pl.read_csv('data.csv')
# 注意:group_by 后必须显式调用 agg()
result = df.filter(pl.col('value') > 100).group_by('category').agg(pl.all().sum())第三步:调整线程配置
Polars 默认启用所有逻辑核心,但在虚拟机或容器中可能被限制。如果运行在 vCPU 受限的环境,需要显式设置:
import polars as pl
# Polars 0.19+ 推荐配置方式
pl.Config.set_thread_pool_size(2) # 设为实际可用的 vCPU 数效果验证方法
1. 时间对比
用 time 模块或%timeit 记录相同操作在两个库下的耗时,差距在 3 倍以上才有迁移价值。
2. 内存监控
使用系统监控工具观察内存占用,Polars 在处理大文件时内存占用通常为 Pandas 的 1/5 到 1/10。
3. CPU 利用率
运行期间观察 CPU 使用率,Polars 应该能看到多核同时工作,而 Pandas 通常只有一个核心满载。
常见坑与排查
1. API 不完全兼容
Polars 的 API 与 Pandas 高度相似但有差异,例如布尔索引语法不支持、fillna 改为 fill_null、groupby 后必须显式调用 agg() 等,直接复制代码会报错。
2. 惰性求值需要主动启用
使用 pl.read_csv() 是立即执行,想利用惰性优化需要用 pl.scan_csv() + .collect(),否则性能优势会打折扣。
3. 小数据量反而更慢
Polars 有编译和初始化开销,100 万行以下的数据集两者耗时差在秒级,Pandas 的语法直觉和生态优势更明显。
4. 机器学习生态支持
scikit-learn、matplotlib、seaborn 等库原生支持 Pandas DataFrame,使用 Polars 可能需要额外转换步骤(如 .to_pandas())。
参考来源
- Polars 与 Pandas 核心架构差异对比 - 官方文档关于底层实现、执行模式、内存模型的说明
- 2024 年公开基准测试数据 - 社区关于大尺寸数据集下性能对比的汇总
- Polars 官方基准测试 - TPC-H 基准测试中 Polars 表现优于 Pandas 的相关数据(多核环境)
- Polars 文档 - 关于 Config.set_thread_pool_size 线程配置和惰性求值的使用说明