Есть так много сообщений так о том, как извлечь правила дерева решений sklearn, но я не смог найти ничего об использовании pandas.
Возьмем, к примеру, эти данные и модель, как показано ниже.
# Create Decision Tree classifer object
clf = DecisionTreeClassifier(criterion = "entropy", max_depth=3)
# Train Decision Tree Classifer
clf = clf.fit(X_train,y_train)
Результат:
Ожидал:
В этом примере есть 8 правил.
Слева направо обратите внимание, что фрейм данных df
r1 = (df['glucose']<=127.5) & (df['bmi']<=26.45) & (df['bmi']<=9.1)
……
r8 = (df['glucose']>127.5) & (df['bmi']>28.15) & (df['glucose']>158.5)
Я не мастер извлечения правил дерева решений sklearn. Получение логических условий pandas поможет мне рассчитать выборки и другие показатели для каждого правила. Поэтому я хочу извлечь каждое правило из логического условия pandas.
Прежде всего, давайте используем scikit документация в структуре дерева решений, чтобы получить информацию о построенном дереве:
n_nodes = clf.tree_.node_count
children_left = clf.tree_.children_left
children_right = clf.tree_.children_right
feature = clf.tree_.feature
threshold = clf.tree_.threshold
Затем мы определяем две рекурсивные функции. Первый найдет путь от корня дерева до создания определенного узла (в нашем случае всех листьев). Второй запишет конкретные правила, используемые для создания узла, используя его путь создания:
def find_path(node_numb, path, x):
path.append(node_numb)
if node_numb == x:
return True
left = False
right = False
if (children_left[node_numb] !=-1):
left = find_path(children_left[node_numb], path, x)
if (children_right[node_numb] !=-1):
right = find_path(children_right[node_numb], path, x)
if left or right :
return True
path.remove(node_numb)
return False
def get_rule(path, column_names):
mask = ''
for index, node in enumerate(path):
#We check if we are not in the leaf
if index!=len(path)-1:
# Do we go under or over the threshold ?
if (children_left[node] == path[index+1]):
mask += "(df['{}']<= {}) \t ".format(column_names[feature[node]], threshold[node])
else:
mask += "(df['{}']> {}) \t ".format(column_names[feature[node]], threshold[node])
# We insert the & at the right places
mask = mask.replace("\t", "&", mask.count("\t") - 1)
mask = mask.replace("\t", "")
return mask
Наконец, мы используем эти две функции, чтобы сначала сохранить путь создания каждого листа. А затем сохранить правила, используемые для создания каждого листа:
# Leaves
leave_id = clf.apply(X_test)
paths = {}
for leaf in np.unique(leave_id):
path_leaf = []
find_path(0, path_leaf, leaf)
paths[leaf] = np.unique(np.sort(path_leaf))
rules = {}
for key in paths:
rules[key] = get_rule(paths[key], pima.columns)
С данными, которые вы дали, вывод:
rules =
{3: "(df['insulin']<= 127.5) & (df['bp']<= 26.450000762939453) & (df['bp']<= 9.100000381469727) ",
4: "(df['insulin']<= 127.5) & (df['bp']<= 26.450000762939453) & (df['bp']> 9.100000381469727) ",
6: "(df['insulin']<= 127.5) & (df['bp']> 26.450000762939453) & (df['skin']<= 27.5) ",
7: "(df['insulin']<= 127.5) & (df['bp']> 26.450000762939453) & (df['skin']> 27.5) ",
10: "(df['insulin']> 127.5) & (df['bp']<= 28.149999618530273) & (df['insulin']<= 145.5) ",
11: "(df['insulin']> 127.5) & (df['bp']<= 28.149999618530273) & (df['insulin']> 145.5) ",
13: "(df['insulin']> 127.5) & (df['bp']> 28.149999618530273) & (df['insulin']<= 158.5) ",
14: "(df['insulin']> 127.5) & (df['bp']> 28.149999618530273) & (df['insulin']> 158.5) "}
Поскольку правила являются строками, вы не можете напрямую вызывать их с помощью df[rules[3]]
, вы должны использовать функцию eval вот так df[eval(rules[3])]
Проблема anthor заключается в том, что при использовании выбора фрейма данных на основе логических условий, таких как df[rules[4]], возникает ошибка. как это решить
@Jack Я изменил рекурсивную функцию get_rule
для отображения столбцов. И я указал, почему вы получаете ошибку в конце ответа :)
Я нашел еще одно решение этой проблемы (вторая часть решения, опубликованного vlemaistre), которое позволяет пользователю запускать любой узел и подмножество данных на основе логического условия pandas.
node_id = 3
def datatree_path_summarystats(node_id):
for k, v in paths.items():
if node_id in v:
d = k,v
ruleskey = d[0]
numberofsteps = sum(map(lambda x : x<node_id, d[1]))
for k, v in rules.items():
if k == ruleskey:
b = k,v
stringsubset = b[1]
datasubset = "&".join(stringsubset.split('&')[:numberofsteps])
return datasubset
datasubset = datatree_path_summarystats(node_id)
df[eval(datasubset)]
Эта функция проходит по путям, которые содержат идентификатор узла, который вы ищете. Затем он разделит правило на основе этого количества узлов, создав логику для подмножества фрейма данных на основе этого конкретного узла.
Теперь вы можете использовать export_text.
from sklearn.tree import export_text
r = export_text(loan_tree, feature_names=(list(X_train.columns)))
print(r)
Полный пример из склерн
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)
отлично, но я хочу использовать имена столбцов. Не могли бы вы ответить на него?