class OWLearningCurveB(OWBaseWidget): name = "Learning Curve (B)" description = ("Takes a dataset and a set of learners and shows a " "learning curve in a table") icon = "icons/LearningCurve.svg" priority = 1010 # [start-snippet-1] class Inputs: data = Input("Data", Table, default=True) test_data = Input("Test Data", Table) learner = MultiInput("Learner", Learner) # [end-snippet-1] #: cross validation folds folds = settings.Setting(5) #: points in the learning curve steps = settings.Setting(10) #: index of the selected scoring function scoringF = settings.Setting(0) #: compute curve on any change of parameters commitOnChange = settings.Setting(True) def __init__(self): super().__init__() # sets self.curvePoints, self.steps equidistant points from # 1/self.steps to 1 self.updateCurvePoints() self.scoring = [("Classification Accuracy", Orange.evaluation.scoring.CA), ("AUC", Orange.evaluation.scoring.AUC), ("Precision", Orange.evaluation.scoring.Precision), ("Recall", Orange.evaluation.scoring.Recall)] #: Input data on which to construct the learning curve self.data = None #: Optional test data self.testdata = None #: LearnerData for each learner input self.learners: List[LearnerData] = [] # GUI box = gui.widgetBox(self.controlArea, "Info") self.infoa = gui.widgetLabel(box, 'No data on input.') self.infob = gui.widgetLabel(box, 'No learners.') gui.separator(self.controlArea) box = gui.widgetBox(self.controlArea, "Evaluation Scores") gui.comboBox(box, self, "scoringF", items=[x[0] for x in self.scoring], callback=self._invalidate_curves) gui.separator(self.controlArea) box = gui.widgetBox(self.controlArea, "Options") gui.spin(box, self, 'folds', 2, 100, step=1, label='Cross validation folds: ', keyboardTracking=False, callback=lambda: self._invalidate_results() if self.commitOnChange else None) gui.spin(box, self, 'steps', 2, 100, step=1, label='Learning curve points: ', keyboardTracking=False, callback=[ self.updateCurvePoints, lambda: self._invalidate_results() if self.commitOnChange else None ]) gui.checkBox(box, self, 'commitOnChange', 'Apply setting on any change') self.commitBtn = gui.button(box, self, "Apply Setting", callback=self._invalidate_results, disabled=True) gui.rubber(self.controlArea) # table widget self.table = gui.table(self.mainArea, selectionMode=QTableWidget.NoSelection) ########################################################################## # slots: handle input signals @Inputs.data def set_dataset(self, data): """Set the input train dataset.""" # Clear all results/scores for all learner inputs for item in self.learners: item.results = None item.curve = None self.data = data if data is not None: self.infoa.setText('%d instances in input dataset' % len(data)) else: self.infoa.setText('No data on input.') self.commitBtn.setEnabled(self.data is not None) @Inputs.test_data def set_testdataset(self, testdata): """Set a separate test dataset.""" # Clear all results/scores for all learner inputs for item in self.learners: item.results = None item.curve = None self.testdata = testdata @Inputs.learner def set_learner(self, index: int, learner): """Set the input learner at index""" # update/replace a learner on a previously connected link item = self.learners[index] item.learner = learner item.results = None item.curve = None @Inputs.learner.insert def insert_learner(self, index, learner): """Insert a learner at index""" self.learners.insert(index, LearnerData(learner, None, None)) @Inputs.learner.remove def remove_learner(self, index): """"Remove a learner at index""" # remove a learner and corresponding results del self.learners[index] def handleNewSignals(self): if len(self.learners): self.infob.setText("%d learners on input." % len(self.learners)) else: self.infob.setText("No learners.") self.commitBtn.setEnabled(len(self.learners)) if self.data is not None: self._update() self._update_curve_points() self._update_table() def _invalidate_curves(self): if self.data is not None: self._update_curve_points() self._update_table() def _invalidate_results(self): for item in self.learners: item.results = None item.curve = None if self.data is not None: self._update() self._update_curve_points() self._update_table() def _update(self): assert self.data is not None # collect all learners for which results have not yet been computed need_update = [(i, item) for (i, item) in enumerate(self.learners) if item.results is None] if not need_update: return learners = [item.learner for _, item in need_update] if self.testdata is None: # compute the learning curve result for all learners in one go results = learning_curve( learners, self.data, folds=self.folds, proportions=self.curvePoints, ) else: results = learning_curve_with_test_data( learners, self.data, self.testdata, times=self.folds, proportions=self.curvePoints, ) # split the combined result into per learner/model results results = [ list(Results.split_by_model(p_results)) for p_results in results ] for i, (_, item) in enumerate(need_update): item.results = [p_results[i] for p_results in results] def _update_curve_points(self): scoref = self.scoring[self.scoringF][1] for item in self.learners: item.curve = [scoref(x)[0] for x in item.results] def _update_table(self): self.table.setRowCount(0) self.table.setRowCount(len(self.curvePoints)) self.table.setColumnCount(len(self.learners)) self.table.setHorizontalHeaderLabels( [item.learner.name for item in self.learners]) self.table.setVerticalHeaderLabels( ["{:.2f}".format(p) for p in self.curvePoints]) if self.data is None: return for column, item in enumerate(self.learners): for row, point in enumerate(item.curve): self.table.setItem(row, column, QTableWidgetItem("{:.5f}".format(point))) for i in range(len(self.learners)): sh = self.table.sizeHintForColumn(i) cwidth = self.table.columnWidth(i) self.table.setColumnWidth(i, max(sh, cwidth)) def updateCurvePoints(self): self.curvePoints = [(x + 1.) / self.steps for x in range(self.steps)]
class OWLearningCurveC(OWBaseWidget): name = "Learning Curve (C)" description = ("Takes a dataset and a set of learners and shows a " "learning curve in a table") icon = "icons/LearningCurve.svg" priority = 1010 class Inputs: data = Input("Data", Table, default=True) test_data = Input("Test Data", Table) learner = MultiInput("Learner", Learner) #: cross validation folds folds = settings.Setting(5) #: points in the learning curve steps = settings.Setting(10) #: index of the selected scoring function scoringF = settings.Setting(0) #: compute curve on any change of parameters commitOnChange = settings.Setting(True) def __init__(self): super().__init__() # sets self.curvePoints, self.steps equidistant points from # 1/self.steps to 1 self.updateCurvePoints() self.scoring = [("Classification Accuracy", Orange.evaluation.scoring.CA), ("AUC", Orange.evaluation.scoring.AUC), ("Precision", Orange.evaluation.scoring.Precision), ("Recall", Orange.evaluation.scoring.Recall)] #: Input data on which to construct the learning curve self.data = None #: Optional test data self.testdata = None #: LearnerData for each learner input self.learners: List[LearnerData] = [] # [start-snippet-3] #: The current evaluating task (if any) self._task = None # type: Optional[Task] #: An executor we use to submit learner evaluations into a thread pool self._executor = concurrent.futures.ThreadPoolExecutor() # [end-snippet-3] # GUI box = gui.widgetBox(self.controlArea, "Info") self.infoa = gui.widgetLabel(box, 'No data on input.') self.infob = gui.widgetLabel(box, 'No learners.') gui.separator(self.controlArea) box = gui.widgetBox(self.controlArea, "Evaluation Scores") gui.comboBox(box, self, "scoringF", items=[x[0] for x in self.scoring], callback=self._invalidate_curves) gui.separator(self.controlArea) box = gui.widgetBox(self.controlArea, "Options") gui.spin(box, self, 'folds', 2, 100, step=1, label='Cross validation folds: ', keyboardTracking=False, callback=lambda: self._invalidate_results() if self.commitOnChange else None) gui.spin(box, self, 'steps', 2, 100, step=1, label='Learning curve points: ', keyboardTracking=False, callback=[ self.updateCurvePoints, lambda: self._invalidate_results() if self.commitOnChange else None ]) gui.checkBox(box, self, 'commitOnChange', 'Apply setting on any change') self.commitBtn = gui.button(box, self, "Apply Setting", callback=self._invalidate_results, disabled=True) gui.rubber(self.controlArea) # table widget self.table = gui.table(self.mainArea, selectionMode=QTableWidget.NoSelection) ########################################################################## # slots: handle input signals @Inputs.data def set_dataset(self, data): """Set the input train dataset.""" # Clear all results/scores for item in self.learners: item.results = None item.curve = None self.data = data if data is not None: self.infoa.setText('%d instances in input dataset' % len(data)) else: self.infoa.setText('No data on input.') self.commitBtn.setEnabled(self.data is not None) @Inputs.test_data def set_testdataset(self, testdata): """Set a separate test dataset.""" # Clear all results/scores for all learner inputs for item in self.learners: item.results = None item.curve = None self.testdata = testdata @Inputs.learner def set_learner(self, index: int, learner): """Set the input learner at index""" # update/replace a learner on a previously connected link item = self.learners[index] item.learner = learner item.results = None item.curve = None @Inputs.learner.insert def insert_learner(self, index, learner): """Insert a learner at index""" self.learners.insert(index, LearnerData(learner, None, None)) @Inputs.learner.remove def remove_learner(self, index): """"Remove a learner at index""" # remove a learner and corresponding results del self.learners[index] # [start-snippet-4] def handleNewSignals(self): if len(self.learners): self.infob.setText("%d learners on input." % len(self.learners)) else: self.infob.setText("No learners.") self.commitBtn.setEnabled(len(self.learners)) self._update() # [end-snippet-4] def _invalidate_curves(self): if self.data is not None: self._update_curve_points() self._update_table() def _invalidate_results(self): for item in self.learners: item.results = None item.curve = None self._update() # [start-snippet-5] def _update(self): if self._task is not None: # First make sure any pending tasks are cancelled. self.cancel() assert self._task is None if self.data is None: return # collect all learners for which results have not yet been computed need_update = [(i, item) for (i, item) in enumerate(self.learners) if item.results is None] if not need_update: self._update_curve_points() self._update_table() return # [end-snippet-5] # [start-snippet-6] learners = [item.learner for _, item in need_update] # setup the learner evaluations as partial function capturing # the necessary arguments. if self.testdata is None: learning_curve_func = partial( learning_curve, learners, self.data, folds=self.folds, proportions=self.curvePoints, ) else: learning_curve_func = partial( learning_curve_with_test_data, learners, self.data, self.testdata, times=self.folds, proportions=self.curvePoints, ) # [end-snippet-6] # [start-snippet-7] # setup the task state self._task = task = Task() # The learning_curve[_with_test_data] also takes a callback function # to report the progress. We instrument this callback to both invoke # the appropriate slots on this widget for reporting the progress # (in a thread safe manner) and to implement cooperative cancellation. set_progress = methodinvoke(self, "setProgressValue", (float, )) def callback(finished): # check if the task has been cancelled and raise an exception # from within. This 'strategy' can only be used with code that # properly cleans up after itself in the case of an exception # (does not leave any global locks, opened file descriptors, ...) if task.cancelled: raise KeyboardInterrupt() set_progress(finished * 100) # capture the callback in the partial function learning_curve_func = partial(learning_curve_func, callback=callback) # [end-snippet-7] # [start-snippet-8] self.progressBarInit() # Submit the evaluation function to the executor and fill in the # task with the resultant Future. task.future = self._executor.submit(learning_curve_func) # Setup the FutureWatcher to notify us of completion task.watcher = FutureWatcher(task.future) # by using FutureWatcher we ensure `_task_finished` slot will be # called from the main GUI thread by the Qt's event loop task.watcher.done.connect(self._task_finished) # [end-snippet-8] # [start-snippet-progress] @pyqtSlot(float) def setProgressValue(self, value): assert self.thread() is QThread.currentThread() self.progressBarSet(value) # [end-snippet-progress] # [start-snippet-9] @pyqtSlot(concurrent.futures.Future) def _task_finished(self, f): """ Parameters ---------- f : Future The future instance holding the result of learner evaluation. """ assert self.thread() is QThread.currentThread() assert self._task is not None assert self._task.future is f assert f.done() self._task = None self.progressBarFinished() try: results = f.result() # type: List[Results] except Exception as ex: # Log the exception with a traceback log = logging.getLogger() log.exception(__name__, exc_info=True) self.error("Exception occurred during evaluation: {!r}".format(ex)) # clear all results for item in self.learners: item.results = None else: # split the combined result into per learner/model results ... results = [ list(Results.split_by_model(p_results)) for p_results in results ] # type: List[List[Results]] assert all(len(r.learners) == 1 for r1 in results for r in r1) assert len(results) == len(self.curvePoints) learners = [r.learners[0] for r in results[0]] # map learner back to LearnerData instance data_by_learner = {item.learner: item for item in self.learners} # ... and update self.results for i, learner in enumerate(learners): item = data_by_learner[learner] item.results = [p_results[i] for p_results in results] # update the display self._update_curve_points() self._update_table() # [end-snippet-9] # [start-snippet-10] def cancel(self): """ Cancel the current task (if any). """ if self._task is not None: self._task.cancel() assert self._task.future.done() # disconnect the `_task_finished` slot self._task.watcher.done.disconnect(self._task_finished) self._task = None self.progressBarFinished() # [end-snippet-10] # [start-snippet-11] def onDeleteWidget(self): self.cancel() super().onDeleteWidget() # [end-snippet-11] def _update_curve_points(self): scoref = self.scoring[self.scoringF][1] for item in self.learners: item.curve = [scoref(x)[0] for x in item.results] def _update_table(self): self.table.setRowCount(0) self.table.setRowCount(len(self.curvePoints)) self.table.setColumnCount(len(self.learners)) self.table.setHorizontalHeaderLabels( [item.learner.name for item in self.learners]) self.table.setVerticalHeaderLabels( ["{:.2f}".format(p) for p in self.curvePoints]) if self.data is None: return for column, item in enumerate(self.learners): for row, point in enumerate(item.curve): self.table.setItem(row, column, QTableWidgetItem("{:.5f}".format(point))) for i in range(len(self.learners)): sh = self.table.sizeHintForColumn(i) cwidth = self.table.columnWidth(i) self.table.setColumnWidth(i, max(sh, cwidth)) def updateCurvePoints(self): self.curvePoints = [(x + 1.) / self.steps for x in range(self.steps)]
class OWNxEmbedding(OWWidget): name = "Network Embeddings" description = "Embed network elements" icon = "icons/NetworkEmbedding.svg" priority = 6450 class Inputs: network = Input("Network", Network, default=True) class Outputs: items = Output("Items", Table) resizing_enabled = False want_main_area = False p = settings.Setting(1.0) q = settings.Setting(1.0) walk_len = settings.Setting(50) num_walks = settings.Setting(10) emb_size = settings.Setting(128) window_size = settings.Setting(5) num_epochs = settings.Setting(1) auto_commit = settings.Setting(True) def __init__(self): super().__init__() self.network = None self.embedder = None self._worker_thread = None self._progress_updater = None def commit(): return self.commit() box = gui.widgetBox(self.controlArea, box=True) kwargs = dict(controlWidth=75, alignment=Qt.AlignRight, callback=commit) gui.spin(box, self, "p", 0.0, 10.0, 0.1, label="Return parameter (p): ", spinType=float, **kwargs) gui.spin(box, self, "q", 0.0, 10.0, 0.1, label="In-out parameter (q): ", spinType=float, **kwargs) gui.spin(box, self, "walk_len", 1, 100_000, 1, label="Walk length: ", **kwargs) gui.spin(box, self, "num_walks", 1, 10_000, 1, label="Walks per node: ", **kwargs) gui.spin(box, self, "emb_size", 1, 10_000, 1, label="Embedding size: ", **kwargs) gui.spin(box, self, "window_size", 1, 20, 1, label="Context size: ", **kwargs) gui.spin(box, self, "num_epochs", 1, 100, 1, label="Number of epochs: ", **kwargs) gui.auto_commit(self.controlArea, self, "auto_commit", "Commit", checkbox_label="Auto-commit", orientation=Qt.Horizontal) commit() @Inputs.network def set_network(self, net): self.network = net self.commit() def commit(self): self.Warning.clear() # cancel existing computation if running if self._worker_thread is not None: self._worker_thread.finished.disconnect() self._worker_thread.quit() self._worker_thread = None if self.network is None: self.Outputs.items.send(None) return self._progress_updater = ProgressBarUpdater(self, self.num_epochs) self.embedder = embeddings.Node2Vec(self.p, self.q, self.walk_len, self.num_walks, self.emb_size, self.window_size, self.num_epochs, callbacks=[self._progress_updater]) self._worker_thread = EmbedderThread( lambda: self.embedder(self.network)) self._worker_thread.finished.connect(self.on_finished) self.progressBarInit() self.progressBarSet(1e-5) self._worker_thread.start() def on_finished(self): output = self._worker_thread.result self._worker_thread = None self._progress_updater = None self.progressBarFinished() self.Outputs.items.send(output) def onDeleteWidget(self): if self._worker_thread is not None: # prevent the callback from trying to access deleted widget object self._progress_updater.widget = None self._worker_thread.finished.disconnect() self._worker_thread.quit() super().onDeleteWidget()
class OWLearningCurveB(OWBaseWidget): name = "Learning Curve (B)" description = ("Takes a dataset and a set of learners and shows a " "learning curve in a table") icon = "icons/LearningCurve.svg" priority = 1010 # [start-snippet-1] class Inputs: data = Input("Data", Orange.data.Table, default=True) test_data = Input("Test Data", Orange.data.Table) learner = Input("Learner", Orange.classification.Learner, multiple=True) # [end-snippet-1] #: cross validation folds folds = settings.Setting(5) #: points in the learning curve steps = settings.Setting(10) #: index of the selected scoring function scoringF = settings.Setting(0) #: compute curve on any change of parameters commitOnChange = settings.Setting(True) def __init__(self): super().__init__() # sets self.curvePoints, self.steps equidistant points from # 1/self.steps to 1 self.updateCurvePoints() self.scoring = [("Classification Accuracy", Orange.evaluation.scoring.CA), ("AUC", Orange.evaluation.scoring.AUC), ("Precision", Orange.evaluation.scoring.Precision), ("Recall", Orange.evaluation.scoring.Recall)] #: input data on which to construct the learning curve self.data = None #: optional test data self.testdata = None #: A {input_id: Learner} mapping of current learners from input channel self.learners = OrderedDict() #: A {input_id: List[Results]} mapping of input id to evaluation #: results list, one for each curve point self.results = OrderedDict() #: A {input_id: List[float]} mapping of input id to learning curve #: point scores self.curves = OrderedDict() # GUI box = gui.widgetBox(self.controlArea, "Info") self.infoa = gui.widgetLabel(box, 'No data on input.') self.infob = gui.widgetLabel(box, 'No learners.') gui.separator(self.controlArea) box = gui.widgetBox(self.controlArea, "Evaluation Scores") gui.comboBox(box, self, "scoringF", items=[x[0] for x in self.scoring], callback=self._invalidate_curves) gui.separator(self.controlArea) box = gui.widgetBox(self.controlArea, "Options") gui.spin(box, self, 'folds', 2, 100, step=1, label='Cross validation folds: ', keyboardTracking=False, callback=lambda: self._invalidate_results() if self.commitOnChange else None) gui.spin(box, self, 'steps', 2, 100, step=1, label='Learning curve points: ', keyboardTracking=False, callback=[ self.updateCurvePoints, lambda: self._invalidate_results() if self.commitOnChange else None ]) gui.checkBox(box, self, 'commitOnChange', 'Apply setting on any change') self.commitBtn = gui.button(box, self, "Apply Setting", callback=self._invalidate_results, disabled=True) gui.rubber(self.controlArea) # table widget self.table = gui.table(self.mainArea, selectionMode=QTableWidget.NoSelection) ########################################################################## # slots: handle input signals @Inputs.data def set_dataset(self, data): """Set the input train dataset.""" # Clear all results/scores for id in list(self.results): self.results[id] = None for id in list(self.curves): self.curves[id] = None self.data = data if data is not None: self.infoa.setText('%d instances in input dataset' % len(data)) else: self.infoa.setText('No data on input.') self.commitBtn.setEnabled(self.data is not None) @Inputs.test_data def set_testdataset(self, testdata): """Set a separate test dataset.""" # Clear all results/scores for id in list(self.results): self.results[id] = None for id in list(self.curves): self.curves[id] = None self.testdata = testdata @Inputs.learner def set_learner(self, learner, id): """Set the input learner for channel id.""" if id in self.learners: if learner is None: # remove a learner and corresponding results del self.learners[id] del self.results[id] del self.curves[id] else: # update/replace a learner on a previously connected link self.learners[id] = learner # invalidate the cross-validation results and curve scores # (will be computed/updated in `_update`) self.results[id] = None self.curves[id] = None else: if learner is not None: self.learners[id] = learner # initialize the cross-validation results and curve scores # (will be computed/updated in `_update`) self.results[id] = None self.curves[id] = None if len(self.learners): self.infob.setText("%d learners on input." % len(self.learners)) else: self.infob.setText("No learners.") self.commitBtn.setEnabled(len(self.learners)) def handleNewSignals(self): if self.data is not None: self._update() self._update_curve_points() self._update_table() def _invalidate_curves(self): if self.data is not None: self._update_curve_points() self._update_table() def _invalidate_results(self): for id in self.learners: self.curves[id] = None self.results[id] = None if self.data is not None: self._update() self._update_curve_points() self._update_table() def _update(self): assert self.data is not None # collect all learners for which results have not yet been computed need_update = [(id, learner) for id, learner in self.learners.items() if self.results[id] is None] if not need_update: return learners = [learner for _, learner in need_update] if self.testdata is None: # compute the learning curve result for all learners in one go results = learning_curve( learners, self.data, folds=self.folds, proportions=self.curvePoints, ) else: results = learning_curve_with_test_data( learners, self.data, self.testdata, times=self.folds, proportions=self.curvePoints, ) # split the combined result into per learner/model results results = [ list(Results.split_by_model(p_results)) for p_results in results ] for i, (id, learner) in enumerate(need_update): self.results[id] = [p_results[i] for p_results in results] def _update_curve_points(self): for id in self.learners: curve = [ self.scoring[self.scoringF][1](x)[0] for x in self.results[id] ] self.curves[id] = curve def _update_table(self): self.table.setRowCount(0) self.table.setRowCount(len(self.curvePoints)) self.table.setColumnCount(len(self.learners)) self.table.setHorizontalHeaderLabels( [learner.name for _, learner in self.learners.items()]) self.table.setVerticalHeaderLabels( ["{:.2f}".format(p) for p in self.curvePoints]) if self.data is None: return for column, curve in enumerate(self.curves.values()): for row, point in enumerate(curve): self.table.setItem(row, column, QTableWidgetItem("{:.5f}".format(point))) for i in range(len(self.learners)): sh = self.table.sizeHintForColumn(i) cwidth = self.table.columnWidth(i) self.table.setColumnWidth(i, max(sh, cwidth)) def updateCurvePoints(self): self.curvePoints = [(x + 1.) / self.steps for x in range(self.steps)] # [start-snippet-3] def test_run_signals(self): data = Orange.data.Table("iris") indices = numpy.random.permutation(len(data)) traindata = data[indices[:-20]] testdata = data[indices[-20:]] self.set_dataset(traindata) self.set_testdataset(testdata) l1 = Orange.classification.NaiveBayesLearner() l1.name = 'Naive Bayes' self.set_learner(l1, 1) l2 = Orange.classification.LogisticRegressionLearner() l2.name = 'Logistic Regression' self.set_learner(l2, 2) l4 = Orange.classification.SklTreeLearner() l4.name = "Decision Tree" self.set_learner(l4, 3)