为何Polars过滤比Pandas等效实现慢?求优化方案
问题:Polars过滤函数在小数据集下比Pandas慢的优化方案?
我尝试用Polars实现一个过滤函数,但其速度明显慢于等效的Pandas实现。真实数据集规模为1000至10000行、100列,且需要处理多个不同数据集。以形状为(1000, 100)的数据集为例,Pandas版本速度快得多(Pandas耗时0.0006秒,Polars耗时0.0037秒),这超出预期。请问是否有更高效的Polars查询写法?或是小数据集下Pandas本就更具性能优势?
补充测试细节:
- 当测试2列数据时Polars更快,但列数越多Polars速度越慢;
- 当行数达到约500000行、100列时,Polars开始优于Pandas;
- 实际场景中需要返回多个匹配最接近值的行;
- 运行环境为Linux服务器。
原实现代码
Pandas版本
import pandas as pd import time import numpy as np target_value = 0.5 data = np.random.rand(1000,100) df = pd.DataFrame(data) run_times = [] for i in range(100): st = time.perf_counter() df_filtered = df.loc[(df[0] - target_value).abs() == (df[0] - target_value).abs().min()] run_time = time.perf_counter() - st run_times.append(run_time) print(f"avg pandas run: {sum(run_times)/len(run_times)}")
Polars版本
import polars as pl import time import numpy as np target_value = 0.5 data = np.random.rand(1000,100) df = pl.DataFrame(data) run_times = [] for i in range(100): st = time.perf_counter() df = df.with_columns(abs_diff = (pl.col('column_0')-target_value).abs()) df_filtered = df.filter(pl.col('abs_diff') == df['abs_diff'].min()) run_time = time.perf_counter() - st run_times.append(run_time) print(f"avg polars run: {sum(run_times)/len(run_times)}")
问题分析与优化方案
1. 原Polars代码慢的核心原因
原代码中每次循环都调用with_columns添加新列,这会复制整个DataFrame的所有100列数据,而Pandas的loc操作基于原数据的索引/视图操作,无额外列复制开销。当列数较多时,这种复制的性能损耗会被放大,导致Polars在小数据集下跑不过Pandas。
另外,Polars的查询优化、列存储等特性在小数据量时,优势不足以抵消其内部机制(如查询计划构建、列处理)带来的额外开销;而当数据量达到几十万行级别时,向量化和并行处理的优势会覆盖这些开销,反超Pandas。
2. 优化后的Polars写法
方案一:避免额外列的创建,直接在filter中完成计算
import polars as pl import time import numpy as np target_value = 0.5 data = np.random.rand(1000,100) df = pl.DataFrame(data) run_times = [] for i in range(100): st = time.perf_counter() df_filtered = df.filter( (pl.col('column_0') - target_value).abs() == (pl.col('column_0') - target_value).abs().min() ) run_time = time.perf_counter() - st run_times.append(run_time) print(f"avg polars optimized run: {sum(run_times)/len(run_times)}")
直接在filter中计算绝对差值和最小值,无需创建临时列,避免了全量数据复制的开销。
方案二:使用懒执行(LazyFrame)优化查询计划
如果需要处理多个数据集或重复执行类似查询,懒执行可以让Polars提前优化整个查询流程,减少冗余计算:
import polars as pl import time import numpy as np target_value = 0.5 data = np.random.rand(1000,100) df = pl.DataFrame(data) run_times = [] for i in range(100): st = time.perf_counter() df_filtered = df.lazy().filter( (pl.col('column_0') - target_value).abs() == (pl.col('column_0') - target_value).abs().min() ).collect() run_time = time.perf_counter() - st run_times.append(run_time) print(f"avg polars lazy run: {sum(run_times)/len(run_times)}")
3. 效果验证
优化后的写法能显著降低小数据集下的Polars执行耗时,接近甚至在部分场景下追平Pandas的速度;同时保留了Polars在大数据量下的性能优势。
内容的提问来源于stack exchange,提问作者Raymond Han
相关产品推荐
相关产品推荐

