为了账号安全,请及时绑定邮箱和手机立即绑定

如何强制Python决策树每次只在一个节点上继续分裂(每次形成一个节点/叶子)

如何强制Python决策树每次只在一个节点上继续分裂(每次形成一个节点/叶子)

烙印99 2023-04-11 16:28:49
我想使用 Python 构建一个决策树分类器,但我想强制这棵树,无论它认为什么是最好的,每次只将一个节点分成两片叶子。也就是说,每一次,一个节点都会分裂成一个终端叶子和另一个将继续分裂的内部节点,而不是分裂成两个本身可以分裂的内部节点。我希望其中一个拆分每次都以终止结束,直到您最终得到两片低于最小数量的叶子。例如,下面的树满足这个要求但第二个没有:我想这样做的原因是为了获得一组嵌套的观察分割。我在另一篇文章(Finding a corresponding leaf node for each data point in a decision tree (scikit-learn))上看到可以找到观察的节点ID,这很关键。我意识到我可以通过构建一棵没有这种限制的树并将其中一个叶节点上升到顶部来做到这一点,但这可能无法提供足够的观察结果,我基本上希望在所有观察结果中获得这种嵌套结构数据集。在我的应用程序中,我实际上并不关心分类任务,我只想获得由特征拆分形成的嵌套观察集。我曾计划让目标变量随机生成,这样特征上的分割就没有意义了(这是违反直觉的,这是我想要的,但我将它用于不同的目的)。或者,如果有人知道 Python 中实现相同目的的类似二进制拆分方法,请告诉我。
查看完整描述

1 回答

?
慕标琳琳

TA贡献1830条经验 获得超9个赞

我意识到一种方法是构建一个 max_depth=1 的决策树。这将执行分裂成两片叶子。然后挑出杂质最高的叶子继续分裂,再次将决策树拟合到这个子集上,如此重复。为确保层次结构清晰可见,我重新标记了 leaf_ids,以便清楚地看到,当您在树上向上移动时,ID 值会下降。这是一个例子:


import numpy as np

from sklearn.tree import DecisionTreeClassifier

import pandas as pd


def decision_tree_one_path(X, y=None, min_leaf_size=3):

    nobs = X.shape[0]


    # boolean vector to include observations in the newest split

    include = np.ones((nobs,), dtype=bool)

    # try to get leaves around min_leaf_size

    min_leaf_size = max(min_leaf_size, 1)

    # one-level DT splitter

    dtmodel = DecisionTreeClassifier(splitter="best", criterion="gini", max_depth=1, min_samples_split=int(np.round(2.05*min_leaf_size)))             

    leaf_id = np.ones((nobs,), dtype='int64')

    iter = 0


    if y is None:

        y = np.random.binomial(n=1, p=0.5, size=nobs)


    while nobs >= 2*min_leaf_size:


        dtmodel.fit(X=X.loc[include], y=y[include])

        # give unique node id

        new_leaf_names = dtmodel.apply(X=X.loc[include])

        impurities = dtmodel.tree_.impurity[1:]

        if len(impurities) == 0:

            # was not able to split while maintaining constraint

            break


        # make sure node that is not split gets the lower node_label 1

        most_impure_node = np.argmax(impurities)

        if most_impure_node == 0: # i.e., label 1

            # switch 1 and 2 labels above

            is_label_2 = new_leaf_names == 2

            new_leaf_names[is_label_2] = 1

            new_leaf_names[np.logical_not(is_label_2)] = 2


        # rename leaves

        leaf_id[include] = iter + new_leaf_names

        will_be_split = new_leaf_names == 2


        # ignore the other one

        tmp = np.ones((nobs,), dtype=bool)

        tmp[np.logical_not(will_be_split)] = False

        include[include] = tmp


        # now create new labels

        nobs = np.sum(will_be_split)

        iter = iter + 1


    return leaf_id

leaf_id 因此是按顺序观察的叶子 ID。因此,例如 leaf_id==1 是第一个被拆分成终端节点的观察结果。leaf_id==2 是下一个从生成 leaf_id==1 的拆分中拆分出来的终端节点,如下所示。因此有 k+1 个叶子。


#0

#|\

#1 .

#  |\

#  2 .

#.......

#

#     |\ 

#     k (k+1) 

   

不过,我想知道是否有一种方法可以在 Python 中自动执行此操作。


查看完整回答
反对 回复 2023-04-11
  • 1 回答
  • 0 关注
  • 133 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信