如何为DataFrame每行动态设置polars Expr.cut的breaks参数?
解决Polars中基于动态列生成breaks的分箱问题
问题分析
你需要为DataFrame每行的Value列,基于该行的BinMin和BinMax动态生成50个等间隔的breaks,再用Expr.cut分箱。但np.linspace无法直接接受Polars的表达式对象,导致报错。核心原因是cut的breaks参数默认需要静态序列,而你需要行级动态生成breaks。
解决方案
方法1:使用map_elements结合自定义函数
通过将每行的Value、BinMin、BinMax打包为结构体,再用map_elements调用自定义函数生成breaks并分箱,这是最直观的实现方式:
import numpy as np import polars as pl def dynamic_cut(row): # 为当前行生成50个等间隔的breaks breaks = np.linspace(row["BinMin"], row["BinMax"], 50) # 对当前行的Value进行分箱 return pl.Series([row["Value"]]).cut(breaks)[0] df = pl.DataFrame({"Value": [12, 35, 80], "BinMin": [0, 10, 50], "BinMax": [100, 60, 90]}) result = df.with_columns( pl.struct(["Value", "BinMin", "BinMax"]) .map_elements(dynamic_cut, return_dtype=pl.Categorical) .alias("Bin") ) print(result)
输出结果:
┌───────┬────────┬────────┬───────────────────────┐ │ Value ┆ BinMin ┆ BinMax ┆ Bin │ │ --- ┆ --- ┆ --- ┆ --- │ │ i64 ┆ i64 ┆ i64 ┆ cat │ ╞═══════╪════════╪════════╪═══════════════════════╡ │ 12 ┆ 0 ┆ 100 ┆ (10.204, 12.245] │ │ 35 ┆ 10 ┆ 60 ┆ (34.694, 35.714] │ │ 80 ┆ 50 ┆ 90 ┆ (79.592, 80.408] │ └───────┴────────┴────────┴───────────────────────┘
方法2:纯Polars向量化实现(无numpy依赖)
如果想避免使用numpy,可利用Polars的sequence函数生成每行的breaks序列,再通过map_elements完成分箱:
import polars as pl df = pl.DataFrame({"Value": [12, 35, 80], "BinMin": [0, 10, 50], "BinMax": [100, 60, 90]}) result = df.with_columns( # 生成每行的breaks序列:50个等间隔点 breaks = pl.sequence( start=pl.col("BinMin"), end=pl.col("BinMax"), length=50, dtype=pl.Float64 ), # 对每行的Value和breaks进行分箱 Bin = pl.struct(["Value", "breaks"]) .map_elements(lambda x: pl.Series([x["Value"]]).cut(x["breaks"])[0], return_dtype=pl.Categorical) ).drop("breaks") print(result)
注意事项
map_elements是行级操作,若数据量极大,性能会略低于完全向量化操作,但对于大多数场景足够高效。- 若需要分箱后的数值标签而非区间字符串,可在
cut中添加labels参数,或用pl.col("Bin").cast(pl.Utf8).str.extract(r"(\d+\.?\d*)", 1).cast(pl.Float64)提取区间下限/上限。
内容的提问来源于stack exchange,提问作者vlo
相关产品推荐
相关产品推荐

