Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Orange/widgets/visualize/owpythagorastree.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ def __init__(self):
box_plot, self, 'show_legend', label='Show legend',
callback=self.update_show_legend)

gui.button(self.controlArea, self, label="Redraw", callback=self.redraw)

# Stretch to fit the rest of the unsused area
gui.rubber(self.controlArea)

Expand All @@ -133,7 +135,7 @@ def __init__(self):
self.view.setRenderHint(QPainter.Antialiasing, True)
self.mainArea.layout().addWidget(self.view)

self.ptree = PythagorasTreeViewer()
self.ptree = PythagorasTreeViewer(self)
self.scene.addItem(self.ptree)
self.view.set_central_widget(self.ptree)

Expand Down Expand Up @@ -224,6 +226,10 @@ def update_size_calc(self):
self._update_log_scale_slider()
self.invalidate_tree()

def redraw(self):
self.tree_adapter.shuffle_children()
self.invalidate_tree()

def invalidate_tree(self):
"""When the tree needs to be completely recalculated."""
if self.model is not None:
Expand Down
16 changes: 13 additions & 3 deletions Orange/widgets/visualize/pythagorastreeviewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ class PythagorasTreeViewer(QGraphicsWidget):

def __init__(self, parent=None, adapter=None, depth_limit=0, padding=0,
**kwargs):
super().__init__(parent)
super().__init__()
self.parent = parent

# In case a tree was passed, it will be handled at the end of init
self.tree_adapter = None
Expand Down Expand Up @@ -137,17 +138,19 @@ def set_tree(self, tree_adapter, weight_adjustment=lambda x: x,
"""
self.clear_tree()
self.tree_adapter = tree_adapter
self.weight_adjustment = weight_adjustment

if self.tree_adapter is not None:
self.root = self._calculate_tree(self.tree_adapter, weight_adjustment)
self.root = self._calculate_tree(self.tree_adapter, self.weight_adjustment)
self.set_depth_limit(tree_adapter.max_depth)
self.target_class_changed(target_class_index)
self._draw_tree(self.root)

def set_size_calc(self, weight_adjustment):
"""Set the weight adjustment on the tree. Redraws the whole tree."""
# Since we have to redraw the whole tree anyways, just call `set_tree`
self.set_tree(self.tree_adapter, weight_adjustment,
self.weight_adjustment = weight_adjustment
self.set_tree(self.tree_adapter, self.weight_adjustment,
self._target_class_index)

def set_depth_limit(self, depth):
Expand Down Expand Up @@ -467,6 +470,13 @@ def _propagate_to_parents(self, graphics_item, fnc, other_fnc):
# propagate up the tree
self._propagate_to_parents(parent, fnc, other_fnc)

def mouseDoubleClickEvent(self, event):
self.tree_node.tree.reverse_children(self.tree_node.label)
p = self.parentWidget() # PythagorasTreeViewer
p.set_tree(p.tree_adapter, p.weight_adjustment, self.tree_node.target_class_index)
widget = p.parent # OWPythagorasTree
widget._update_main_area()

def selection_changed(self):
"""Handle selection changed."""
self.any_selected = len(self.scene().selectedItems()) > 0
Expand Down
10 changes: 10 additions & 0 deletions Orange/widgets/visualize/utils/tree/skltreeadapter.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Tree adapter class for sklearn trees."""
from collections import OrderedDict
import random

import numpy as np
from Orange.widgets.visualize.utils.tree.treeadapter import BaseTreeAdapter
Expand Down Expand Up @@ -63,6 +64,15 @@ def __left_child(self, node):
def __right_child(self, node):
return self._tree.children_right[node]

def reverse_children(self, node):
self._tree.children_left[node], self._tree.children_right[node] = \
self._tree.children_right[node], self._tree.children_left[node]

def shuffle_children(self):
for i in range(self.num_nodes):
if random.randrange(2) == 0:
self.reverse_children(i)

@memoize_method(maxsize=1024)
def get_distribution(self, node):
value = self._tree.value[node]
Expand Down
26 changes: 26 additions & 0 deletions Orange/widgets/visualize/utils/tree/treeadapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from abc import ABCMeta, abstractmethod
from functools import reduce
from operator import add
import random


class BaseTreeAdapter(metaclass=ABCMeta):
Expand Down Expand Up @@ -117,6 +118,20 @@ def children(self, node):
"""
pass

def reverse_children(self, node):
"""Reverse children of a given node.

Parameters
----------
node : object
"""
pass

def shuffle_children(self):
"""Randomly shuffle node's children in the entire tree.
"""
pass

@abstractmethod
def get_distribution(self, node):
"""Get the distribution of types for a given node.
Expand Down Expand Up @@ -295,6 +310,17 @@ def is_leaf(self, node):
def children(self, node):
return [child for child in node.children if child is not None]

def reverse_children(self, node):
node.children = node.children[::-1]

def shuffle_children(self):
def _shuffle_children(node):
if node and node.children:
random.shuffle(node.children)
for c in node.children:
_shuffle_children(c)
_shuffle_children(self.root)

def get_distribution(self, node):
return [node.value]

Expand Down