Skip to main content

PyO3-Polars OptBinning extension with Rust-side optimization kernels

Project description

polars-optbinning

PyPI

基于 Rust 的 Polars DataFrame 规则最优分箱引擎。注意项目仍处于早期开发阶段,每个版本API可能会发生重大破坏性变更。

核心 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=[...]) 可以在本次输出中临时追加规则统计映射,缺失的统计量会像 summary(extra_stat=...) 一样先按当前数据重新聚合,且不修改转换器自身。表达式版 transform_() 没有显式 df 参数,因此会使用 fit() 时保存的训练视图;解析或手动构造的转换器仍需要这些统计量已存在于规则中。

summary(..., transition_count=True) 会按输入 DataFrame 的当前行顺序额外统计每个规则组的转入数量:当前时刻分箱为 i 且上一时刻分箱不为 i 的次数。转移率可用 transition_count / count 在 Polars 中直接计算。

两两 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"

原生 GBDT

mode="gbdt" 使用 Rust 原生 XGBoost-like 直方图梯度提升训练器。训练器支持数值特征、原生类别特征、回归目标、二分类目标,以及 Objective.log_loss() / Objective.cross_entropy_loss() 多分类:

model = BinningTransformer(features=["x1", "x2"], target="y").fit(
    df,
    mode="gbdt",
    objective=Objective.log_loss(),
    mapping=[Stat.bin_value(), Stat.gain()],
    extra_params={
        "n_estimators": 100,
        "learning_rate": 0.1,
        "max_depth": 3,
        "max_leaves": 16,
        "max_bin": 64,
        "min_child_weight": 1e-3,
        "min_samples_leaf": 2,
        "lambda_l2": 1.0,
        "gamma": 0.0,
        "subsample": 0.9,
        "colsample_bytree": 1.0,
        "categorical_features": ["x2"],
        "monotone_constraints": {"x1": 1},
        "missing_policy": "auto",
        "seed": 42,
    },
)

probability = df.select(model.predict_(output="probability"))
importance = model.feature_importance()

训练器使用 objective.rs 中的原生 loss 枚举统一计算初始分数、梯度和海森矩阵。 数值特征按分位点构建 histogram split;类别特征先编码为稳定类别 code,再按类别 leaf score 排序并扫描有序前缀作为候选集合,避免枚举指数级子集。缺失值使用 XGBoost-like default direction;数值特征支持单调约束候选过滤和子节点预测边界传播。 完整 wine 多分类示例见 examples/example_gbdt.py

树规则暴露 Stat.gain(),定义为从根到叶子路径上的累积分裂增益。feature_importance() 则对每个内部分裂恰好计数一次,返回 featuregaingain_normalizedsplit_count。结构化的规则编辑会使原始树的重要性信息失效。

原生随机森林

mode="random_forest" 提供 Rust 原生随机森林后端,复用同一套树规则、预测和特征重要性接口。

forest = BinningTransformer(features=["x1", "x2"], target="y").fit(
    df,
    mode="random_forest",
    objective=Objective.log_loss(),
    mapping=[Stat.bin_value(), Stat.gain()],
    extra_params={
        "n_trees": 100,
        "max_depth": 6,
        "min_samples_leaf": 2,
        "m": 4,
        "bootstrap": True,
        "categorical_features": ["x2"],
        "seed": 42,
    },
)

原生 RF 支持回归、二分类、多分类、缺失默认方向、类别 one-vs-rest split、bootstrap/subsample 和特征子采样。wine 数据集示例见 examples/example_wine_random_forest.py

当前范围

  • 通过 BinningTransformer.fit 的多特征优化 1D 分箱。
  • 类型化的 ObjectiveStat 选择器。
  • 即时 transform(df) 和表达式友好的 transform_()
  • summary() 以 Polars DataFrame 格式返回拟合的规则统计量。
  • 通过 mode="pairwise_2d" 的两两 2D 矩形分箱。
  • XGBoost-like 原生直方图 GBDT 训练、树规则转换及模型预测。
  • 原生随机森林训练、树规则转换及模型预测。
  • 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

发布脚本支持 --phase 参数,默认 all 会同时执行 PyPI 发布和 Gitee 打包上传/打标;--phase pypi 仅执行 maturin 发布;--phase gitee 仅构建或读取 dist 并同步到 Gitee,适合单独重试某个阶段:

python scripts/gitee_release.py --phase pypi
python scripts/gitee_release.py --phase gitee

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=1SKIP_GITEE=1 仍可作为 CI 兼容开关覆盖阶段执行。所有凭证必须存储在 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.3.0.tar.gz (135.0 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.3.0-cp39-abi3-win_amd64.whl (5.5 MB view details)

Uploaded CPython 3.9+Windows x86-64

File details

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

File metadata

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

File hashes

Hashes for polars_optbinning-0.3.0.tar.gz
Algorithm Hash digest
SHA256 8a3b23bafe04e05b93b920a80206bfe31c2359f30048e4692e3930fef9483ce1
MD5 1cf67f9e77274899f72051ef01304572
BLAKE2b-256 e7290a18f946fee746295df118b2a99c9234d8d7f11c25c4b9e95a08162e925d

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for polars_optbinning-0.3.0-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 2ce33ba1eb7e939ff51167692d0661ade7d5fee2e84f7492304f267118aecaae
MD5 8438506b7678d2f450166a66ab054cb9
BLAKE2b-256 c6c3e59e5735b7ee72655c9ea436e21dfaf0f65b53a9c246e17cfa2752d87c46

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