Render fitted TuiML decision trees as figures, ASCII text, or GraphViz DOT.

This module turns the node objects built by TuiML's tree learners into something a human can read. It accepts any fitted estimator from trees -- DecisionTreeClassifier and DecisionTreeRegressor, RandomForestClassifier / RandomForestRegressor ensembles (one member tree at a time) and DecisionStumpClassifier -- as well as a bare tree-node object.

Three output formats

plot_tree A matplotlib figure of rounded/square node boxes joined by edges. export_text A plain ASCII rule listing, for terminals, logs and docstrings. export_graphviz A DOT-language string (or file) for rendering with GraphViz.

How the drawing pipeline fits together

Those learners do not share a node class: CART uses TreeNode, the streaming learner uses HoeffdingNode, M5 names its split fields split_attr / split_value, and the stump keeps no nodes at all. Every entry point therefore funnels through one duck-typing shim, _TreeNodeAdapter, before any layout happens:

fitted estimator
      |  _get_tree_root / _check_is_fitted   (unwrap .tree_ or .estimators_)
      v
_TreeNodeAdapter                             (one interface for every node type)
      |  _build_bigtree_nodes                (+ _build_node_label per node)
      v
bigtree.Node tree
      |  bigtree.reingold_tilford            (assigns tidy x / y per node)
      v
_draw_bigtree                                (boxes, edges, edge labels -> Axes)

export_text and export_graphviz share the adapter but skip the bigtree layout entirely -- they walk the adapter tree recursively and emit characters instead of artists.

Notes

Both matplotlib and bigtree are imported lazily at module load and are optional: their absence is recorded in the module flags HAS_MATPLOTLIB and HAS_BIGTREE rather than raised. Only plot_tree needs them, and it raises ImportError at call time if either is missing. The text and DOT exports depend on neither, so they work in a headless install.
python
>>> from tuiml.datasets import load_iris
>>> from tuiml.algorithms.trees import DecisionTreeClassifier
>>> from tuiml.evaluation.visualization import plot_tree
>>> X, y = load_iris()
>>> clf = DecisionTreeClassifier(max_depth=2, random_state=0).fit(X, y)
>>> fig = plot_tree(clf, feature_names=['sl', 'sw', 'pl', 'pw'])  # doctest: +SKIP

Functions

Func

plot_tree

Line 1035
plot_tree(decision_tree, *, max_depth=None, feature_names=None, class_names=None, label='all', filled=False, impurity=True, node_ids=False, proportion=False, rounded=False, precision=3, ax=None, fontsize=None, tree_index=0, figsize=None, save_path=None, title=None)

Draw a fitted decision tree as a matplotlib figure of labelled boxes.

Each internal node is drawn as a box holding its split condition, each leaf as a box holding its prediction, and edges run from a parent's bottom edge to a child's top edge. Positions come from the Reingold-Tilford tidy tree algorithm, which centres each parent over its children and keeps subtrees from overlapping; box sizes are then measured from the label text and the layout is scaled so no two boxes collide.

The signature follows scikit-learn's plot_tree so existing calls port over, with four TuiML-only additions (tree_index, figsize, save_path, title) at the end.

Parameters

decision_tree
object
The fitted tree to plot. Accepts a single-tree estimator with a tree_ attribute (for example DecisionTreeClassifier, with estimators_ such as RandomForestClassifier (see tree_index), a fitted DecisionStumpClassifier, or a bare tree node object.
max_depth
int = None
The maximum depth of the representation. If None, the tree is fully generated.
feature_names
array-like of str = None
Names of each of the features. If None, generic names will be used ("X[0]", "X[1]", ...).
class_names
array-like of str = None
Names of each of the target classes in ascending numerical order, used to label leaves. Only relevant for classification. A class value that cannot index this list is printed as-is rather than raising.
label
{'all', 'root', 'none'} = 'all'
Which nodes get the informative samples = ... line: 'all' shows it at every node, 'root' only at the top node, 'none' at no node. Split conditions and leaf predictions are always drawn.
filled
bool = False
When True, fill node boxes with a color encoding the node's role: blue for internal nodes, green for classification leaves, orange for regression leaves, gray for max_depth truncation markers.
impurity
bool = True
Accepted for scikit-learn signature compatibility but currently ignored: TuiML node labels do not include an impurity line.
node_ids
bool = False
Accepted for scikit-learn signature compatibility but currently ignored by this function. export_graphviz does honour it.
proportion
bool = False
Accepted for scikit-learn signature compatibility but currently ignored; sample counts are always drawn as absolute counts.
rounded
bool = False
When True, draw node boxes with rounded corners instead of square ones. Fonts are unaffected.
precision
int = 3
Number of significant digits (%g) used when formatting a floating-point threshold in a node label.
ax
matplotlib.axes.Axes = None
Axes to draw on. If None, a new figure and axes are created using figsize and the TuiML plot style.
fontsize
int = None
Point size for node text. If None, chosen from the node count within the range 8-12 so that large trees stay legible.
tree_index
int = 0
For ensembles with estimators_, which member tree to draw (TuiML extension).
figsize
tuple of float = None
Figure size (width, height) in inches (TuiML extension). Ignored when ax is given. If None, computed from the tree's leaf count and depth as (max(10, 3 * n_leaves), max(6, 2.5 * (depth + 1))).
save_path
str = None
If given, the figure is also written to this path as a 300 dpi PNG (TuiML extension).
title
str = None
Custom plot title (TuiML extension). Defaults to the estimator's class name, or "Random Forest - Tree <i>" for an ensemble member.

Returns

annotations
list
Always an empty list. The return value exists for signature compatibility with scikit-learn's plot_tree, which returns the annotation artists; this implementation draws FancyBboxPatch boxes directly onto the axes instead and does not collect them. Retrieve the figure with ax.get_figure() or plt.gcf() if you need it.

Raises

ImportError
If matplotlib or bigtree is not installed. Both are optional dependencies imported lazily at module load; install the latter with pip install bigtree.
ValueError
If decision_tree is not fitted, or if tree_index is out of range for the ensemble's estimators_.

Notes

The figure is displayed with plt.show() before returning, so in a script with an interactive backend this call blocks until the window is closed. Set a non-interactive backend (matplotlib.use('Agg')) when plotting headlessly, and pass save_path to keep the result.
python
>>> from tuiml.datasets import load_iris
>>> from tuiml.algorithms.trees import DecisionTreeClassifier
>>> from tuiml.evaluation.visualization import plot_tree
>>> X, y = load_iris()
>>> names = ['sepal_l', 'sepal_w', 'petal_l', 'petal_w']
>>> clf = DecisionTreeClassifier(max_depth=3, random_state=0).fit(X, y)
>>> plot_tree(clf, feature_names=names, filled=True)      # doctest: +SKIP

Draw only the top two levels of one tree out of a fitted forest, and save

it instead of inspecting it interactively:

python
>>> from tuiml.algorithms.trees import RandomForestClassifier
>>> rf = RandomForestClassifier(n_estimators=10, random_state=0).fit(X, y)
>>> plot_tree(rf, tree_index=3, max_depth=2, rounded=True,
...           save_path='tree3.png')                      # doctest: +SKIP
Func

export_text

Line 1491
export_text(decision_tree, feature_names: Optional[List[str]]=None, class_names: Optional[List[str]]=None, max_depth: int=10, spacing: int=3, decimals: int=2, show_weights: bool=False) -> str

Build a text report showing the rules of a decision tree.

This is similar to scikit-learn's export_text function and provides a human-readable ASCII representation of the tree structure.

Parameters

decision_tree
fitted tree model
A fitted TuiML tree algorithm instance with a tree_ attribute, or the tree root node itself.
feature_names
list of str
Feature names for display. If None, uses feature_{i} format.
class_names
list of str
Class names for classification trees. If None for classification, uses the tree's internal class representation.
max_depth
int = 10
Only the first max_depth levels of the tree are exported. Truncated branches will be marked with "...".
spacing
int = 3
Number of spaces between edges. The higher it is, the wider the result.
decimals
int = 2
Number of decimal digits to display for thresholds and values.
show_weights
bool = False
If True for classification trees, the class distribution (number of samples per class) will be exported on each leaf.

Returns

report
str
Text summary of all the rules in the decision tree.
python
>>> from tuiml.algorithms.trees import DecisionTreeClassifier
>>> from tuiml.datasets import load_iris
>>> X, y = load_iris()
>>> from tuiml.evaluation.visualization.trees import export_text
>>> clf = DecisionTreeClassifier().fit(X, y)
>>> print(export_text(clf, feature_names=['a', 'b']))   # doctest: +SKIP
|--- a <= 0.50
|   |--- class: 0
|--- a >  0.50
|   |--- b <= 1.00
|   |   |--- class: 1
|   |--- b >  1.00
|   |   |--- class: 2
Func

export_graphviz

Line 1635
export_graphviz(decision_tree, out_file: Optional[Union[str, StringIO]]=None, feature_names: Optional[List[str]]=None, class_names: Optional[List[str]]=None, label: str='all', filled: bool=False, leaves_parallel: bool=False, impurity: bool=True, node_ids: bool=False, proportion: bool=False, rotate: bool=False, rounded: bool=False, special_characters: bool=False, precision: int=3, fontname: str='helvetica', max_depth: Optional[int]=None) -> Optional[str]

Export a decision tree in DOT format.

This function generates a GraphViz representation of the decision tree, which can be rendered using the GraphViz dot tool:

$ dot -Tpng tree.dot -o tree.png

Parameters

decision_tree
fitted tree model
A fitted TuiML tree algorithm instance with a tree_ attribute, or the tree root node itself.
out_file
str or file-like object
Handle or name of the output file. If None, the result is returned as a string.
feature_names
list of str
Feature names for display. If None, uses feature_{i} format.
class_names
list of str
Class names for classification trees.
label
{'all', 'root', 'none'} = 'all'
Whether to show informative labels (sample counts, etc.). Options include 'all' to show at every node, 'root' to show only at the top root node, or 'none' to not show at any node.
filled
bool = False
When set to True, paint nodes to indicate leaf type (classification vs regression).
leaves_parallel
bool = False
When set to True, draw all leaf nodes at the bottom of the tree.
impurity
bool = True
When set to True, show the impurity (where available) at each node.
node_ids
bool = False
When set to True, show the ID number on each node.
proportion
bool = False
When set to True, change the display of sample counts to be proportions of the total.
rotate
bool = False
When set to True, orient tree left to right rather than top-down.
rounded
bool = False
When set to True, draw node boxes with rounded corners.
special_characters
bool = False
When set to True, use special characters (e.g., Greek letters) for certain symbols.
precision
int = 3
Number of digits of precision for floating point values.
fontname
str = 'helvetica'
Name of font used to render text.
max_depth
int
Maximum depth of the tree to export. If None, exports the entire tree.

Returns

dot_data
str or None
String representation of the input tree in GraphViz dot format. Only returned if out_file is None.
python
>>> from tuiml.algorithms.trees import DecisionTreeClassifier
>>> from tuiml.datasets import load_iris
>>> X, y = load_iris()
>>> from tuiml.evaluation.visualization.trees import export_graphviz
>>> clf = DecisionTreeClassifier().fit(X, y)
>>> dot_data = export_graphviz(clf, feature_names=['a', 'b'])
>>> print(dot_data)                                    # doctest: +SKIP
digraph Tree {
node [shape=box, style="filled", color="black", fontname="helvetica"] ;
...