猿问

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

我想使用 Python 构建一个决策树分类器,但我想强制这棵树,无论它认为什么是最好的,每次只将一个节点分成两片叶子。也就是说,每一次,一个节点都会分裂成一个终端叶子和另一个将继续分裂的内部节点,而不是分裂成两个本身可以分裂的内部节点。我希望其中一个拆分每次都以终止结束,直到您最终得到两片低于最小数量的叶子。

例如,下面的树满足这个要求

但第二个没有:

http://img2.mukewang.com/64351a67000103fb06300423.jpg

我想这样做的原因是为了获得一组嵌套的观察分割。我在另一篇文章(Finding a corresponding leaf node for each data point in a decision tree (scikit-learn))上看到可以找到观察的节点ID,这很关键。我意识到我可以通过构建一棵没有这种限制的树并将其中一个叶节点上升到顶部来做到这一点,但这可能无法提供足够的观察结果,我基本上希望在所有观察结果中获得这种嵌套结构数据集。

在我的应用程序中,我实际上并不关心分类任务,我只想获得由特征拆分形成的嵌套观察集。我曾计划让目标变量随机生成,这样特征上的分割就没有意义了(这是违反直觉的,这是我想要的,但我将它用于不同的目的)。或者,如果有人知道 Python 中实现相同目的的类似二进制拆分方法,请告诉我。


烙印99
浏览 134回答 1
1回答

慕标琳琳

我意识到一种方法是构建一个 max_depth=1 的决策树。这将执行分裂成两片叶子。然后挑出杂质最高的叶子继续分裂,再次将决策树拟合到这个子集上,如此重复。为确保层次结构清晰可见,我重新标记了 leaf_ids,以便清楚地看到,当您在树上向上移动时,ID 值会下降。这是一个例子:import numpy as npfrom sklearn.tree import DecisionTreeClassifierimport pandas as pddef 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_idleaf_id 因此是按顺序观察的叶子 ID。因此,例如 leaf_id==1 是第一个被拆分成终端节点的观察结果。leaf_id==2 是下一个从生成 leaf_id==1 的拆分中拆分出来的终端节点,如下所示。因此有 k+1 个叶子。#0#|\#1 .#  |\#  2 .#.......##     |\ #     k (k+1)    不过,我想知道是否有一种方法可以在 Python 中自动执行此操作。
随时随地看视频慕课网APP

相关分类

Python
我要回答