-
-
Notifications
You must be signed in to change notification settings - Fork 25.9k
[MRG] Matplotlib tree plotting #9251
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
amueller
merged 73 commits into
scikit-learn:master
from
amueller:matplotlib_tree_plotting
Oct 11, 2018
Merged
Changes from all commits
Commits
Show all changes
73 commits
Select commit
Hold shift + click to select a range
1881ece
add reingold tillford tree layout algorithm
amueller bd7d022
add first silly implementation of matplotlib based plotting for trees
amueller 287c1d2
object oriented design for export_graphviz so it can be extended
amueller 0d5e3e2
add class for mlp export
amueller 8f52d87
add colors
amueller 4a5fe67
separately scale x and y, add arrowheads, fix strings
amueller ddb6c16
implement max_depth
amueller fed2d1d
don't use alpha for coloring because it makes boxes transparent
amueller 5145ed2
remove unused variables
amueller 8663ad7
vertical center of boxes
amueller d750deb
fix/simplify newline trimming
amueller d3c17ea
somewhere in the middle of stuff
amueller 823ce1f
remove "find_longest_child" for now, fix tests
amueller 0229d5d
make scalex and scaley internal, and ax local.
amueller a2df69e
add some margin to the max bbox width
amueller 5212f59
add _BaseTreeExporter baseclass
amueller 60c0b73
add docstring to plot_tree
amueller 3b4a730
use data coordinates so we can put the plot in a subplot, remove some…
amueller a30f634
remove scalex, scaley, add automatic font size
amueller 27a29ac
use rendered stuff for setting limits (well nearly there)
amueller c2e6d31
Merge branch 'master' into matplotlib_tree_plotting
amueller 538d257
import plot_tree into tree module
amueller c6ecbb2
set limits before font size adjustment?
amueller fc7bdbe
add tree plotting via matplotlib to iris example and to docs
amueller 9d672ab
pep8 fix
amueller 1c8b8d6
skip doctest on plot_tree because matplotlib is not installed on all …
amueller 474c557
redo everything in axis pixel coordinates
amueller 4c97f37
fix max-depth
amueller b31e7ec
consider height in fontsize computation
amueller 9f36648
fix error when max_depth is None
amueller 697aede
Merge branch 'master' into matplotlib_tree_plotting
amueller d0b2c95
Merge branch 'master' into matplotlib_tree_plotting
amueller bac2c51
Merge branch 'matplotlib_tree_plotting' of github.com:amueller/scikit…
amueller 752135e
add docstring for tree plotting fontsize
amueller 9d7a3fa
Merge branch 'master' into matplotlib_tree_plotting
amueller fe92f74
starting on jnothman's review
amueller 83a4a2e
renaming fixes
amueller a1ba414
whatsnew for tree plotting
amueller df6620d
clear axes prior to doing anything.
amueller 02aeeab
fix doctests
amueller 072a66b
skip matplotlib doctest
amueller 88cc060
trying to debug circle failure
amueller 9eb0549
trying to show full traceback
amueller 59714c0
Merge branch 'master' into matplotlib_tree_plotting
amueller 61dd70a
more print debugging
amueller 5d84743
remove debugging crud
amueller cf4a620
hack around matplotlib <1.5 issues
amueller db61249
copy bbox args because old matplotlib is weird.
amueller 5beedf2
pep8 fixes
amueller 64d47ac
add explicit boxstyle
amueller cbfce1c
more pep8
amueller f92aca1
even more pep8
amueller d6cc6ad
add comment about matplotlib version requirement
amueller daae2e2
remove redundant file
amueller 82b5459
Merge branch 'master' into matplotlib_tree_plotting
amueller 7d76ca9
add whatsnew entry that the merge lost
amueller 1a9a874
Merge branch 'master' into matplotlib_tree_plotting
amueller 1937f3d
fix merge issue
amueller db464a3
more merge issues
amueller 042865a
whitespace ...
amueller 1a16750
remove doctest skip to see what's happening
amueller 6f2d597
added some simple invariance tests buchheim function
amueller 6803f96
refactor
amueller 7b62316
added some tests of plot_tree
amueller 817b2bb
put skip back in, fix typo, fix versionadded number
amueller 55c7d36
remove unused parameters special_characters and parallel_leaves from …
amueller c228ae0
Merge branch 'master' into matplotlib_tree_plotting
amueller 69e7c40
Merge branch 'master' into matplotlib_tree_plotting
amueller fc756d0
Merge branch 'matplotlib_tree_plotting' of github.com:amueller/scikit…
amueller 9554a87
rename tests to test_reingold_tilford
amueller becfa07
Merge branch 'master' into matplotlib_tree_plotting
amueller 435d217
added license header from pymag-trees repo
amueller 98e8d5a
remove duplicate test file.
amueller File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
# taken from https://github.com/llimllib/pymag-trees/blob/master/buchheim.py | ||
# with slight modifications | ||
|
||
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | ||
# Version 2, December 2004 | ||
# | ||
# Copyright (C) 2004 Sam Hocevar <[email protected]> | ||
# | ||
# Everyone is permitted to copy and distribute verbatim or modified | ||
# copies of this license document, and changing it is allowed as long | ||
# as the name is changed. | ||
# | ||
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | ||
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | ||
|
||
# 0. You just DO WHAT THE FUCK YOU WANT TO. | ||
|
||
|
||
import numpy as np | ||
|
||
|
||
class DrawTree(object): | ||
def __init__(self, tree, parent=None, depth=0, number=1): | ||
self.x = -1. | ||
self.y = depth | ||
self.tree = tree | ||
self.children = [DrawTree(c, self, depth + 1, i + 1) | ||
for i, c | ||
in enumerate(tree.children)] | ||
self.parent = parent | ||
self.thread = None | ||
self.mod = 0 | ||
self.ancestor = self | ||
self.change = self.shift = 0 | ||
self._lmost_sibling = None | ||
# this is the number of the node in its group of siblings 1..n | ||
self.number = number | ||
|
||
def left(self): | ||
return self.thread or len(self.children) and self.children[0] | ||
|
||
def right(self): | ||
return self.thread or len(self.children) and self.children[-1] | ||
|
||
def lbrother(self): | ||
n = None | ||
if self.parent: | ||
for node in self.parent.children: | ||
if node == self: | ||
return n | ||
else: | ||
n = node | ||
return n | ||
|
||
def get_lmost_sibling(self): | ||
if not self._lmost_sibling and self.parent and self != \ | ||
self.parent.children[0]: | ||
self._lmost_sibling = self.parent.children[0] | ||
return self._lmost_sibling | ||
lmost_sibling = property(get_lmost_sibling) | ||
|
||
def __str__(self): | ||
return "%s: x=%s mod=%s" % (self.tree, self.x, self.mod) | ||
|
||
def __repr__(self): | ||
return self.__str__() | ||
|
||
def max_extents(self): | ||
extents = [c.max_extents() for c in self. children] | ||
extents.append((self.x, self.y)) | ||
return np.max(extents, axis=0) | ||
|
||
|
||
def buchheim(tree): | ||
dt = first_walk(DrawTree(tree)) | ||
min = second_walk(dt) | ||
if min < 0: | ||
third_walk(dt, -min) | ||
return dt | ||
|
||
|
||
def third_walk(tree, n): | ||
tree.x += n | ||
for c in tree.children: | ||
third_walk(c, n) | ||
|
||
|
||
def first_walk(v, distance=1.): | ||
if len(v.children) == 0: | ||
if v.lmost_sibling: | ||
v.x = v.lbrother().x + distance | ||
else: | ||
v.x = 0. | ||
else: | ||
default_ancestor = v.children[0] | ||
for w in v.children: | ||
first_walk(w) | ||
default_ancestor = apportion(w, default_ancestor, distance) | ||
# print("finished v =", v.tree, "children") | ||
execute_shifts(v) | ||
|
||
midpoint = (v.children[0].x + v.children[-1].x) / 2 | ||
|
||
w = v.lbrother() | ||
if w: | ||
v.x = w.x + distance | ||
v.mod = v.x - midpoint | ||
else: | ||
v.x = midpoint | ||
return v | ||
|
||
|
||
def apportion(v, default_ancestor, distance): | ||
w = v.lbrother() | ||
if w is not None: | ||
# in buchheim notation: | ||
# i == inner; o == outer; r == right; l == left; r = +; l = - | ||
vir = vor = v | ||
vil = w | ||
vol = v.lmost_sibling | ||
sir = sor = v.mod | ||
sil = vil.mod | ||
sol = vol.mod | ||
while vil.right() and vir.left(): | ||
vil = vil.right() | ||
vir = vir.left() | ||
vol = vol.left() | ||
vor = vor.right() | ||
vor.ancestor = v | ||
shift = (vil.x + sil) - (vir.x + sir) + distance | ||
if shift > 0: | ||
move_subtree(ancestor(vil, v, default_ancestor), v, shift) | ||
sir = sir + shift | ||
sor = sor + shift | ||
sil += vil.mod | ||
sir += vir.mod | ||
sol += vol.mod | ||
sor += vor.mod | ||
if vil.right() and not vor.right(): | ||
vor.thread = vil.right() | ||
vor.mod += sil - sor | ||
else: | ||
if vir.left() and not vol.left(): | ||
vol.thread = vir.left() | ||
vol.mod += sir - sol | ||
default_ancestor = v | ||
return default_ancestor | ||
|
||
|
||
def move_subtree(wl, wr, shift): | ||
subtrees = wr.number - wl.number | ||
# print(wl.tree, "is conflicted with", wr.tree, 'moving', subtrees, | ||
# 'shift', shift) | ||
# print wl, wr, wr.number, wl.number, shift, subtrees, shift/subtrees | ||
wr.change -= shift / subtrees | ||
wr.shift += shift | ||
wl.change += shift / subtrees | ||
wr.x += shift | ||
wr.mod += shift | ||
|
||
|
||
def execute_shifts(v): | ||
shift = change = 0 | ||
for w in v.children[::-1]: | ||
# print("shift:", w, shift, w.change) | ||
w.x += shift | ||
w.mod += shift | ||
change += w.change | ||
shift += w.shift + change | ||
|
||
|
||
def ancestor(vil, v, default_ancestor): | ||
# the relevant text is at the bottom of page 7 of | ||
# "Improving Walker's Algorithm to Run in Linear Time" by Buchheim et al, | ||
# (2002) | ||
# http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.16.8757&rep=rep1&type=pdf | ||
if vil.ancestor in v.parent.children: | ||
return vil.ancestor | ||
else: | ||
return default_ancestor | ||
|
||
|
||
def second_walk(v, m=0, depth=0, min=None): | ||
v.x += m | ||
v.y = depth | ||
|
||
if min is None or v.x < min: | ||
min = v.x | ||
|
||
for w in v.children: | ||
min = second_walk(w, m + v.mod, depth + 1, min) | ||
|
||
return min | ||
|
||
|
||
class Tree(object): | ||
def __init__(self, label="", node_id=-1, *children): | ||
self.label = label | ||
self.node_id = node_id | ||
if children: | ||
self.children = children | ||
else: | ||
self.children = [] |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this file not include a more extensive license?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added the license.