如何用Polars内置表达式实现按列值的有效数字舍入?
基于Polars表达式实现动态有效数字舍入的优化方案
问题背景
现有如下Polars DataFrame:
import polars as pl df = pl.from_repr(""" ┌───────────────┬──────────────┬────────────────────────┐ │ exchange_rate ┆ sig_figs_len ┆ reverse_rate_from_euro │ │ --- ┆ --- ┆ --- │ │ f64 ┆ u32 ┆ f64 │ ╞═══════════════╪══════════════╪════════════════════════╡ │ 6.4881 ┆ 5 ┆ 0.154128 │ │ 6.5196 ┆ 5 ┆ 0.153384 │ │ 6.4527 ┆ 5 ┆ 0.154974 │ │ 6.41 ┆ 3 ┆ 0.156006 │ │ 6.425 ┆ 4 ┆ 0.155642 │ └───────────────┴──────────────┴────────────────────────┘ """)
需要用sig_figs_len列的对应值,对reverse_rate_from_euro列的每个单元格进行有效数字舍入。当前使用map_rows实现,但该方法基于Python循环,在大数据集下性能较差,需要改用Polars内置表达式API实现更高效的方案。
优化解决方案
利用Polars的数学表达式组合实现向量化运算,避免逐行处理:
df = df.with_columns( pl.when(pl.col("reverse_rate_from_euro") == 0) .then(0.0) .otherwise( # 计算数值的数量级指数 (pl.col("reverse_rate_from_euro") / pl.pow(10, pl.floor(pl.log10(pl.col("reverse_rate_from_euro").abs())))) # 缩放后保留对应小数位(对应有效数字数量) .round(pl.col("sig_figs_len") - 1) # 缩放回原数量级 * pl.pow(10, pl.floor(pl.log10(pl.col("reverse_rate_from_euro").abs()))) ) .alias("reverse_rate_to_euro_rounded_sig_figs") )
逻辑说明
- 特殊值处理:先判断数值是否为0,避免
log10(0)的计算错误 - 数量级计算:通过
floor(log10(abs(value)))得到数值的10次幂指数,确定其数量级 - 缩放至[1,10)区间:将原数值除以
10^指数,得到一个1到10之间的数 - 有效数字舍入:对缩放后的数保留
sig_figs_len - 1位小数(因为第一位已经是有效数字,剩余位数对应有效数字总数) - 还原数量级:将舍入后的数乘以
10^指数,得到最终的有效数字舍入结果
运行结果
执行代码后得到的结果与预期一致:
shape: (5, 4) ┌───────────────┬──────────────┬────────────────────────┬───────────────────────────────────┐ │ exchange_rate ┆ sig_figs_len ┆ reverse_rate_from_euro ┆ reverse_rate_to_euro_rounded_sig… │ │ --- ┆ --- ┆ --- ┆ --- │ │ f64 ┆ u32 ┆ f64 ┆ f64 │ ╞═══════════════╪══════════════╪════════════════════════╪═══════════════════════════════════╡ │ 6.4881 ┆ 5 ┆ 0.154128 ┆ 0.15413 │ │ 6.5196 ┆ 5 ┆ 0.153384 ┆ 0.15338 │ │ 6.4527 ┆ 5 ┆ 0.154974 ┆ 0.15497 │ │ 6.41 ┆ 3 ┆ 0.156006 ┆ 0.156 │ │ 6.425 ┆ 4 ┆ 0.155642 ┆ 0.1556 │ └───────────────┴──────────────┴────────────────────────┴───────────────────────────────────┘
内容的提问来源于stack exchange,提问作者banji
相关产品推荐
相关产品推荐

