ฉันสามารถแยกกฎการตัดสินใจพื้นฐาน (หรือ 'เส้นทางการตัดสินใจ') จากต้นไม้ที่ผ่านการฝึกอบรมในต้นไม้การตัดสินใจเป็นรายการที่เป็นข้อความได้หรือไม่?
สิ่งที่ต้องการ:
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)
ฟังก์ชันนี้จะพิมพ์ฟังก์ชัน Python ที่ถูกต้อง นี่คือตัวอย่างเอาต์พุตของทรีที่พยายามคืนค่าอินพุตซึ่งเป็นตัวเลขระหว่าง 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
ชุดของ tuples ด้านล่างมีทุกสิ่งที่ฉันต้องการในการสร้าง SAS if / then / else statement ฉันไม่ชอบการใช้do
บล็อกใน SAS ซึ่งเป็นเหตุผลที่ฉันสร้างตรรกะอธิบายเส้นทางทั้งหมดของโหนด จำนวนเต็มเดียวหลังจาก tuples คือ 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 เรียนรู้แนะนำวิธีการใหม่ที่อร่อยที่เรียกว่าexport_text
ในรุ่น 0.21 (พฤษภาคม 2019) เพื่อแยกกฎจากต้นไม้ เอกสารที่นี่เอกสารที่นี่ไม่จำเป็นต้องสร้างฟังก์ชันที่กำหนดเองอีกต่อไป
เมื่อคุณพอดีกับแบบจำลองของคุณคุณต้องมีโค้ดสองบรรทัด ก่อนนำเข้าexport_text
:
from sklearn.tree.export import export_text
ประการที่สองสร้างวัตถุที่จะมีกฎของคุณ ในการทำให้กฎดูง่ายขึ้นให้ใช้feature_names
อาร์กิวเมนต์และส่งรายการชื่อคุณลักษณะของคุณ ตัวอย่างเช่นถ้าแบบจำลองของคุณถูกเรียกmodel
และคุณสมบัติของคุณถูกตั้งชื่อใน dataframe ชื่อ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()
คุณสามารถเห็นต้นไม้ขุด จากนั้นclf.tree_.feature
และclf.tree_.value
เป็นอาร์เรย์ของโหนดแยกคุณสมบัติและอาร์เรย์ของค่าโหนดตามลำดับ คุณสามารถดูรายละเอียดเพิ่มเติมได้จากแหล่ง gitHubนี้
เพียงเพราะทุกคนมีประโยชน์มากฉันจะเพิ่มการแก้ไข Zelazny7 และโซลูชันที่สวยงามของ Daniele อันนี้ใช้สำหรับ python 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)
รหัสด้านล่างเป็นแนวทางของฉันภายใต้งูใหญ่ python 2.7 บวกกับชื่อแพคเกจ "pydot-ng" เพื่อสร้างไฟล์ PDF ที่มีกฎการตัดสินใจ ฉันหวังว่ามันจะเป็นประโยชน์
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)
นี่คือวิธีในการแปลทรีทั้งหมดเป็นนิพจน์หลามเดียว (ไม่จำเป็นต้องเป็นมนุษย์อ่านได้เกินไป) โดยใช้ไลบรารีSKompiler :
from skompiler import skompile
skompile(dtree.predict).to('python/code')
สิ่งนี้สร้างขึ้นจากคำตอบของ @paulkernfeld หากคุณมี dataframe X พร้อมกับคุณสมบัติและ target dataframe y ที่มี resonses ของคุณและคุณต้องการทราบว่าค่า y ใดที่ลงท้ายด้วยโหนดใด (และ ant เพื่อพล็อตตาม) คุณสามารถทำสิ่งต่อไปนี้
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 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)
นี่คือฟังก์ชั่นการพิมพ์กฎของแผนผังการตัดสินใจเรียนรู้ scikit ภายใต้ python 3 และมีการออฟเซ็ตสำหรับบล็อกที่มีเงื่อนไขเพื่อให้โครงสร้างอ่านได้ง่ายขึ้น:
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 เช่น
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)
แก้ไขรหัสของ Zelazny7 เพื่อดึงข้อมูล SQL จากแผนผังการตัดสินใจ
# 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 เรียนรู้อย่างถูกต้องเอกสาร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
ชุดเพียง