의사 결정 트리의 훈련 된 트리에서 텍스트 목록으로 기본 의사 결정 규칙 (또는 '결정 경로')을 추출 할 수 있습니까?
다음과 같은 것 :
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'
당신의 도움을 주셔서 감사합니다.
답변:
이 답변이 다른 답변보다 더 정확하다고 생각합니다.
from sklearn.tree import _tree
def tree_to_code(tree, feature_names):
tree_ = tree.tree_
feature_name = [
feature_names[i] if i != _tree.TREE_UNDEFINED else "undefined!"
for i in tree_.feature
]
print "def tree({}):".format(", ".join(feature_names))
def recurse(node, depth):
indent = " " * depth
if tree_.feature[node] != _tree.TREE_UNDEFINED:
name = feature_name[node]
threshold = tree_.threshold[node]
print "{}if {} <= {}:".format(indent, name, threshold)
recurse(tree_.children_left[node], depth + 1)
print "{}else: # if {} > {}".format(indent, name, threshold)
recurse(tree_.children_right[node], depth + 1)
else:
print "{}return {}".format(indent, tree_.value[node])
recurse(0, 1)
유효한 파이썬 함수를 출력합니다. 다음은 입력을 리턴하려는 트리에 대한 예제 출력입니다 (0과 10 사이의 숫자).
def tree(f0):
if f0 <= 6.0:
if f0 <= 1.5:
return [[ 0.]]
else: # if f0 > 1.5
if f0 <= 4.5:
if f0 <= 3.5:
return [[ 3.]]
else: # if f0 > 3.5
return [[ 4.]]
else: # if f0 > 4.5
return [[ 5.]]
else: # if f0 > 6.0
if f0 <= 8.5:
if f0 <= 7.5:
return [[ 7.]]
else: # if f0 > 7.5
return [[ 8.]]
else: # if f0 > 8.5
return [[ 9.]]
다른 답변에서 볼 수있는 걸림돌은 다음과 같습니다.
tree_.threshold == -2
노드가 잎인지 여부를 결정하는 것은 좋은 생각이 아니다. 임계 값이 -2 인 실제 의사 결정 노드 인 경우 어떻게합니까? 대신, tree.feature
또는tree.children_*
합니다.features = [feature_names[i] for i in tree_.feature]
, sklearn의 내 버전과 충돌의 일부 값 때문에tree.tree_.feature
입니다 -2 (특히 잎 노드).print "{}return {}".format(indent, tree_.value[node])
변경해야합니다 print "{}return {}".format(indent, np.argmax(tree_.value[node][0]))
.
RandomForestClassifier.estimators_
알지만 견적 결과를 결합하는 방법을 알아낼 수 없었습니다.
print "bla"
=>print("bla")
sklearn이 만든 의사 결정 트리에서 규칙을 추출하는 자체 기능을 만들었습니다.
import pandas as pd
import numpy as np
from sklearn.tree import DecisionTreeClassifier
# dummy data:
df = pd.DataFrame({'col1':[0,1,2,3],'col2':[3,4,5,6],'dv':[0,1,0,1]})
# create decision tree
dt = DecisionTreeClassifier(max_depth=5, min_samples_leaf=1)
dt.fit(df.ix[:,:2], df.dv)
이 함수는 먼저 노드 (자식 배열에서 -1로 식별)로 시작한 다음 부모를 재귀 적으로 찾습니다. 나는 이것을 노드의 '계보'라고 부릅니다. 그 과정에서 if / then / else SAS 논리를 작성하는 데 필요한 값을 가져옵니다.
def get_lineage(tree, feature_names):
left = tree.tree_.children_left
right = tree.tree_.children_right
threshold = tree.tree_.threshold
features = [feature_names[i] for i in tree.tree_.feature]
# get ids of child nodes
idx = np.argwhere(left == -1)[:,0]
def recurse(left, right, child, lineage=None):
if lineage is None:
lineage = [child]
if child in left:
parent = np.where(left == child)[0].item()
split = 'l'
else:
parent = np.where(right == child)[0].item()
split = 'r'
lineage.append((parent, split, threshold[parent], features[parent]))
if parent == 0:
lineage.reverse()
return lineage
else:
return recurse(left, right, parent, lineage)
for child in idx:
for node in recurse(left, right, child):
print node
아래 튜플 세트에는 SAS if / then / else 문을 작성하는 데 필요한 모든 것이 포함되어 있습니다. do
SAS에서 블록을 사용하는 것을 좋아하지 않기 때문에 노드의 전체 경로를 설명하는 논리를 작성합니다. 튜플 뒤의 단일 정수는 경로에서 터미널 노드의 ID입니다. 앞의 모든 튜플이 결합되어 해당 노드를 만듭니다.
In [1]: get_lineage(dt, df.columns)
(0, 'l', 0.5, 'col1')
1
(0, 'r', 0.5, 'col1')
(2, 'l', 4.5, 'col2')
3
(0, 'r', 0.5, 'col1')
(2, 'r', 4.5, 'col2')
(4, 'l', 2.5, 'col1')
5
(0, 'r', 0.5, 'col1')
(2, 'r', 4.5, 'col2')
(4, 'r', 2.5, 'col1')
6
(0.5, 2.5]
있습니다. 나무는 재귀 분할로 만들어집니다. 변수가 여러 번 선택되는 것을 막을 수있는 것은 없습니다.
Zelazny7 이 제출 한 코드를 수정하여 의사 코드를 인쇄했습니다.
def get_code(tree, feature_names):
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):
if (threshold[node] != -2):
print "if ( " + features[node] + " <= " + str(threshold[node]) + " ) {"
if left[node] != -1:
recurse (left, right, threshold, features,left[node])
print "} else {"
if right[node] != -1:
recurse (left, right, threshold, features,right[node])
print "}"
else:
print "return " + str(value[node])
recurse(left, right, threshold, features, 0)
get_code(dt, df.columns)
동일한 예제 를 호출 하면 다음을 얻을 수 있습니다.
if ( col1 <= 0.5 ) {
return [[ 1. 0.]]
} else {
if ( col2 <= 4.5 ) {
return [[ 0. 1.]]
} else {
if ( col1 <= 2.5 ) {
return [[ 1. 0.]]
} else {
return [[ 0. 1.]]
}
}
}
(threshold[node] != -2)
에 ( left[node] != -1)
(자식 노드의 ID를 얻기 위해 아래의 방법과 유사)
Scikit Learn에서는 export_text
트리에서 규칙을 추출하기 위해 버전 0.21 (2019 년 5 월)에서 맛있는 새로운 방법을 도입했습니다 . 여기에 문서 . 더 이상 사용자 정의 기능을 만들 필요가 없습니다.
모델에 적합하면 두 줄의 코드 만 있으면됩니다. 먼저 수입 export_text
:
from sklearn.tree.export import export_text
둘째, 규칙을 포함 할 객체를 만듭니다. 규칙을보다 읽기 쉽게 보이게하려면 feature_names
인수를 사용하고 기능 이름 목록을 전달하십시오. 예를 들어, 모델이 호출 model
되고 피쳐가이라는 데이터 프레임에 이름이 지정된 경우 다음 과 X_train
같은 객체를 작성할 수 있습니다 tree_rules
.
tree_rules = export_text(model, feature_names=list(X_train))
그런 다음 인쇄하거나 저장하십시오 tree_rules
. 결과는 다음과 같습니다.
|--- Age <= 0.63
| |--- EstimatedSalary <= 0.61
| | |--- Age <= -0.16
| | | |--- class: 0
| | |--- Age > -0.16
| | | |--- EstimatedSalary <= -0.06
| | | | |--- class: 0
| | | |--- EstimatedSalary > -0.06
| | | | |--- EstimatedSalary <= 0.40
| | | | | |--- EstimatedSalary <= 0.03
| | | | | | |--- class: 1
새로운이 DecisionTreeClassifier
방법은 decision_path
에, 0.18.0 릴리스. 개발자는 광범위한 (잘 문서화 된) 연습을 제공합니다 합니다.
연습에서 트리 구조를 인쇄하는 첫 번째 코드 섹션은 정상인 것 같습니다. 그러나 두 번째 섹션의 코드를 수정하여 하나의 샘플을 조사했습니다. 내 변경 사항은# <--
편집# <--
아래 코드에 표시된 변경 사항 은 풀 요청 # 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
다른 샘플의 결정 경로를 보려면를 변경 하십시오. 개발자에게 이러한 변경 사항에 대해 묻지 않고 예제를 통해 작업 할 때 더 직관적 인 것처럼 보였습니다.
from StringIO import StringIO
out = StringIO()
out = tree.export_graphviz(clf, out_file=out)
print out.getvalue()
Digraph Tree를 볼 수 있습니다. 그런 다음, clf.tree_.feature
및 clf.tree_.value
노드 분할 기능 및 노드 값의 배열의 배열은 각각이다. 이 github 소스 에서 자세한 내용을 참조 할 수 있습니다 .
모두가 매우 도움이 되었기 때문에 Zelazny7과 Daniele의 아름다운 솔루션에 수정 사항을 추가합니다. 이것은 파이썬 2.7 용이며 더 읽기 쉬운 탭이 있습니다.
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)
아래 코드는 anaconda python 2.7 및 결정 규칙이있는 PDF 파일을 만드는 패키지 이름 "pydot-ng"에 대한 나의 접근 방식입니다. 도움이 되길 바랍니다.
from sklearn import tree
clf = tree.DecisionTreeClassifier(max_leaf_nodes=n)
clf_ = clf.fit(X, data_y)
feature_names = X.columns
class_name = clf_.classes_.astype(int).astype(str)
def output_pdf(clf_, name):
from sklearn import tree
from sklearn.externals.six import StringIO
import pydot_ng as pydot
dot_data = StringIO()
tree.export_graphviz(clf_, out_file=dot_data,
feature_names=feature_names,
class_names=class_name,
filled=True, rounded=True,
special_characters=True,
node_ids=1,)
graph = pydot.graph_from_dot_data(dot_data.getvalue())
graph.write_pdf("%s.pdf"%name)
output_pdf(clf_, name='filename%s'%n)
이 과정을 거쳤지만 규칙을이 형식으로 작성해야했습니다.
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)
이것은 @paulkernfeld의 답변을 기반으로합니다. 특징이있는 데이터 프레임 X와 공명이있는 대상 데이터 프레임 y가 있고 어떤 노드에서 어떤 y 값이 끝났는지 (그리고 그에 따라 개미 표시) 아이디어를 얻으려면 다음을 수행 할 수 있습니다.
def tree_to_code(tree, feature_names):
from sklearn.tree import _tree
codelines = []
codelines.append('def get_cat(X_tmp):\n')
codelines.append(' catout = []\n')
codelines.append(' for codelines in range(0,X_tmp.shape[0]):\n')
codelines.append(' Xin = X_tmp.iloc[codelines]\n')
tree_ = tree.tree_
feature_name = [
feature_names[i] if i != _tree.TREE_UNDEFINED else "undefined!"
for i in tree_.feature
]
#print "def tree({}):".format(", ".join(feature_names))
def recurse(node, depth):
indent = " " * depth
if tree_.feature[node] != _tree.TREE_UNDEFINED:
name = feature_name[node]
threshold = tree_.threshold[node]
codelines.append ('{}if Xin["{}"] <= {}:\n'.format(indent, name, threshold))
recurse(tree_.children_left[node], depth + 1)
codelines.append( '{}else: # if Xin["{}"] > {}\n'.format(indent, name, threshold))
recurse(tree_.children_right[node], depth + 1)
else:
codelines.append( '{}mycat = {}\n'.format(indent, node))
recurse(0, 1)
codelines.append(' catout.append(mycat)\n')
codelines.append(' return pd.DataFrame(catout,index=X_tmp.index,columns=["category"])\n')
codelines.append('node_ids = get_cat(X)\n')
return codelines
mycode = tree_to_code(clf,X.columns.values)
# now execute the function and obtain the dataframe with all nodes
exec(''.join(mycode))
node_ids = [int(x[0]) for x in node_ids.values]
node_ids2 = pd.DataFrame(node_ids)
print('make plot')
import matplotlib.cm as cm
colors = cm.rainbow(np.linspace(0, 1, 1+max( list(set(node_ids)))))
#plt.figure(figsize=cm2inch(24, 21))
for i in list(set(node_ids)):
plt.plot(y[node_ids2.values==i],'o',color=colors[i], label=str(i))
mytitle = ['y colored by node']
plt.title(mytitle ,fontsize=14)
plt.xlabel('my xlabel')
plt.ylabel(tagname)
plt.xticks(rotation=70)
plt.legend(loc='upper center', bbox_to_anchor=(0.5, 1.00), shadow=True, ncol=9)
plt.tight_layout()
plt.show()
plt.close
가장 우아한 버전은 아니지만 작업을 수행합니다 ...
jupyter 노트북 python 3에서 올바르게 들여 쓰기 위해 가장 좋아하는 코드를 수정했습니다.
import numpy as np
from sklearn.tree import _tree
def tree_to_code(tree, feature_names):
tree_ = tree.tree_
feature_name = [feature_names[i]
if i != _tree.TREE_UNDEFINED else "undefined!"
for i in tree_.feature]
print("def tree({}):".format(", ".join(feature_names)))
def recurse(node, depth):
indent = " " * depth
if tree_.feature[node] != _tree.TREE_UNDEFINED:
name = feature_name[node]
threshold = tree_.threshold[node]
print("{}if {} <= {}:".format(indent, name, threshold))
recurse(tree_.children_left[node], depth + 1)
print("{}else: # if {} > {}".format(indent, name, threshold))
recurse(tree_.children_right[node], depth + 1)
else:
print("{}return {}".format(indent, np.argmax(tree_.value[node])))
recurse(0, 1)
다음은 파이썬 3에서 scikit-learn 의사 결정 트리의 규칙을 인쇄하고 구조를 읽기 쉽도록 조건부 블록에 대한 오프셋을 갖는 함수입니다.
def print_decision_tree(tree, feature_names=None, offset_unit=' '):
'''Plots textual representation of rules of a decision tree
tree: scikit-learn representation of tree
feature_names: list of feature names. They are set to f1,f2,f3,... if not specified
offset_unit: a string of offset of the conditional block'''
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+"return " + str(value[node]))
recurse(left, right, threshold, features, 0,0)
또한 어떤 클래스에 속하는지 또는 출력 값을 언급하여 더 유익한 정보를 만들 수도 있습니다.
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)
다음은 SQL에서 직접 사용할 수있는 형식으로 결정 규칙을 추출하는 방법입니다. 따라서 데이터를 노드별로 그룹화 할 수 있습니다. (이전 포스터의 접근 방식을 기반으로합니다.)
결과는 CASE
sql 문에 복사 할 수 있는 후속 절입니다 (예 : ex.
SELECT COALESCE(*CASE WHEN <conditions> THEN > <NodeA>*, > *CASE WHEN
<conditions> THEN <NodeB>*, > ....)NodeName,* > FROM <table or view>
import numpy as np
import pickle
feature_names=.............
features = [feature_names[i] for i in range(len(feature_names))]
clf= pickle.loads(trained_model)
impurity=clf.tree_.impurity
importances = clf.feature_importances_
SqlOut=""
#global Conts
global ContsNode
global Path
#Conts=[]#
ContsNode=[]
Path=[]
global Results
Results=[]
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,ParentNode=0,IsElse=0):
global Conts
global ContsNode
global Path
global Results
global LeftParents
LeftParents=[]
global RightParents
RightParents=[]
for i in range(len(left)): # This is just to tell you how to create a list.
LeftParents.append(-1)
RightParents.append(-1)
ContsNode.append("")
Path.append("")
for i in range(len(left)): # i is node
if (left[i]==-1 and right[i]==-1):
if LeftParents[i]>=0:
if Path[LeftParents[i]]>" ":
Path[i]=Path[LeftParents[i]]+" AND " +ContsNode[LeftParents[i]]
else:
Path[i]=ContsNode[LeftParents[i]]
if RightParents[i]>=0:
if Path[RightParents[i]]>" ":
Path[i]=Path[RightParents[i]]+" AND not " +ContsNode[RightParents[i]]
else:
Path[i]=" not " +ContsNode[RightParents[i]]
Results.append(" case when " +Path[i]+" then ''" +"{:4d}".format(i)+ " "+"{:2.2f}".format(impurity[i])+" "+Path[i][0:180]+"''")
else:
if LeftParents[i]>=0:
if Path[LeftParents[i]]>" ":
Path[i]=Path[LeftParents[i]]+" AND " +ContsNode[LeftParents[i]]
else:
Path[i]=ContsNode[LeftParents[i]]
if RightParents[i]>=0:
if Path[RightParents[i]]>" ":
Path[i]=Path[RightParents[i]]+" AND not " +ContsNode[RightParents[i]]
else:
Path[i]=" not "+ContsNode[RightParents[i]]
if (left[i]!=-1):
LeftParents[left[i]]=i
if (right[i]!=-1):
RightParents[right[i]]=i
ContsNode[i]= "( "+ features[i] + " <= " + str(threshold[i]) + " ) "
recurse(left, right, threshold, features, 0,0,0,0)
print_decision_tree(clf,features)
SqlOut=""
for i in range(len(Results)):
SqlOut=SqlOut+Results[i]+ " end,"+chr(13)+chr(10)
이제 export_text를 사용할 수 있습니다.
from sklearn.tree import export_text
r = export_text(loan_tree, feature_names=(list(X_train.columns)))
print(r)
[sklearn] [1]의 완전한 예
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn.tree import export_text
iris = load_iris()
X = iris['data']
y = iris['target']
decision_tree = DecisionTreeClassifier(random_state=0, max_depth=2)
decision_tree = decision_tree.fit(X, y)
r = export_text(decision_tree, feature_names=iris['feature_names'])
print(r)
의사 결정 트리에서 SQL을 가져 오도록 Zelazny7의 코드를 수정했습니다.
# SQL from decision tree
def get_lineage(tree, feature_names):
left = tree.tree_.children_left
right = tree.tree_.children_right
threshold = tree.tree_.threshold
features = [feature_names[i] for i in tree.tree_.feature]
le='<='
g ='>'
# get ids of child nodes
idx = np.argwhere(left == -1)[:,0]
def recurse(left, right, child, lineage=None):
if lineage is None:
lineage = [child]
if child in left:
parent = np.where(left == child)[0].item()
split = 'l'
else:
parent = np.where(right == child)[0].item()
split = 'r'
lineage.append((parent, split, threshold[parent], features[parent]))
if parent == 0:
lineage.reverse()
return lineage
else:
return recurse(left, right, parent, lineage)
print 'case '
for j,child in enumerate(idx):
clause=' when '
for node in recurse(left, right, child):
if len(str(node))<3:
continue
i=node
if i[1]=='l': sign=le
else: sign=g
clause=clause+i[3]+sign+str(i[2])+' and '
clause=clause[:-4]+' then '+str(j)
print clause
print 'else 99 end as clusters'
분명히 오래 전에 누군가 이미 공식 scikit의 트리 내보내기 기능 (기본적으로 export_graphviz 만 지원)에 다음 기능을 추가하려고했습니다.
def export_dict(tree, feature_names=None, max_depth=None) :
"""Export a decision tree in dict format.
그의 커밋은 다음과 같습니다.
이 의견에 무슨 일이 있었는지 확실하지 않습니다. 그러나 해당 기능을 사용하려고 할 수도 있습니다.
나는 이것이 scikit-learn의 좋은 사람들에게 속성으로 노출 sklearn.tree.Tree
되는 기본 트리 구조 인 API 를 올바르게 문서화하기 위해 심각한 문서 요청을 필요로한다고 생각 DecisionTreeClassifier
합니다 tree_
.
단지에서 기능을 사용 sklearn.tree 같이
from sklearn.tree import export_graphviz
export_graphviz(tree,
out_file = "tree.dot",
feature_names = tree.columns) //or just ["petal length", "petal width"]
그런 다음 프로젝트 폴더에서 tree.dot 파일을 찾아 모든 내용을 복사하여 http://www.webgraphviz.com/에 붙여 넣은 다음 그래프를 생성하십시오. :)
@paulkerfeld의 훌륭한 솔루션에 감사드립니다. 그의 솔루션의 상단에, 나무의 직렬화 된 버전이 원하는 모든 사람들, 그냥 사용을 위해 tree.threshold
, tree.children_left
, tree.children_right
, tree.feature
와 tree.value
. 잎은 분할이없고 따라서 어떤 이름과 아이들, 자신의 자리를 기능하지 않기 때문에 tree.feature
하고 tree.children_***
있다 _tree.TREE_UNDEFINED
및 _tree.TREE_LEAF
. 모든 스플릿에는에 의해 고유 인덱스가 할당됩니다 depth first search
.
(가) 것을 알 tree.value
모양입니다[n, 1, 1]
다음은 출력을 변환하여 의사 결정 트리에서 Python 코드를 생성하는 함수입니다 export_text
.
import string
from sklearn.tree import export_text
def export_py_code(tree, feature_names, max_depth=100, spacing=4):
if spacing < 2:
raise ValueError('spacing must be > 1')
# Clean up feature names (for correctness)
nums = string.digits
alnums = string.ascii_letters + nums
clean = lambda s: ''.join(c if c in alnums else '_' for c in s)
features = [clean(x) for x in feature_names]
features = ['_'+x if x[0] in nums else x for x in features if x]
if len(set(features)) != len(feature_names):
raise ValueError('invalid feature names')
# First: export tree to text
res = export_text(tree, feature_names=features,
max_depth=max_depth,
decimals=6,
spacing=spacing-1)
# Second: generate Python code from the text
skip, dash = ' '*spacing, '-'*(spacing-1)
code = 'def decision_tree({}):\n'.format(', '.join(features))
for line in repr(tree).split('\n'):
code += skip + "# " + line + '\n'
for line in res.split('\n'):
line = line.rstrip().replace('|',' ')
if '<' in line or '>' in line:
line, val = line.rsplit(maxsplit=1)
line = line.replace(' ' + dash, 'if')
line = '{} {:g}:'.format(line, float(val))
else:
line = line.replace(' {} class:'.format(dash), 'return')
code += skip + line + '\n'
return code
샘플 사용법 :
res = export_py_code(tree, feature_names=names, spacing=4)
print (res)
샘플 출력 :
def decision_tree(f1, f2, f3):
# DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=3,
# max_features=None, max_leaf_nodes=None,
# min_impurity_decrease=0.0, min_impurity_split=None,
# min_samples_leaf=1, min_samples_split=2,
# min_weight_fraction_leaf=0.0, presort=False,
# random_state=42, splitter='best')
if f1 <= 12.5:
if f2 <= 17.5:
if f1 <= 10.5:
return 2
if f1 > 10.5:
return 3
if f2 > 17.5:
if f2 <= 22.5:
return 1
if f2 > 22.5:
return 1
if f1 > 12.5:
if f1 <= 17.5:
if f3 <= 23.5:
return 2
if f3 > 23.5:
return 3
if f1 > 17.5:
if f1 <= 25:
return 1
if f1 > 25:
return 2
위의 예제는 names = ['f'+str(j+1) for j in range(NUM_FEATURES)]
.
편리한 기능 중 하나는 간격을 줄이면 더 작은 파일 크기를 생성 할 수 있다는 것입니다. 그냥 설정하십시오 spacing=2
.