Skip to content

Commit

Permalink
Merge pull request #4749 from aturanjanin/owpredictions
Browse files Browse the repository at this point in the history
Predictions: data info displayed in the status bar
  • Loading branch information
PrimozGodec committed Sep 24, 2020
2 parents a385a0e + c3cdcdc commit aac22ce
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 17 deletions.
44 changes: 27 additions & 17 deletions Orange/widgets/evaluate/owpredictions.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

from Orange.widgets.utils.colorpalettes import LimitedDiscretePalette
from orangewidget.report import plural
from Orange.widgets.utils.state_summary import format_summary_details

import Orange
from Orange.evaluation import Results
Expand All @@ -32,6 +31,7 @@
from Orange.widgets.widget import OWWidget, Msg, Input, Output
from Orange.widgets.utils.itemmodels import TableModel
from Orange.widgets.utils.sql import check_sql_input
from Orange.widgets.utils.state_summary import format_summary_details


# Input slot for the Predictors channel
Expand Down Expand Up @@ -85,6 +85,9 @@ def __init__(self):
self.selection_store = None
self.__pending_selection = self.selection

self._set_input_summary()
self._set_output_summary(None)

gui.listBox(self.controlArea, self, "selected_classes", "class_values",
box="Show probabibilities for",
callback=self._update_prediction_delegate,
Expand Down Expand Up @@ -220,7 +223,7 @@ def handleNewSignals(self):
self._update_predictions_model()
self._update_prediction_delegate()
self._set_errors()
self._update_info()
self._set_input_summary()
self.commit()

def _call_predictors(self):
Expand Down Expand Up @@ -326,30 +329,40 @@ def _set_errors(self):
else:
self.Warning.wrong_targets.clear()

def _update_info(self):
def _set_input_summary(self):
if not self.data and not self.predictors:
self.info.set_input_summary(self.info.NoInput)
return

summary = str(len(self.data)) if self.data else "0"
summary = len(self.data) if self.data else 0
details = self._get_details()
self.info.set_input_summary(summary, details)
self.info.set_input_summary(summary, details, format=Qt.RichText)

def _get_details(self):
details = "Data:<br>"
details += format_summary_details(self.data).replace('\n', '<br>') if \
self.data else "No data on input."
details += "<hr>"
pred_names = [v.name for v in self.predictors.values()]
n_predictors = len(self.predictors)
if self.data:
details = plural("{number} instance{s}", len(self.data))
else:
details = "No data"
if n_predictors:
n_valid = len(self._non_errored_predictors())
details += plural("\n{number} model{s}", n_predictors)
details += plural("Model: {number} model{s}", n_predictors)
if n_valid != n_predictors:
details += plural(" ({number} failed)", n_predictors - n_valid)
details += f" ({n_predictors - n_valid} failed)"
details += "<ul>"
for name in pred_names:
details += f"<li>{name}</li>"
details += "</ul>"
else:
details += "\nNo models"
details += "Model:<br>No model on input."
return details

def _set_output_summary(self, output):
summary = len(output) if output else self.info.NoOutput
details = format_summary_details(output) if output else ""
self.info.set_output_summary(summary, details)

def _invalidate_predictions(self):
for inputid, pred in list(self.predictors.items()):
self.predictors[inputid] = pred._replace(results=None)
Expand Down Expand Up @@ -572,8 +585,8 @@ def _commit_evaluation_results(self):

def _commit_predictions(self):
if not self.data:
self._set_output_summary(None)
self.Outputs.predictions.send(None)
self.info.set_output_summary(self.info.NoOutput)
return

newmetas = []
Expand Down Expand Up @@ -618,10 +631,7 @@ def _commit_predictions(self):
source_rows = [map_to(index(row, 0)).row() for row in rows]
predictions = predictions[source_rows]
self.Outputs.predictions.send(predictions)

summary = str(len(predictions))
details = format_summary_details(predictions)
self.info.set_output_summary(summary, details)
self._set_output_summary(predictions)

@staticmethod
def _add_classification_out_columns(slot, newmetas, newcolumns):
Expand Down
59 changes: 59 additions & 0 deletions Orange/widgets/evaluate/tests/test_owpredictions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Tests for OWPredictions"""
# pylint: disable=protected-access
import io
import unittest
from unittest.mock import Mock
Expand All @@ -25,6 +26,7 @@
from Orange.widgets.tests.utils import excepthook_catch, \
possible_duplicate_table
from Orange.widgets.utils.colorpalettes import LimitedDiscretePalette
from Orange.widgets.utils.state_summary import format_summary_details


class TestOWPredictions(WidgetTest):
Expand Down Expand Up @@ -476,6 +478,63 @@ def test_unregister_prediction_model(self):
self.send_signal(self.widget.Inputs.predictors, log_reg_iris)
self.widget.selection_store.unregister.called_with(prev_model)

def test_summary(self):
"""Check if the status bar updates when data is recieved"""
data = self.iris
info = self.widget.info
no_input, no_output = "No data on input", "No data on output"
predictor1 = ConstantLearner()(self.iris)
predictor2 = LogisticRegressionLearner()(self.iris)

self.send_signal(self.widget.Inputs.predictors, predictor1)
details = f"Data:<br>{no_input}.<hr>" + \
"Model: 1 model (1 failed)<ul><li>constant</li></ul>"
self.assertEqual(info._StateInfo__input_summary.brief, "0")
self.assertEqual(info._StateInfo__input_summary.details, details)
self.assertEqual(info._StateInfo__output_summary.brief, "")
self.assertEqual(info._StateInfo__output_summary.details, no_output)

self.send_signal(self.widget.Inputs.data, data)
details = "Data:<br>" + \
format_summary_details(data).replace('\n', '<br>') + \
"<hr>Model: 1 model<ul><li>constant</li></ul>"
self.assertEqual(info._StateInfo__input_summary.brief, "150")
self.assertEqual(info._StateInfo__input_summary.details, details)
output = self.get_output(self.widget.Outputs.predictions)
summary, details = f"{len(output)}", format_summary_details(output)
self.assertEqual(info._StateInfo__output_summary.brief, summary)
self.assertEqual(info._StateInfo__output_summary.details, details)

self.send_signal(self.widget.Inputs.predictors, predictor2, 1)
details = "Data:<br>"+ \
format_summary_details(data).replace('\n', '<br>') + \
"<hr>Model: 2 models<ul><li>constant</li>" + \
"<li>logistic regression</li></ul>"
self.assertEqual(info._StateInfo__input_summary.brief, "150")
self.assertEqual(info._StateInfo__input_summary.details, details)
output = self.get_output(self.widget.Outputs.predictions)
summary, details = f"{len(output)}", format_summary_details(output)
self.assertEqual(info._StateInfo__output_summary.brief, summary)
self.assertEqual(info._StateInfo__output_summary.details, details)

self.send_signal(self.widget.Inputs.predictors, None)
self.send_signal(self.widget.Inputs.predictors, None, 1)
details = "Data:<br>" + \
format_summary_details(data).replace('\n', '<br>') + \
"<hr>Model:<br>No model on input."
self.assertEqual(info._StateInfo__input_summary.brief, "150")
self.assertEqual(info._StateInfo__input_summary.details, details)
output = self.get_output(self.widget.Outputs.predictions)
summary, details = f"{len(output)}", format_summary_details(output)
self.assertEqual(info._StateInfo__output_summary.brief, summary)
self.assertEqual(info._StateInfo__output_summary.details, details)

self.send_signal(self.widget.Inputs.data, None)
self.assertEqual(info._StateInfo__input_summary.brief, "")
self.assertEqual(info._StateInfo__input_summary.details, no_input)
self.assertEqual(info._StateInfo__output_summary.brief, "")
self.assertEqual(info._StateInfo__output_summary.details, no_output)


class SelectionModelTest(unittest.TestCase):
def setUp(self):
Expand Down

0 comments on commit aac22ce

Please sign in to comment.