Skip to main content

segmtree

可解释的无监督决策树分群库 —— 把一张"客户标签"表自动划分成若干人群,每个人群都由一条 简短、人类可读的规则链定义,全程不需要任何标签 y。

segmtree 实现的是聚类决策树(clustering tree):不预测类别,而是自顶向下递归二分, 每一刀选择使"组内异质性下降最多"的单特征阈值规则,并按 Best-First(全局收益最大的叶子 先切)生长。可以理解为"没有 y 也能长的决策树"——同时给你划分结果和划分规则,这是 k-means / GMM 做不到的。

为什么选 segmtree?

k-means / GMM segmtree
人群定义 质心坐标 income > 50k AND is_active = 0 式规则
新样本归类 最近质心 至多几条阈值判断即可回放
二值 + 连续混合特征 需要各种缩放技巧 归一化后同口径比较
内置自检 收益可加性 + 规则回放一致性校验
稳定性评估 自己想办法 一行调用的 Bootstrap ARI

典型场景:带行为标签的客户分群、患者表型归类、问卷人群画像——一切"大量 0/1 标签 + 少量计数/金额列"的表。

安装

pip install segmtree

仅要求 Python ≥ 3.9 与 numpy。

快速上手

import numpy as np
from segmtree import SegTree, extract_rules, profile_leaves, replay_check, bootstrap_stability

rng = np.random.default_rng(0)
X = np.vstack([
    np.hstack([rng.normal([0, -1, 2, .5], .5, (250, 4)), (rng.random((250, 3)) < .1)]),
    np.hstack([rng.normal([4, 3, -2, .5], .5, (200, 4)), (rng.random((200, 3)) < .5)]),
    np.hstack([rng.normal([-4, 2, 0, 8], .5, (150, 4)), (rng.random((150, 3)) < .9)]),
])

tree = SegTree(min_gain=0.02, min_leaf_frac=0.05, min_leaf_abs=10)
tree.fit(X)

print(f"{tree.n_leaves_} 类人群,异质性降低 {tree.reduction_:.0%}")

for rule in extract_rules(tree):
    print(rule)                      # #0: x1 <= -1.24 AND x0 <= 1.83 ...

profile_leaves(tree, X, top_k=3)     # 每类与全体差异最大的特征(z 分数)
replay_check(tree, X)                # True:规则回放与 predict 完全一致
bootstrap_stability(tree, X, n_replicates=12, random_state=0).mean   # 例如 0.98

取值只有 {0, 1} 的列会被自动识别为二值特征(只做一次"是/否"切分);直接传 pandas DataFrame 也可以,列名会自动带入规则文本。

类别特征

整数编码的类别列(如名义变量的 0..k-1 码)可通过 categorical_features 声明。 它们不按阈值切分,而是按类别子集划分,规则呈现为成员判断:

tree = SegTree(categorical_features=["color"], max_categories=12).fit(df)
# 规则示例: #3: color in {1, 2} AND income > 42 ...

异质性贡献:类别列使用 Gini 杂度相对根 Gini 的比值,连续/二值列保持归一化 方差。Gini 只取决于类别分布,因此对编码做任何一一重映射都会得到等价的划分—— 结果与编码方式无关。

注意:

  • 编码必须为整数值;非数值标签请先因子化 (pd.factorizedf["color"].astype("category").cat.codes);
  • 任意大小的编码都会被精确处理(内部 int64/float64),即使连续列选择 dtype=np.float32 也不会合并;
  • 候选子集:默认 categorical_splitter="exact" 会精确评估节点内全部 2^(k-1)-1 种类别划分(指数复杂度,由 max_categories 把关)。高基数列请改用 categorical_splitter="one_vs_rest":逐个评估"单类别 vs 其余",代价随 k 线性 增长,且不再受 max_categories 限制——但可能错过"多个类别合并到一侧"的最优切分。
  • 预测时未见过的编码由 handle_unknown 控制:"complement"(默认)确定性地 落入补集一侧且 replay_check 依然严格一致;"error" 抛出带特征名与未知值 的 ValueError。训练期类别集合通过 categories_ 属性暴露;
  • profile_leaves 通过 category_enrichment(特征, 编码, 叶内占比, lift))报告类别结构,z 分数仅用于连续/二值特征。

方法原理

节点异质性是各特征杂度比值的加权平均:

H(S) = Σ_j w_j · Var_j(S)/Var_j(root) / Σ_j w_j        H(root) = 1

未传 feature_weights 时所有 w_j = 1。二值特征的伯努利方差 p(1−p) 天然适用 同一公式;声明为类别的列改用 Gini 杂度相对根 Gini 的比值。一刀切成 L/R 的收益:

Gain = H(S) − [ n_L/n_S · H(L) + n_R/n_S · H(R) ]

候选阈值取各特征在节点内的分位数(quantiles=8 即 5%~95% 共 8 个;二值特征仅 0.5 一刀)。每个特征用排序 + 前缀和 O(n log n) 批量评估全部候选;frontier 放在以 负 gain 为键的最小堆里,因此每次扩展都取当前全局收益最大的叶子(Best-First, 同收益按插入顺序稳定决胜)。满足任一条件即停止:最优刀收益低于 min_gain、子节点 过小(max(min_leaf_abs, min_leaf_frac·n))、达到 max_depth,或叶数已达 max_leaf_nodes(每刀恰好新增一个叶子,预算耗尽后剩余候选节点直接成为叶子)。

两个开箱即用的可信度保证:

  • 收益可加性 —— total_gain_ == h_root_ − h_avg_(浮点误差量级),任意合法 权重下同样精确成立;
  • 规则回放 —— replay_check() 从零重新求值每条叶子规则链,必须与 predict() 逐行一致。

测试套件还包含与一个独立朴素参考实现逐位一致性的对照测试。

特征加权

业务上常常"某些列更重要"。feature_weights 接受长度等于特征数的一维数值序列、 按列名索引的 dict(未列出的列权重为 1),或默认 None(等权重):

tree = SegTree(feature_weights={"income": 4.0, "churn_flag": 2.0}).fit(df)

权重缩放各列对异质性与收益的贡献——它并不禁止在零权重列上切分:只要零权重列能很好 地分开其他被加权的特征,仍可作为代理变量被选中。

导出拟合好的树

d = tree.export_dict()      # 可直接 JSON 序列化的嵌套 dict
import json; json.dumps(d)  # 开箱即用

print(tree.export_text())   # 缩进文本:分支条件、gain、样本数、叶子编号

split 节点包含 feature/feature_index/op/threshold|categories/gain/n_samples/ left/right;leaf 节点包含 leaf_id/n_samples/fraction/heterogeneity/conditions。 不暴露任何私有对象。想出图可自行把 dict 转成 graphviz 等格式(segmtree 不引入 额外运行时依赖),README 英文版附有转换示例。

API 一览

SegTree(min_gain=0.01, min_leaf_frac=0.01, min_leaf_abs=50, max_depth=None,
        quantiles=8, binary_features="auto", categorical_features=None,
        max_categories=12, handle_unknown="complement", feature_names=None,
        dtype=np.float64, feature_weights=None, max_leaf_nodes=None,
        categorical_splitter="exact")
成员 用途
.fit(X) / .fit_predict(X) / .predict(X) sklearn 风格训练与归类
.labels_, .n_leaves_, .leaves_, .cuts_ 拟合结构
.reduction_, .h_avg_, .total_gain_, .summary() 质量统计
.feature_weights_ 生效的逐特征权重
.export_dict() / .export_text() 公开树导出(JSON 安全 dict / 文本)
extract_rules(tree) 返回 Rule 列表(str(rule) 即规则链文本)
replay_labels(tree, X) / replay_check(tree, X) 规则回放校验
profile_leaves(tree, X, top_k=3) 每类 z 分数画像
bootstrap_stability(tree, X, ...) Bootstrap ARI(mean/std)
adjusted_rand_score(a, b) 纯 numpy 实现的 ARI

调参建议:

  • 想要更少、更大的人群 → 提高 min_gain(如 0.02~0.05)、减少 quantiles; 已知目标人群数时直接设 max_leaf_nodes
  • 规则想更短、结果想更稳 → 设 max_depth(4~6);
  • 小数据集(< 5000 行)→ 降低 min_leaf_abs(默认 50 是为大数据设的下限);
  • 有业务侧重点 → 用 feature_weights 强调关键列,而不是删掉其余特征;
  • Bootstrap ARI 偏低 → 通常减少分位数、提高 min_gain、限深或限叶数即可稳定结构。

与相关工具的比较

  • 先 k-means 再用 sklearn 决策树拟合(代理树):两步走,规则可能与聚类结果矛盾; segmtree 把切分质量与规则一致性放进同一个目标。
  • forest-clustering / URF:基于森林邻近度的聚类,精度好但人群本身不可直接解释。
  • CUBT(R 包):学术上最接近的方法;segmtree 在纯 numpy 包内补齐了 Best-First 生长、混合类型归一化、可加性自检与 Bootstrap 稳定性。

开发

git clone <your-fork-url> && cd segmtree
pip install -e .[dev]
pytest            # 运行测试
ruff check src tests

许可证

MIT — 见 LICENSE

English

Download files

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

Source Distribution

segmtree-0.4.0.tar.gz (50.2 kB view details)

Uploaded Source

Built Distribution

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

segmtree-0.4.0-py3-none-any.whl (28.6 kB view details)

Uploaded Python 3

File details

Details for the file segmtree-0.4.0.tar.gz.

File metadata

  • Download URL: segmtree-0.4.0.tar.gz
  • Upload date:
  • Size: 50.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for segmtree-0.4.0.tar.gz
Algorithm Hash digest
SHA256 16e2635a41f46e2686f99ca1b584c031f4b9fd8cf4dbf39d504527a64c63f73d
MD5 2248394e461c08b6418b92e26fe93030
BLAKE2b-256 829e25bb844558fc540563d19e2d0ed07573214a07acd7b9b268aa773eb671e5

See more details on using hashes here.

Provenance

The following attestation bundles were made for segmtree-0.4.0.tar.gz:

Publisher: release.yml on Unknownxu1/segmtree

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file segmtree-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: segmtree-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 28.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for segmtree-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 bce23c125915f853488c9ba1121ac23f39258adacae97ec82df3a6af86426956
MD5 9e353db672861a7bea099d3efdd594c2
BLAKE2b-256 0bdfa6d65b718c0d808cc32275db0dfbd9a7c760e6fa8ad87305fc36fb440cc3

See more details on using hashes here.

Provenance

The following attestation bundles were made for segmtree-0.4.0-py3-none-any.whl:

Publisher: release.yml on Unknownxu1/segmtree

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 files

0.3.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page