我能从决策树中的训练树中提取基本的决策规则(或“决策路径”)作为文本列表吗?
喜欢的东西:
if A>0.4 then if B<0.2 then if C>0.8 then class='X'
我能从决策树中的训练树中提取基本的决策规则(或“决策路径”)作为文本列表吗?
喜欢的东西:
if A>0.4 then if B<0.2 then if C>0.8 then class='X'
当前回答
在0.18.0版本中,有一个新的DecisionTreeClassifier方法decision_path。开发人员提供了一个广泛的(文档良好的)演练。
演练中打印树结构的第一部分代码似乎没有问题。但是,我修改了第二节中的代码来检查一个示例。我的更改用# <——表示
在拉取请求#8653和#10951中指出错误后,下面代码中由# <——标记的更改已在演练链接中更新。现在就容易多了。
sample_id = 0
node_index = node_indicator.indices[node_indicator.indptr[sample_id]:
node_indicator.indptr[sample_id + 1]]
print('Rules used to predict sample %s: ' % sample_id)
for node_id in node_index:
if leave_id[sample_id] == node_id: # <-- changed != to ==
#continue # <-- comment out
print("leaf node {} reached, no decision here".format(leave_id[sample_id])) # <--
else: # < -- added else to iterate through decision nodes
if (X_test[sample_id, feature[node_id]] <= threshold[node_id]):
threshold_sign = "<="
else:
threshold_sign = ">"
print("decision id node %s : (X[%s, %s] (= %s) %s %s)"
% (node_id,
sample_id,
feature[node_id],
X_test[sample_id, feature[node_id]], # <-- changed i to sample_id
threshold_sign,
threshold[node_id]))
Rules used to predict sample 0:
decision id node 0 : (X[0, 3] (= 2.4) > 0.800000011921)
decision id node 2 : (X[0, 2] (= 5.1) > 4.94999980927)
leaf node 4 reached, no decision here
更改sample_id以查看其他示例的决策路径。我没有向开发人员询问这些更改,只是在示例中看起来更直观。
其他回答
我已经经历过这些了,但我需要把规则写成这种形式
if A>0.4 then if B<0.2 then if C>0.8 then class='X'
所以我改编了@paulkernfeld的答案(谢谢),你可以根据自己的需要定制
def tree_to_code(tree, feature_names, Y):
tree_ = tree.tree_
feature_name = [
feature_names[i] if i != _tree.TREE_UNDEFINED else "undefined!"
for i in tree_.feature
]
pathto=dict()
global k
k = 0
def recurse(node, depth, parent):
global k
indent = " " * depth
if tree_.feature[node] != _tree.TREE_UNDEFINED:
name = feature_name[node]
threshold = tree_.threshold[node]
s= "{} <= {} ".format( name, threshold, node )
if node == 0:
pathto[node]=s
else:
pathto[node]=pathto[parent]+' & ' +s
recurse(tree_.children_left[node], depth + 1, node)
s="{} > {}".format( name, threshold)
if node == 0:
pathto[node]=s
else:
pathto[node]=pathto[parent]+' & ' +s
recurse(tree_.children_right[node], depth + 1, node)
else:
k=k+1
print(k,')',pathto[parent], tree_.value[node])
recurse(0, 1, 0)
显然,很久以前就有人决定尝试将以下函数添加到官方scikit的树导出函数中(基本上只支持export_graphviz)
def export_dict(tree, feature_names=None, max_depth=None) :
"""Export a decision tree in dict format.
以下是他的全部承诺:
https://github.com/scikit-learn/scikit-learn/blob/79bdc8f711d0af225ed6be9fdb708cea9f98a910/sklearn/tree/export.py
不太确定这条评论发生了什么。但是你也可以尝试使用这个函数。
我认为这为scikit-learn的优秀人员提供了一个严肃的文档需求,以正确地记录sklearn.tree.Tree API,这是一个底层的树结构,DecisionTreeClassifier将其作为属性tree_公开。
因为每个人都很乐于助人,所以我将对Zelazny7和Daniele的漂亮解决方案进行修改。这是针对python 2.7的,使用tab使其更具可读性:
def get_code(tree, feature_names, tabdepth=0):
left = tree.tree_.children_left
right = tree.tree_.children_right
threshold = tree.tree_.threshold
features = [feature_names[i] for i in tree.tree_.feature]
value = tree.tree_.value
def recurse(left, right, threshold, features, node, tabdepth=0):
if (threshold[node] != -2):
print '\t' * tabdepth,
print "if ( " + features[node] + " <= " + str(threshold[node]) + " ) {"
if left[node] != -1:
recurse (left, right, threshold, features,left[node], tabdepth+1)
print '\t' * tabdepth,
print "} else {"
if right[node] != -1:
recurse (left, right, threshold, features,right[node], tabdepth+1)
print '\t' * tabdepth,
print "}"
else:
print '\t' * tabdepth,
print "return " + str(value[node])
recurse(left, right, threshold, features, 0)
下面是一种使用SKompiler库将整个树转换为单个(不一定太容易读懂)python表达式的方法:
from skompiler import skompile
skompile(dtree.predict).to('python/code')
您还可以通过区分它属于哪个类,甚至通过提到它的输出值,使它具有更丰富的信息。
def print_decision_tree(tree, feature_names, offset_unit=' '):
left = tree.tree_.children_left
right = tree.tree_.children_right
threshold = tree.tree_.threshold
value = tree.tree_.value
if feature_names is None:
features = ['f%d'%i for i in tree.tree_.feature]
else:
features = [feature_names[i] for i in tree.tree_.feature]
def recurse(left, right, threshold, features, node, depth=0):
offset = offset_unit*depth
if (threshold[node] != -2):
print(offset+"if ( " + features[node] + " <= " + str(threshold[node]) + " ) {")
if left[node] != -1:
recurse (left, right, threshold, features,left[node],depth+1)
print(offset+"} else {")
if right[node] != -1:
recurse (left, right, threshold, features,right[node],depth+1)
print(offset+"}")
else:
#print(offset,value[node])
#To remove values from node
temp=str(value[node])
mid=len(temp)//2
tempx=[]
tempy=[]
cnt=0
for i in temp:
if cnt<=mid:
tempx.append(i)
cnt+=1
else:
tempy.append(i)
cnt+=1
val_yes=[]
val_no=[]
res=[]
for j in tempx:
if j=="[" or j=="]" or j=="." or j==" ":
res.append(j)
else:
val_no.append(j)
for j in tempy:
if j=="[" or j=="]" or j=="." or j==" ":
res.append(j)
else:
val_yes.append(j)
val_yes = int("".join(map(str, val_yes)))
val_no = int("".join(map(str, val_no)))
if val_yes>val_no:
print(offset,'\033[1m',"YES")
print('\033[0m')
elif val_no>val_yes:
print(offset,'\033[1m',"NO")
print('\033[0m')
else:
print(offset,'\033[1m',"Tie")
print('\033[0m')
recurse(left, right, threshold, features, 0,0)