Skip to main content

PyO3-Polars OptBinning extension with Rust-side optimization kernels

Project description

polars-optbinning

基于 Rust 的 Polars DataFrame 规则分箱引擎。

核心 API 是 BinningTransformer:为一个或多个特征拟合经优化的分箱规则,保留拟合后的规则组,并在 Polars 管道中复用。

安装

从 PyPI 安装已发布的包:

pip install polars-optbinning

本地开发:

uv sync --group dev
uv run maturin develop --release

用法

import polars as pl
from polars_optbinning import BinningTransformer, Objective, Stat

df = pl.DataFrame(
    {
        "x1": [1.0, 1.2, 2.0, 2.2, 3.1, 3.4, 4.5, 5.0, 5.2, 6.0],
        "x2": [10.0, 9.8, 9.5, 8.9, 8.1, 7.3, 6.5, 5.9, 5.1, 4.2],
        "y": [0, 0, 0, 1, 0, 1, 1, 1, 1, 1],
    }
)

transformer = BinningTransformer(
    features=[pl.col("x1"), pl.col("x2")],
    target=pl.col("y"),
)

transformer.fit(
    df,
    objective=Objective.iv(),
    mapping=[Stat.woe(), Stat.count()],
    max_bins=4,
)

eager_out = transformer.transform(df)
pipeline_out = df.with_columns(transformer.transform_())
count_out = transformer.transform(df, extra_mapping=[Stat.count()])
print(eager_out)
print(pipeline_out)
print(count_out)
print(transformer.summary())

summary() 可以重新执行当前规则并计算与 transform 映射无关的统计量:

stats = transformer.summary(
    extra_stat=[
        Stat.count(),
        Stat.feat_avg(),
        Stat.target_avg(),
        Stat.avg("x2"),
        Stat.sum("x2"),
        Stat.woe(),
    ]
)

feat_avg()feat_min()feat_max() 始终返回列表,覆盖每个规则组所用的特征。avg(expr)min(expr)max(expr)sum(expr) 返回单个 Polars 表达式的标量统计量。基于表达式的输出名称从表达式派生,例如 Stat.avg("x2") 产生 x2_avgsum()woe()iv()event_rate() 默认使用初始化时指定的目标,也接受显式表达式。

拟合后的转换器会复用其选定的训练数据。传递 summary(extra_stat=[...], df=new_df) 可以在另一数据集上计算统计量。通过解析或手动构造的转换器需要提供 df,并可以使用显式表达式统计量(如 Stat.woe("label"));依赖默认目标的统计量需要 BinningTransformer(target=...)fit()parse() 中提供的 mapping 仍然是 transform()transform_() 产生的显式模式;未被 mapping 选中的规则统计量不会自动展开到 transform 列中。transform(df, extra_mapping=[...])transform_(extra_mapping=[...]) 可以在本次输出中临时追加已存在的规则统计映射,且不修改转换器自身。

两两 2D 分箱为每个特征对创建一个特征:

pairwise = BinningTransformer(
    features=[pl.col("x1"), pl.col("x2")],
    target=pl.col("y"),
    prefix="pair_",
)

pairwise.fit(
    df,
    objective=Objective.iv(),
    mapping=[Stat.index()],
    max_bins=4,
    mode="pairwise_2d",
    progress=True,
)

out = df.with_columns(pairwise.transform_())

连续目标回归示例见 examples/example_linear.py

原生 XGBoost 和 LightGBM 树模型可以解析为可复用的规则组:

model = BinningTransformer.parse(
    "examples/models/multiclass_xgb.json",
    mapping=[Stat.bin_value()],
    prefix="xgb_",
)

tree_outputs = df.with_columns(model.transform_())
raw_margin = df.with_columns(prediction=model.predict_())
probability = df.with_columns(prediction=model.predict_(output="probability"))
classes = df.with_columns(prediction=model.predict_(output="class"))
eager_prediction = model.predict(df, output="class")

parse() 接受 XGBoost save_model() JSON/UBJ 文件和 LightGBM Booster.save_model() 文本文件、路径及可读文件对象。解析后的转换器每棵树暴露一个可编辑的 RuleGroup,不可再次拟合。原生模型训练、序列化、解析以及与 Polars 预测对比的完整示例见 examples/example_multiclass_models.py

也支持手动构建规则引擎:

from polars_optbinning import Condition, Rule, RuleGroup

group = RuleGroup(
    "x",
    ["x"],
    [
        Rule(Condition.interval("x", None, 2.0), [Stat.const(0.1)]),
        Rule(Condition.interval("x", 2.0, None), [Stat.const(0.9)]),
    ],
    [Stat.const(0.0)],
)

engine = BinningTransformer(groups=[group])
out = pl.DataFrame({"x": [1.0, 2.5]}).with_columns(engine.transform_())

条件可以跨一个或多个特征进行集合组合:

high_risk = (Condition.interval("age", 65.0, None) & Condition.is_in("region", [1.0, 2.0]))
eligible = high_risk | Condition.interval("score", 700.0, None)
excluded = eligible - Condition.is_in("region", [9.0])
assert excluded.features == ["age", "region", "score"]
excluded = excluded.simplify()

group = RuleGroup(
    "decision",
    ["age", "region", "score"],
    [
        Rule(excluded, [Stat.const(1.0)]),
        Rule(Condition.otherwise(), [Stat.const(0.0)]),
    ],
    [Stat.const(0.0)],
)

otherwise 是可选的,但存在时必须作为最后一条规则。空组以及没有 otherwise 规则的未匹配行输出 null。拟合后的分箱组按拟合计数的降序排列,且始终包含一条最终的 otherwise 规则。

规则可以编译为显式的二进制执行链:

group.compile()
assert group.execution_kind == "rule_chain"

Perpetual GBDT

通过同一个转换器支持多维梯度提升:

model = BinningTransformer(features=["x1", "x2"], target="y").fit(
    df,
    mode="gbdt",
    objective=Objective.squared_loss(),
    extra_params={
        "budget": 0.5,
        "iteration_limit": 100,
        "categorical_features": ["x2"],
    },
)

raw = df.select(model.predict_())
tree_outputs = df.select(model.transform_())
importance = model.feature_importance()

分类使用 Objective.log_loss()。整数类别标签被保留,包括非连续标签。extra_params 会进行严格校验,与特征相关的设置使用特征名称而非列索引。

树规则暴露 Stat.gain(),定义为从根到叶子路径上的累积分裂增益。feature_importance() 则对每个内部分裂恰好计数一次,返回 featuregaingain_normalizedsplit_count。结构化的规则编辑会使原始树的重要性信息失效。包含类别和缺失值处理的泰坦尼克生存分类示例见 examples/example_perpetual.py

Rust 集成当前锁定 perpetual 3.0.0-rc.2,这是第一个基于稳定 Rust 构建的兼容版本;perpetual 2.1.0 需要仅限 nightly 的编译器特性。

当前范围

  • 通过 BinningTransformer.fit 的多特征优化 1D 分箱。
  • 类型化的 ObjectiveStat 选择器。
  • 即时 transform(df) 和表达式友好的 transform_()
  • summary() 以 Polars DataFrame 格式返回拟合的规则统计量。
  • 通过 mode="pairwise_2d" 的两两 2D 矩形分箱。
  • XGBoost/LightGBM 数值树解析、叶子规则转换及模型预测。

ND 优化拟合在可复用规则引擎接口稳定期间暂时搁置。

发布

发布脚本会验证 pyproject.tomlCargo.toml 使用相同的版本号,构建优化后的 wheel 和源码分发包,发布到 PyPI,创建对应的 v<version> Gitee 标签,并将文件同步到 Gitee Release。

推荐使用 Token 认证:

$env:GITEE_TOKEN = "<personal-access-token>"
$env:MATURIN_PYPI_TOKEN = "<pypi-api-token>"
python scripts/gitee_release.py

PYPI_API_TOKEN 可作为别名使用。也支持账号/密码认证,通过 MATURIN_USERNAME + MATURIN_PASSWORDPYPI_USERNAME + PYPI_PASSWORD

PyPI 分发包是不可变的。重复运行时,--skip-existing 会跳过已发布的文件,同时允许上传其他平台对应的 wheel。Gitee Release 会更新,同名附件会被替换。已有 wheel 会被保留。

设置 SKIP_BUILD=1 可上传已在 WHEEL_DIR(默认为 dist)中的文件,SKIP_PYPI=1 仅同步到 Gitee,SKIP_GITEE=1 仅发布到 PyPI。所有凭证必须存储在 CI 服务的安全变量存储中。

发布检查清单:

  1. 同时更新 pyproject.tomlCargo.toml 中的版本号。
  2. 更新 CHANGELOG.md
  3. 运行 Clippy 和 Python 测试套件。
  4. 在应打版本标签的提交上运行发布脚本。

项目政策请参考 CONTRIBUTING.mdSECURITY.mdLICENSE

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

polars_optbinning-0.2.2.tar.gz (111.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

polars_optbinning-0.2.2-cp39-abi3-win_amd64.whl (5.7 MB view details)

Uploaded CPython 3.9+Windows x86-64

File details

Details for the file polars_optbinning-0.2.2.tar.gz.

File metadata

  • Download URL: polars_optbinning-0.2.2.tar.gz
  • Upload date:
  • Size: 111.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: maturin/1.13.3

File hashes

Hashes for polars_optbinning-0.2.2.tar.gz
Algorithm Hash digest
SHA256 b18438529660ee35e48b9b67038474f966a352d382a097fe839d845be44669ff
MD5 0b7d6be42863b4d7b04fe54677afa699
BLAKE2b-256 f84af13338b2af054e9744038826c37ce2eddf7ee75f5833920c0113612b0409

See more details on using hashes here.

File details

Details for the file polars_optbinning-0.2.2-cp39-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for polars_optbinning-0.2.2-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 17752534dae168fa33b66df138afa7a78a3d72831c3e2d622980cd36e98c8854
MD5 f9b78e346a709c973c9a4b094d1463da
BLAKE2b-256 862f5c5125a8da48ec6490054ca8f398caef9c9276e055544e21c9102a6c65dd

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page