Skip to content

Commit

Permalink
Scatter plot: Bring discrete attributes functionality back"
Browse files Browse the repository at this point in the history
This reverts commit f193420.
  • Loading branch information
VesnaT committed May 14, 2021
1 parent 3773093 commit a2b32db
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 85 deletions.
79 changes: 63 additions & 16 deletions Orange/widgets/visualize/owscatterplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@

import pyqtgraph as pg

from Orange.data import Table, Domain, DiscreteVariable, Variable, \
ContinuousVariable
from Orange.data import Table, Domain, DiscreteVariable, Variable
from Orange.data.sql.table import SqlTable, AUTO_DL_LIMIT
from Orange.preprocess.score import ReliefF, RReliefF

from Orange.widgets import gui
from Orange.widgets import gui, report
from Orange.widgets.io import MatplotlibFormat, MatplotlibPDFFormat
from Orange.widgets.settings import (
Setting, ContextSetting, SettingProvider, IncompatibleContext)
from Orange.widgets.utils import get_variable_values_sorted
from Orange.widgets.utils.itemmodels import DomainModel
from Orange.widgets.utils.widgetpreview import WidgetPreview
from Orange.widgets.visualize.owscatterplotgraph import OWScatterPlotBase, \
Expand Down Expand Up @@ -85,7 +85,7 @@ def score_heuristic(self):
assert self.attr_color is not None
master_domain = self.master.data.domain
vars = [v for v in chain(master_domain.variables, master_domain.metas)
if v is not self.attr_color and v.is_continuous]
if v is not self.attr_color and v.is_primitive()]
domain = Domain(attributes=vars, class_vars=self.attr_color)
data = self.master.data.transform(domain)
relief = ReliefF if isinstance(domain.class_var, DiscreteVariable) \
Expand Down Expand Up @@ -117,6 +117,7 @@ def axis_items(self):
class OWScatterPlotGraph(OWScatterPlotBase):
show_reg_line = Setting(False)
orthonormal_regression = Setting(False)
jitter_continuous = Setting(False)

def __init__(self, scatter_widget, parent):
super().__init__(scatter_widget, parent)
Expand All @@ -137,12 +138,43 @@ def update_colors(self):
super().update_colors()
self.update_regression_line()

def _get_jittering_tooltip(self):
def is_discrete(attr):
return attr and attr.is_discrete

if self.jitter_continuous or is_discrete(self.master.attr_x) or \
is_discrete(self.master.attr_y):
return super()._get_jittering_tooltip()
return ""

def jitter_coordinates(self, x, y):
def get_span(attr):
if attr.is_discrete:
# Assuming the maximal jitter size is 10, a span of 4 will
# jitter by 4 * 10 / 100 = 0.4, so there will be no overlap
return 4
elif self.jitter_continuous:
return None # Let _jitter_data determine the span
else:
return 0 # No jittering
span_x = get_span(self.master.attr_x)
span_y = get_span(self.master.attr_y)
if self.jitter_size == 0 or (span_x == 0 and span_y == 0):
return x, y
return self._jitter_data(x, y, span_x, span_y)

def update_axes(self):
for axis, title in self.master.get_axes().items():
use_time = title is not None and title.is_time
for axis, var in self.master.get_axes().items():
axis_item = self.plot_widget.plotItem.getAxis(axis)
if var and var.is_discrete:
ticks = [list(enumerate(get_variable_values_sorted(var)))]
axis_item.setTicks(ticks)
else:
axis_item.setTicks(None)
use_time = var and var.is_time
self.plot_widget.plotItem.getAxis(axis).use_time(use_time)
self.plot_widget.setLabel(axis=axis, text=title or "")
if title is None:
self.plot_widget.setLabel(axis=axis, text=var or "")
if not var:
self.plot_widget.hideAxis(axis)

@staticmethod
Expand Down Expand Up @@ -203,7 +235,8 @@ def update_regression_line(self):
for line in self.reg_line_items:
self.plot_widget.removeItem(line)
self.reg_line_items.clear()
if not self.show_reg_line:
if not (self.show_reg_line
and self.master.can_draw_regresssion_line()):
return
x, y = self.master.get_coordinates_data()
if x is None:
Expand Down Expand Up @@ -254,7 +287,6 @@ class Warning(OWDataProjectionWidget.Warning):
missing_coords = Msg(
"Plot cannot be displayed because '{}' or '{}' "
"is missing for all data points.")
no_continuous_vars = Msg("Data has no numeric variables.")

class Information(OWDataProjectionWidget.Information):
sampled_sql = Msg("Large SQL table; showing a sample.")
Expand All @@ -277,6 +309,7 @@ def _add_controls(self):
self._add_controls_axis()
self._add_controls_sampling()
super()._add_controls()
self.gui.add_widget(self.gui.JitterNumericValues, self._effects_box)
self.gui.add_widgets(
[self.gui.ShowGridLines,
self.gui.ToolTipShowsAll,
Expand All @@ -300,7 +333,7 @@ def _add_controls_axis(self):
self.attr_box = gui.vBox(self.controlArea, 'Axes',
spacing=2 if gui.is_macstyle() else 8)
dmod = DomainModel
self.xy_model = DomainModel(dmod.MIXED, valid_types=ContinuousVariable)
self.xy_model = DomainModel(dmod.MIXED, valid_types=dmod.PRIMITIVE)
self.cb_attr_x = gui.comboBox(
self.attr_box, self, "attr_x", label="Axis x:",
callback=self.set_attr_from_combo,
Expand Down Expand Up @@ -393,11 +426,6 @@ def check_data(self):
if self.auto_sample:
self.__timer.start()

if self.data is not None:
if not self.data.domain.has_continuous_attributes(True, True):
self.Warning.no_continuous_vars()
self.data = None

if self.data is not None and (len(self.data) == 0 or
len(self.data.domain.variables) == 0):
self.data = None
Expand Down Expand Up @@ -433,6 +461,12 @@ def _point_tooltip(self, point_id, skip_attrs=()):
text = "<b>{}</b><br/><br/>{}".format(text, others)
return text

def can_draw_regresssion_line(self):
return self.data is not None and \
self.data.domain is not None and \
self.attr_x.is_continuous and \
self.attr_y.is_continuous

def add_data(self, time=0.4):
if self.data and len(self.data) > 2000:
self.__timer.stop()
Expand Down Expand Up @@ -484,6 +518,7 @@ def handleNewSignals(self):
if self._domain_invalidated:
self.graph.update_axes()
self._domain_invalidated = False
self.cb_reg_line.setEnabled(self.can_draw_regresssion_line())

@Inputs.features
def set_shown_attributes(self, attributes):
Expand All @@ -505,6 +540,7 @@ def set_attr_from_combo(self):
self.xy_changed_manually.emit(self.attr_x, self.attr_y)

def attr_changed(self):
self.cb_reg_line.setEnabled(self.can_draw_regresssion_line())
self.setup_plot()
self.commit()

Expand All @@ -528,6 +564,17 @@ def get_widget_name_extension(self):
return "{} vs {}".format(self.attr_x.name, self.attr_y.name)
return None

def _get_send_report_caption(self):
return report.render_items_vert((
("Color", self._get_caption_var_name(self.attr_color)),
("Label", self._get_caption_var_name(self.attr_label)),
("Shape", self._get_caption_var_name(self.attr_shape)),
("Size", self._get_caption_var_name(self.attr_size)),
("Jittering", (self.attr_x.is_discrete or
self.attr_y.is_discrete or
self.graph.jitter_continuous) and
self.graph.jitter_size)))

@classmethod
def migrate_settings(cls, settings, version):
if version < 2 and "selection" in settings and settings["selection"]:
Expand Down
Loading

0 comments on commit a2b32db

Please sign in to comment.