<h4 dir="auto">Describe the workflow you want to enable</h4>
<p dir="auto">In my opinion It would be great to have the <code class="notranslate">dict</code> representation of decision trees in the library.</p>
<h4 dir="auto">Describe your proposed solution</h4>
<p dir="auto">The interface would be the similar to <a href="https://scikit-learn.org/stable/modules/generated/sklearn.tree.export_text.html#sklearn.tree.export_text" rel="nofollow"><code class="notranslate">tree.export_text</code></a>, maybe <code class="notranslate">tree.export_dict</code>?</p>
<h4 dir="auto">Additional context</h4>
<p dir="auto">More than once I manually exported a decision tree from a model to a dictionary like so:</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="tree = {
  &quot;feature&quot;: &quot;feature 1&quot;,
  &quot;value&quot;: 0.5,
  &quot;left&quot;: {
    &quot;feature&quot;: &quot;feature 2&quot;,
    &quot;value&quot;: 0.1,
    &quot;left&quot;: &quot;category 1&quot;,
    &quot;right&quot;: &quot;category 2&quot;
  },
  &quot;right&quot;: &quot;category 3&quot;
}"><pre class="notranslate"><span class="pl-s1">tree</span> <span class="pl-c1">=</span> {
  <span class="pl-s">"feature"</span>: <span class="pl-s">"feature 1"</span>,
  <span class="pl-s">"value"</span>: <span class="pl-c1">0.5</span>,
  <span class="pl-s">"left"</span>: {
    <span class="pl-s">"feature"</span>: <span class="pl-s">"feature 2"</span>,
    <span class="pl-s">"value"</span>: <span class="pl-c1">0.1</span>,
    <span class="pl-s">"left"</span>: <span class="pl-s">"category 1"</span>,
    <span class="pl-s">"right"</span>: <span class="pl-s">"category 2"</span>
  },
  <span class="pl-s">"right"</span>: <span class="pl-s">"category 3"</span>
}</pre></div>
<p dir="auto">And then run the tree with a simple recursive function like:</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="def run_tree(leaf, data):
    if not isinstance(leaf, dict):
        return leaf
    next_leaf = leaf[&quot;left&quot;] if data[leaf[&quot;feature&quot;]] &lt;= leaf[&quot;value&quot;] else leaf[&quot;right&quot;]
    return run_tree(
        next_leaf,
        data,
    )"><pre class="notranslate"><span class="pl-k">def</span> <span class="pl-en">run_tree</span>(<span class="pl-s1">leaf</span>, <span class="pl-s1">data</span>):
    <span class="pl-k">if</span> <span class="pl-c1">not</span> <span class="pl-en">isinstance</span>(<span class="pl-s1">leaf</span>, <span class="pl-s1">dict</span>):
        <span class="pl-k">return</span> <span class="pl-s1">leaf</span>
    <span class="pl-s1">next_leaf</span> <span class="pl-c1">=</span> <span class="pl-s1">leaf</span>[<span class="pl-s">"left"</span>] <span class="pl-k">if</span> <span class="pl-s1">data</span>[<span class="pl-s1">leaf</span>[<span class="pl-s">"feature"</span>]] <span class="pl-c1">&lt;=</span> <span class="pl-s1">leaf</span>[<span class="pl-s">"value"</span>] <span class="pl-k">else</span> <span class="pl-s1">leaf</span>[<span class="pl-s">"right"</span>]
    <span class="pl-k">return</span> <span class="pl-en">run_tree</span>(
        <span class="pl-s1">next_leaf</span>,
        <span class="pl-s1">data</span>,
    )</pre></div>
<p dir="auto">I like this representation because:</p>
<ol dir="auto">
<li>The inference function is very simple.</li>
<li>Removes <code class="notranslate">sklearn</code> dependency for inference</li>
<li>It can be easily ported to other languages</li>
<li>It's very descriptive and good for educational purposes</li>
</ol>
<p dir="auto">My quick implementation of <code class="notranslate">export_dict</code>:</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="def export_dict(clf, feature_names=None):
    tree = clf.tree_
    if feature_names is None:
        feature_names = range(clf.max_features_)
    
    # Build tree nodes
    tree_nodes = []
    for i in range(tree.node_count):
        if (tree.children_left[i] == tree.children_right[i]):
            tree_nodes.append(
                clf.classes_[np.argmax(tree.value[i])]
            )
        else:
            tree_nodes.append({
                &quot;feature&quot;: feature_names[tree.feature[i]],
                &quot;value&quot;: tree.threshold[i],
                &quot;left&quot;: tree.children_left[i],
                &quot;right&quot;: tree.children_right[i],
            })
    
    # Link tree nodes
    for node in tree_nodes:
        if isinstance(node, dict):
            node[&quot;left&quot;] = tree_nodes[node[&quot;left&quot;]]
        if isinstance(node, dict):
            node[&quot;right&quot;] = tree_nodes[node[&quot;right&quot;]]
    
    # Return root node
    return tree_nodes[0]"><pre class="notranslate"><span class="pl-k">def</span> <span class="pl-en">export_dict</span>(<span class="pl-s1">clf</span>, <span class="pl-s1">feature_names</span><span class="pl-c1">=</span><span class="pl-c1">None</span>):
    <span class="pl-s1">tree</span> <span class="pl-c1">=</span> <span class="pl-s1">clf</span>.<span class="pl-s1">tree_</span>
    <span class="pl-k">if</span> <span class="pl-s1">feature_names</span> <span class="pl-c1">is</span> <span class="pl-c1">None</span>:
        <span class="pl-s1">feature_names</span> <span class="pl-c1">=</span> <span class="pl-en">range</span>(<span class="pl-s1">clf</span>.<span class="pl-s1">max_features_</span>)
    
    <span class="pl-c"># Build tree nodes</span>
    <span class="pl-s1">tree_nodes</span> <span class="pl-c1">=</span> []
    <span class="pl-k">for</span> <span class="pl-s1">i</span> <span class="pl-c1">in</span> <span class="pl-en">range</span>(<span class="pl-s1">tree</span>.<span class="pl-s1">node_count</span>):
        <span class="pl-k">if</span> (<span class="pl-s1">tree</span>.<span class="pl-s1">children_left</span>[<span class="pl-s1">i</span>] <span class="pl-c1">==</span> <span class="pl-s1">tree</span>.<span class="pl-s1">children_right</span>[<span class="pl-s1">i</span>]):
            <span class="pl-s1">tree_nodes</span>.<span class="pl-en">append</span>(
                <span class="pl-s1">clf</span>.<span class="pl-s1">classes_</span>[<span class="pl-s1">np</span>.<span class="pl-en">argmax</span>(<span class="pl-s1">tree</span>.<span class="pl-s1">value</span>[<span class="pl-s1">i</span>])]
            )
        <span class="pl-k">else</span>:
            <span class="pl-s1">tree_nodes</span>.<span class="pl-en">append</span>({
                <span class="pl-s">"feature"</span>: <span class="pl-s1">feature_names</span>[<span class="pl-s1">tree</span>.<span class="pl-s1">feature</span>[<span class="pl-s1">i</span>]],
                <span class="pl-s">"value"</span>: <span class="pl-s1">tree</span>.<span class="pl-s1">threshold</span>[<span class="pl-s1">i</span>],
                <span class="pl-s">"left"</span>: <span class="pl-s1">tree</span>.<span class="pl-s1">children_left</span>[<span class="pl-s1">i</span>],
                <span class="pl-s">"right"</span>: <span class="pl-s1">tree</span>.<span class="pl-s1">children_right</span>[<span class="pl-s1">i</span>],
            })
    
    <span class="pl-c"># Link tree nodes</span>
    <span class="pl-k">for</span> <span class="pl-s1">node</span> <span class="pl-c1">in</span> <span class="pl-s1">tree_nodes</span>:
        <span class="pl-k">if</span> <span class="pl-en">isinstance</span>(<span class="pl-s1">node</span>, <span class="pl-s1">dict</span>):
            <span class="pl-s1">node</span>[<span class="pl-s">"left"</span>] <span class="pl-c1">=</span> <span class="pl-s1">tree_nodes</span>[<span class="pl-s1">node</span>[<span class="pl-s">"left"</span>]]
        <span class="pl-k">if</span> <span class="pl-en">isinstance</span>(<span class="pl-s1">node</span>, <span class="pl-s1">dict</span>):
            <span class="pl-s1">node</span>[<span class="pl-s">"right"</span>] <span class="pl-c1">=</span> <span class="pl-s1">tree_nodes</span>[<span class="pl-s1">node</span>[<span class="pl-s">"right"</span>]]
    
    <span class="pl-c"># Return root node</span>
    <span class="pl-k">return</span> <span class="pl-s1">tree_nodes</span>[<span class="pl-c1">0</span>]</pre></div>
<p dir="auto">If this is a good idea I'd be glad to help!</p>
<p dir="auto">Currently, there are two options to get the decision tree representations: <code class="notranslate">export_graphviz</code> and <code class="notranslate">export_text</code>.</p>
<p dir="auto">I would like to add <code class="notranslate">export_dict</code>, which will output the decision as a nested dictionary.</p>
<p dir="auto">While you can convert the graphviz representation using cli tools, it's a bit unruly and is a weird workflow. When converting back, the nesting is also kind of weird, with <code class="notranslate">/n</code> chars floating around.</p>
<p dir="auto">The main utility for json/dict representations are</p>
<ol dir="auto">
<li>That they're far easier to work with for any front-end work.</li>
<li>Working with a dict allows you to use the representation easily for any downstream tasks using the decision tree (that aren't raw inference).</li>
</ol>