{
    "file_diffs": [
        {
            "old_file_content": "from contextlib import contextmanager\nfrom functools import partial\nfrom operator import itemgetter\nfrom itertools import chain\nfrom typing import Set, Sequence, Union, Optional, List, NamedTuple\n\nimport numpy\nfrom AnyQt.QtWidgets import (\n    QTableView, QListWidget, QSplitter, QToolTip, QStyle, QApplication,\n    QSizePolicy\n)\nfrom AnyQt.QtGui import QPainter, QStandardItem, QPen, QColor\nfrom AnyQt.QtCore import (\n    Qt, QSize, QRect, QRectF, QPoint, QLocale,\n    QModelIndex, pyqtSignal, QTimer,\n    QItemSelectionModel, QItemSelection)\n\nfrom orangewidget.report import plural\nfrom orangewidget.utils.itemmodels import AbstractSortTableModel\n\nimport Orange\nfrom Orange.evaluation import Results\nfrom Orange.base import Model\nfrom Orange.data import ContinuousVariable, DiscreteVariable, Domain\nfrom Orange.data.table import DomainTransformationError\nfrom Orange.data.util import get_unique_names\nfrom Orange.widgets import gui, settings\nfrom Orange.widgets.evaluate.utils import (\n    ScoreTable, usable_scorers, learner_name, scorer_caller)\nfrom Orange.widgets.utils.widgetpreview import WidgetPreview\nfrom Orange.widgets.widget import OWWidget, Msg, Input, Output, MultiInput\nfrom Orange.widgets.utils.itemmodels import TableModel\nfrom Orange.widgets.utils.sql import check_sql_input\nfrom Orange.widgets.utils.state_summary import format_summary_details\nfrom Orange.widgets.utils.colorpalettes import LimitedDiscretePalette\nfrom Orange.widgets.utils.itemdelegates import TableDataDelegate\n\n# Input slot for the Predictors channel\nPredictorSlot = NamedTuple(\n    \"PredictorSlot\", [\n        (\"predictor\", Model),  # The `Model` instance\n        (\"name\", str),       # Predictor name\n        (\"results\", Optional[Results]),    # Computed prediction results or None.\n])\n\n\nclass OWPredictions(OWWidget):\n    name = \"Predictions\"\n    icon = \"icons/Predictions.svg\"\n    priority = 200\n    description = \"Display predictions of models for an input dataset.\"\n    keywords = []\n\n    buttons_area_orientation = None\n\n    class Inputs:\n        data = Input(\"Data\", Orange.data.Table)\n        predictors = MultiInput(\"Predictors\", Model, filter_none=True)\n\n    class Outputs:\n        predictions = Output(\"Predictions\", Orange.data.Table)\n        evaluation_results = Output(\"Evaluation Results\", Results)\n\n    class Warning(OWWidget.Warning):\n        empty_data = Msg(\"Empty dataset\")\n        wrong_targets = Msg(\n            \"Some model(s) predict a different target (see more ...)\\n{}\")\n\n    class Error(OWWidget.Error):\n        predictor_failed = Msg(\"Some predictor(s) failed (see more ...)\\n{}\")\n        scorer_failed = Msg(\"Some scorer(s) failed (see more ...)\\n{}\")\n\n    settingsHandler = settings.ClassValuesContextHandler()\n    score_table = settings.SettingProvider(ScoreTable)\n\n    #: List of selected class value indices in the `class_values` list\n    selected_classes = settings.ContextSetting([])\n    selection = settings.Setting([], schema_only=True)\n\n    def __init__(self):\n        super().__init__()\n\n        self.data = None  # type: Optional[Orange.data.Table]\n        self.predictors = []  # type: List[PredictorSlot]\n        self.class_values = []  # type: List[str]\n        self._delegates = []\n        self.left_width = 10\n        self.selection_store = None\n        self.__pending_selection = self.selection\n\n        controlBox = gui.vBox(self.controlArea, \"Show probabilities for\")\n\n        gui.listBox(controlBox, self, \"selected_classes\", \"class_values\",\n                    callback=self._update_prediction_delegate,\n                    selectionMode=QListWidget.ExtendedSelection,\n                    sizePolicy=(QSizePolicy.Preferred, QSizePolicy.MinimumExpanding),\n                    sizeHint=QSize(1, 350),\n                    minimumHeight=100)\n        self.reset_button = gui.button(\n            controlBox, self, \"Restore Original Order\",\n            callback=self._reset_order,\n            tooltip=\"Show rows in the original order\")\n\n        table_opts = dict(horizontalScrollBarPolicy=Qt.ScrollBarAlwaysOn,\n                          horizontalScrollMode=QTableView.ScrollPerPixel,\n                          selectionMode=QTableView.ExtendedSelection,\n                          focusPolicy=Qt.StrongFocus)\n        self.dataview = TableView(\n            sortingEnabled=True,\n            verticalScrollBarPolicy=Qt.ScrollBarAlwaysOn,\n            **table_opts)\n        self.predictionsview = TableView(\n            sortingEnabled=True,\n            verticalScrollBarPolicy=Qt.ScrollBarAlwaysOff,\n            **table_opts)\n        self.dataview.verticalHeader().hide()\n        dsbar = self.dataview.verticalScrollBar()\n        psbar = self.predictionsview.verticalScrollBar()\n        psbar.valueChanged.connect(dsbar.setValue)\n        dsbar.valueChanged.connect(psbar.setValue)\n\n        self.dataview.verticalHeader().setDefaultSectionSize(22)\n        self.predictionsview.verticalHeader().setDefaultSectionSize(22)\n        self.dataview.verticalHeader().sectionResized.connect(\n            lambda index, _, size:\n            self.predictionsview.verticalHeader().resizeSection(index, size))\n\n        self.dataview.setItemDelegate(DataItemDelegate(self.dataview))\n\n        self.splitter = QSplitter(\n            orientation=Qt.Horizontal, childrenCollapsible=False, handleWidth=2)\n        self.splitter.splitterMoved.connect(self.splitter_resized)\n        self.splitter.addWidget(self.predictionsview)\n        self.splitter.addWidget(self.dataview)\n\n        self.score_table = ScoreTable(self)\n        self.vsplitter = gui.vBox(self.mainArea)\n        self.vsplitter.layout().addWidget(self.splitter)\n        self.vsplitter.layout().addWidget(self.score_table.view)\n\n    def get_selection_store(self, model):\n        # Both models map the same, so it doesn't matter which one is used\n        # to initialize SharedSelectionStore\n        if self.selection_store is None:\n            self.selection_store = SharedSelectionStore(model)\n        return self.selection_store\n\n    @Inputs.data\n    @check_sql_input\n    def set_data(self, data):\n        self.Warning.empty_data(shown=data is not None and not data)\n        self.data = data\n        self.selection_store = None\n        if not data:\n            self.dataview.setModel(None)\n            self.predictionsview.setModel(None)\n        else:\n            # force full reset of the view's HeaderView state\n            self.dataview.setModel(None)\n            model = DataModel(data, parent=None)\n            self.dataview.setModel(model)\n            sel_model = SharedSelectionModel(\n                self.get_selection_store(model), model, self.dataview)\n            self.dataview.setSelectionModel(sel_model)\n            if self.__pending_selection is not None:\n                self.selection = self.__pending_selection\n                self.__pending_selection = None\n                self.selection_store.select_rows(\n                    set(self.selection), QItemSelectionModel.ClearAndSelect)\n            sel_model.selectionChanged.connect(self.commit)\n            sel_model.selectionChanged.connect(self._store_selection)\n\n            self.dataview.model().list_sorted.connect(\n                partial(\n                    self._update_data_sort_order, self.dataview,\n                    self.predictionsview))\n\n        self._invalidate_predictions()\n\n    def _store_selection(self):\n        self.selection = list(self.selection_store.rows)\n\n    @property\n    def class_var(self):\n        return self.data and self.data.domain.class_var\n\n    @Inputs.predictors\n    def set_predictor(self, index, predictor: Model):\n        item = self.predictors[index]\n        self.predictors[index] = item._replace(\n            predictor=predictor, name=predictor.name, results=None\n        )\n\n    @Inputs.predictors.insert\n    def insert_predictor(self, index, predictor: Model):\n        item = PredictorSlot(predictor, predictor.name, None)\n        self.predictors.insert(index, item)\n\n    @Inputs.predictors.remove\n    def remove_predictor(self, index):\n        self.predictors.pop(index)\n\n    def _set_class_values(self):\n        class_values = []\n        for slot in self.predictors:\n            class_var = slot.predictor.domain.class_var\n            if class_var and class_var.is_discrete:\n                for value in class_var.values:\n                    if value not in class_values:\n                        class_values.append(value)\n\n        if self.class_var and self.class_var.is_discrete:\n            values = self.class_var.values\n            self.class_values = sorted(\n                class_values, key=lambda val: val not in values)\n            self.selected_classes = [\n                i for i, name in enumerate(class_values) if name in values]\n        else:\n            self.class_values = class_values  # This assignment updates listview\n            self.selected_classes = []\n\n    def handleNewSignals(self):\n        # Disconnect the model: the model and the delegate will be inconsistent\n        # between _set_class_values and update_predictions_model.\n        self.predictionsview.setModel(None)\n        self._set_class_values()\n        self._call_predictors()\n        self._update_scores()\n        self._update_predictions_model()\n        self._update_prediction_delegate()\n        self._set_errors()\n        self.commit()\n\n    def _call_predictors(self):\n        if not self.data:\n            return\n        if self.class_var:\n            domain = self.data.domain\n            classless_data = self.data.transform(\n                Domain(domain.attributes, None, domain.metas))\n        else:\n            classless_data = self.data\n\n        for index, slot in enumerate(self.predictors):\n            if isinstance(slot.results, Results):\n                continue\n\n            predictor = slot.predictor\n            try:\n                if predictor.domain.class_var.is_discrete:\n                    pred, prob = predictor(classless_data, Model.ValueProbs)\n                else:\n                    pred = predictor(classless_data, Model.Value)\n                    prob = numpy.zeros((len(pred), 0))\n            except (ValueError, DomainTransformationError) as err:\n                self.predictors[index] = \\\n                    slot._replace(results=f\"{predictor.name}: {err}\")\n                continue\n\n            results = Results()\n            results.data = self.data\n            results.domain = self.data.domain\n            results.row_indices = numpy.arange(len(self.data))\n            results.folds = (Ellipsis, )\n            results.actual = self.data.Y\n            results.unmapped_probabilities = prob\n            results.unmapped_predicted = pred\n            results.probabilities = results.predicted = None\n            self.predictors[index] = slot._replace(results=results)\n\n            target = predictor.domain.class_var\n            if target != self.class_var:\n                continue\n\n            if target is not self.class_var and target.is_discrete:\n                backmappers, n_values = predictor.get_backmappers(self.data)\n                prob = predictor.backmap_probs(prob, n_values, backmappers)\n                pred = predictor.backmap_value(pred, prob, n_values, backmappers)\n            results.predicted = pred.reshape((1, len(self.data)))\n            results.probabilities = prob.reshape((1,) + prob.shape)\n\n    def _update_scores(self):\n        model = self.score_table.model\n        model.clear()\n        scorers = usable_scorers(self.class_var) if self.class_var else []\n        self.score_table.update_header(scorers)\n        errors = []\n        for pred in self.predictors:\n            results = pred.results\n            if not isinstance(results, Results) or results.predicted is None:\n                continue\n            row = [QStandardItem(learner_name(pred.predictor)),\n                   QStandardItem(\"N/A\"), QStandardItem(\"N/A\")]\n            for scorer in scorers:\n                item = QStandardItem()\n                try:\n                    score = scorer_caller(scorer, results)()[0]\n                    item.setText(f\"{score:.3f}\")\n                except Exception as exc:  # pylint: disable=broad-except\n                    item.setToolTip(str(exc))\n                    # false pos.; pylint: disable=unsupported-membership-test\n                    if scorer.name in self.score_table.shown_scores:\n                        errors.append(str(exc))\n                row.append(item)\n            self.score_table.model.appendRow(row)\n\n        view = self.score_table.view\n        if model.rowCount():\n            view.setVisible(True)\n            view.ensurePolished()\n            view.setFixedHeight(\n                5 + view.horizontalHeader().height() +\n                view.verticalHeader().sectionSize(0) * model.rowCount())\n        else:\n            view.setVisible(False)\n\n        self.Error.scorer_failed(\"\\n\".join(errors), shown=bool(errors))\n\n    def _set_errors(self):\n        # Not all predictors are run every time, so errors can't be collected\n        # in _call_predictors\n        errors = \"\\n\".join(\n            f\"- {p.predictor.name}: {p.results}\"\n            for p in self.predictors\n            if isinstance(p.results, str) and p.results)\n        self.Error.predictor_failed(errors, shown=bool(errors))\n\n        if self.class_var:\n            inv_targets = \"\\n\".join(\n                f\"- {pred.name} predicts '{pred.domain.class_var.name}'\"\n                for pred in (p.predictor for p in self.predictors\n                             if isinstance(p.results, Results)\n                             and p.results.probabilities is None))\n            self.Warning.wrong_targets(inv_targets, shown=bool(inv_targets))\n        else:\n            self.Warning.wrong_targets.clear()\n\n    def _get_details(self):\n        details = \"Data:<br>\"\n        details += format_summary_details(self.data, format=Qt.RichText)\n        details += \"<hr>\"\n        pred_names = [v.name for v in self.predictors]\n        n_predictors = len(self.predictors)\n        if n_predictors:\n            n_valid = len(self._non_errored_predictors())\n            details += plural(\"Model: {number} model{s}\", n_predictors)\n            if n_valid != n_predictors:\n                details += f\" ({n_predictors - n_valid} failed)\"\n            details += \"<ul>\"\n            for name in pred_names:\n                details += f\"<li>{name}</li>\"\n            details += \"</ul>\"\n        else:\n            details += \"Model:<br>No model on input.\"\n        return details\n\n    def _invalidate_predictions(self):\n        for i, pred in enumerate(self.predictors):\n            self.predictors[i] = pred._replace(results=None)\n\n    def _non_errored_predictors(self):\n        return [p for p in self.predictors\n                if isinstance(p.results, Results)]\n\n    def _reordered_probabilities(self, prediction):\n        cur_values = prediction.predictor.domain.class_var.values\n        new_ind = [self.class_values.index(x) for x in cur_values]\n        probs = prediction.results.unmapped_probabilities\n        new_probs = numpy.full(\n            (probs.shape[0], len(self.class_values)), numpy.nan)\n        new_probs[:, new_ind] = probs\n        return new_probs\n\n    def _update_predictions_model(self):\n        headers = []\n        all_values = []\n        all_probs = []\n        for p in self._non_errored_predictors():\n            values = p.results.unmapped_predicted\n            target = p.predictor.domain.class_var\n            if target.is_discrete:\n                # order probabilities in order from Show prob. for\n                prob = self._reordered_probabilities(p)\n                values = numpy.array(target.values)[values.astype(int)]\n            else:\n                prob = numpy.zeros((len(values), 0))\n            all_values.append(values)\n            all_probs.append(prob)\n            headers.append(p.predictor.name)\n\n        if all_values:\n            model = PredictionsModel(all_values, all_probs, headers)\n            model.list_sorted.connect(\n                partial(\n                    self._update_data_sort_order, self.predictionsview,\n                    self.dataview))\n        else:\n            model = None\n\n        if self.selection_store is not None:\n            self.selection_store.unregister(\n                self.predictionsview.selectionModel())\n\n        self.predictionsview.setModel(model)\n        if model is not None:\n            self.predictionsview.setSelectionModel(\n                SharedSelectionModel(self.get_selection_store(model),\n                                     model, self.predictionsview))\n\n        hheader = self.predictionsview.horizontalHeader()\n        hheader.setSortIndicatorShown(False)\n        hheader.setSectionsClickable(True)\n\n    def _update_data_sort_order(self, sort_source_view, sort_dest_view):\n        sort_source_view.horizontalHeader().setSortIndicatorShown(True)\n        sort_dest_view.horizontalHeader().setSortIndicatorShown(False)\n\n        sort_dest = sort_dest_view.model()\n        sort_source = sort_source_view.model()\n        if sort_dest is not None:\n            if sort_source is not None and sort_source.sortColumn() >= 0:\n                sort_dest.setSortIndices(sort_source.mapToSourceRows(...))\n            else:\n                sort_dest.setSortIndices(None)\n        self.commit()\n\n    def _reset_order(self):\n        datamodel = self.dataview.model()\n        predmodel = self.predictionsview.model()\n        if datamodel is not None:\n            datamodel.setSortIndices(None)\n            datamodel.sort(-1)\n        if predmodel is not None:\n            predmodel.setSortIndices(None)\n            predmodel.sort(-1)\n        self.predictionsview.horizontalHeader().setSortIndicatorShown(False)\n        self.dataview.horizontalHeader().setSortIndicatorShown(False)\n\n    def _all_color_values(self):\n        \"\"\"\n        Return list of colors together with their values from all predictors\n        classes. Colors and values are sorted according to the values order\n        for simpler comparison.\n        \"\"\"\n        predictors = self._non_errored_predictors()\n        color_values = [\n            list(zip(*sorted(zip(\n                p.predictor.domain.class_var.colors,\n                p.predictor.domain.class_var.values\n            ), key=itemgetter(1))))\n            for p in predictors if p.predictor.domain.class_var.is_discrete\n        ]\n        return color_values if color_values else [([], [])]\n\n    @staticmethod\n    def _colors_match(colors1, values1, color2, values2):\n        \"\"\"\n        Test whether colors for values match. Colors matches when all\n        values match for shorter list and colors match for shorter list.\n        It is assumed that values will be sorted together with their colors.\n        \"\"\"\n        shorter_length = min(len(colors1), len(color2))\n        return (values1[:shorter_length] == values2[:shorter_length]\n                and (numpy.array(colors1[:shorter_length]) ==\n                     numpy.array(color2[:shorter_length])).all())\n\n    def _get_colors(self):\n        \"\"\"\n        Defines colors for values. If colors match in all models use the union\n        otherwise use standard colors.\n        \"\"\"\n        all_colors_values = self._all_color_values()\n        base_color, base_values = all_colors_values[0]\n        for c, v in all_colors_values[1:]:\n            if not self._colors_match(base_color, base_values, c, v):\n                base_color = []\n                break\n            # replace base_color if longer\n            if len(v) > len(base_color):\n                base_color = c\n                base_values = v\n\n        if len(base_color) != len(self.class_values):\n            return LimitedDiscretePalette(len(self.class_values)).palette\n        # reorder colors to widgets order\n        colors = [None] * len(self.class_values)\n        for c, v in zip(base_color, base_values):\n            colors[self.class_values.index(v)] = c\n        return colors\n\n    def _update_prediction_delegate(self):\n        self._delegates.clear()\n        colors = self._get_colors()\n        for col, slot in enumerate(self.predictors):\n            target = slot.predictor.domain.class_var\n            shown_probs = (\n                () if target.is_continuous else\n                [val if self.class_values[val] in target.values else None\n                 for val in self.selected_classes]\n            )\n            delegate = PredictionsItemDelegate(\n                None if target.is_continuous else self.class_values,\n                colors,\n                shown_probs,\n                target.format_str if target.is_continuous else None,\n                parent=self.predictionsview\n            )\n            # QAbstractItemView does not take ownership of delegates, so we must\n            self._delegates.append(delegate)\n            self.predictionsview.setItemDelegateForColumn(col, delegate)\n            self.predictionsview.setColumnHidden(col, False)\n\n        self.predictionsview.resizeColumnsToContents()\n        self._recompute_splitter_sizes()\n        if self.predictionsview.model() is not None:\n            self.predictionsview.model().setProbInd(self.selected_classes)\n\n    def _recompute_splitter_sizes(self):\n        if not self.data:\n            return\n        view = self.predictionsview\n        self.left_width = \\\n            view.horizontalHeader().length() + view.verticalHeader().width()\n        self._update_splitter()\n\n    def _update_splitter(self):\n        w1, w2 = self.splitter.sizes()\n        self.splitter.setSizes([self.left_width, w1 + w2 - self.left_width])\n\n    def splitter_resized(self):\n        self.left_width = self.splitter.sizes()[0]\n\n    def commit(self):\n        self._commit_predictions()\n        self._commit_evaluation_results()\n\n    def _commit_evaluation_results(self):\n        slots = [p for p in self._non_errored_predictors()\n                 if p.results.predicted is not None]\n        if not slots:\n            self.Outputs.evaluation_results.send(None)\n            return\n\n        nanmask = numpy.isnan(self.data.get_column_view(self.class_var)[0])\n        data = self.data[~nanmask]\n        results = Results(data, store_data=True)\n        results.folds = None\n        results.row_indices = numpy.arange(len(data))\n        results.actual = data.Y.ravel()\n        results.predicted = numpy.vstack(\n            tuple(p.results.predicted[0][~nanmask] for p in slots))\n        if self.class_var and self.class_var.is_discrete:\n            results.probabilities = numpy.array(\n                [p.results.probabilities[0][~nanmask] for p in slots])\n        results.learner_names = [p.name for p in slots]\n        self.Outputs.evaluation_results.send(results)\n\n    def _commit_predictions(self):\n        if not self.data:\n            self.Outputs.predictions.send(None)\n            return\n\n        newmetas = []\n        newcolumns = []\n        for slot in self._non_errored_predictors():\n            if slot.predictor.domain.class_var.is_discrete:\n                self._add_classification_out_columns(slot, newmetas, newcolumns)\n            else:\n                self._add_regression_out_columns(slot, newmetas, newcolumns)\n\n        attrs = list(self.data.domain.attributes)\n        metas = list(self.data.domain.metas)\n        names = [var.name for var in chain(attrs, self.data.domain.class_vars, metas) if var]\n        uniq_newmetas = []\n        for new_ in newmetas:\n            uniq = get_unique_names(names, new_.name)\n            if uniq != new_.name:\n                new_ = new_.copy(name=uniq)\n            uniq_newmetas.append(new_)\n            names.append(uniq)\n\n        metas += uniq_newmetas\n        domain = Orange.data.Domain(attrs, self.class_var, metas=metas)\n        predictions = self.data.transform(domain)\n        if newcolumns:\n            newcolumns = numpy.hstack(\n                [numpy.atleast_2d(cols) for cols in newcolumns])\n            with predictions.unlocked(predictions.metas):\n                predictions.metas[:, -newcolumns.shape[1]:] = newcolumns\n\n        datamodel = self.dataview.model()\n        predmodel = self.predictionsview.model()\n        assert datamodel is not None  # because we have data\n        assert self.selection_store is not None\n        rows = numpy.array(list(self.selection_store.rows))\n        if rows.size:\n            # Reorder rows as they are ordered in view\n            shown_rows = datamodel.mapFromSourceRows(rows)\n            rows = rows[numpy.argsort(shown_rows)]\n            predictions = predictions[rows]\n        elif datamodel.sortColumn() >= 0 \\\n                or predmodel is not None and predmodel.sortColumn() > 0:\n            # No selection: output all, but in the shown order\n            predictions = predictions[datamodel.mapToSourceRows(...)]\n        self.Outputs.predictions.send(predictions)\n\n    @staticmethod\n    def _add_classification_out_columns(slot, newmetas, newcolumns):\n        # Mapped or unmapped predictions?!\n        # Or provide a checkbox so the user decides?\n        pred = slot.predictor\n        name = pred.name\n        values = pred.domain.class_var.values\n        newmetas.append(DiscreteVariable(name=name, values=values))\n        newcolumns.append(slot.results.unmapped_predicted.reshape(-1, 1))\n        newmetas += [ContinuousVariable(name=f\"{name} ({value})\")\n                     for value in values]\n        newcolumns.append(slot.results.unmapped_probabilities)\n\n    @staticmethod\n    def _add_regression_out_columns(slot, newmetas, newcolumns):\n        newmetas.append(ContinuousVariable(name=slot.predictor.name))\n        newcolumns.append(slot.results.unmapped_predicted.reshape((-1, 1)))\n\n    def send_report(self):\n        def merge_data_with_predictions():\n            data_model = self.dataview.model()\n            predictions_view = self.predictionsview\n            predictions_model = predictions_view.model()\n\n            # use ItemDelegate to style prediction values\n            delegates = [predictions_view.itemDelegateForColumn(i)\n                         for i in range(predictions_model.columnCount())]\n\n            # iterate only over visible columns of data's QTableView\n            iter_data_cols = list(filter(lambda x: not self.dataview.isColumnHidden(x),\n                                         range(data_model.columnCount())))\n\n            # print header\n            yield [''] + \\\n                  [predictions_model.headerData(col, Qt.Horizontal, Qt.DisplayRole)\n                   for col in range(predictions_model.columnCount())] + \\\n                  [data_model.headerData(col, Qt.Horizontal, Qt.DisplayRole)\n                   for col in iter_data_cols]\n\n            # print data & predictions\n            for i in range(data_model.rowCount()):\n                yield [data_model.headerData(i, Qt.Vertical, Qt.DisplayRole)] + \\\n                      [delegate.displayText(\n                          predictions_model.data(predictions_model.index(i, j)),\n                          QLocale())\n                       for j, delegate in enumerate(delegates)] + \\\n                      [data_model.data(data_model.index(i, j),\n                                       role=Qt.DisplayRole)\n                       for j in iter_data_cols]\n\n        if self.data:\n            text = self._get_details().replace('\\n', '<br>')\n            if self.selected_classes:\n                text += '<br>Showing probabilities for: '\n                text += ', '. join([self.class_values[i]\n                                    for i in self.selected_classes])\n            self.report_paragraph('Info', text)\n            self.report_table(\"Data & Predictions\", merge_data_with_predictions(),\n                              header_rows=1, header_columns=1)\n\n            self.report_table(\"Scores\", self.score_table.view)\n\n    def resizeEvent(self, event):\n        super().resizeEvent(event)\n        self._update_splitter()\n\n    def showEvent(self, event):\n        super().showEvent(event)\n        QTimer.singleShot(0, self._update_splitter)\n\n\nclass ItemDelegate(TableDataDelegate):\n    def initStyleOption(self, option, index):\n        super().initStyleOption(option, index)\n        if self.parent().selectionModel().isSelected(index):\n            option.state |= QStyle.State_Selected\n        if self.parent().window().isActiveWindow():\n            option.state |= QStyle.State_Active | QStyle.State_HasFocus\n\n\nclass DataItemDelegate(ItemDelegate):\n    pass\n\n\nclass PredictionsItemDelegate(ItemDelegate):\n    \"\"\"\n    A Item Delegate for custom formatting of predictions/probabilities\n    \"\"\"\n    #: Roles supplied by the `PredictionsModel`\n    DefaultRoles = (Qt.DisplayRole, )\n\n    def __init__(\n            self, class_values, colors, shown_probabilities=(),\n            target_format=None, parent=None,\n    ):\n        super().__init__(parent)\n        self.class_values = class_values  # will be None for continuous\n        self.colors = [QColor(*c) for c in colors]\n        self.target_format = target_format  # target format for cont. vars\n        self.shown_probabilities = self.fmt = self.tooltip = None  # set below\n        self.setFormat(shown_probabilities)\n\n    def setFormat(self, shown_probabilities=()):\n        self.shown_probabilities = shown_probabilities\n        if self.class_values is None:\n            # is continuous class\n            self.fmt = f\"{{value:{self.target_format[1:]}}}\"\n        else:\n            self.fmt = \" \\N{RIGHTWARDS ARROW} \".join(\n                [\" : \".join(f\"{{dist[{i}]:.2f}}\" if i is not None else \"-\"\n                            for i in shown_probabilities)]\n                * bool(shown_probabilities)\n                + [\"{value!s}\"])\n        self.tooltip = \"\"\n        if shown_probabilities:\n            val = ', '.join(\n                self.class_values[i] if i is not None else \"-\"\n                for i in shown_probabilities if i is not None\n            )\n            self.tooltip = f\"p({val})\"\n\n    def displayText(self, value, _):\n        try:\n            value, dist = value\n        except ValueError:\n            return \"\"\n        else:\n            return self.fmt.format(value=value, dist=dist)\n\n    def helpEvent(self, event, view, option, index):\n        if self.tooltip is not None:\n            # ... but can be an empty string, so the current tooltip is removed\n            QToolTip.showText(event.globalPos(), self.tooltip, view)\n            return True\n        else:\n            return super().helpEvent(event, view, option, index)\n\n    def initStyleOption(self, option, index):\n        super().initStyleOption(option, index)\n        if self.class_values is None:\n            option.displayAlignment = \\\n                (option.displayAlignment & Qt.AlignVertical_Mask) | \\\n                Qt.AlignRight\n\n    def sizeHint(self, option, index):\n        # reimplemented\n        sh = super().sizeHint(option, index)\n        if option.widget is not None:\n            style = option.widget.style()\n        else:\n            style = QApplication.style()\n        margin = style.pixelMetric(\n            QStyle.PM_FocusFrameHMargin, option, option.widget) + 1\n        metrics = option.fontMetrics\n        height = sh.height() + metrics.leading() + 2 * margin\n        return QSize(sh.width(), height)\n\n    def distribution(self, index):\n        value = self.cachedData(index, Qt.DisplayRole)\n        if isinstance(value, tuple) and len(value) == 2:\n            _, dist = value\n            return dist\n        else:\n            return None\n\n    def paint(self, painter, option, index):\n        dist = self.distribution(index)\n        if dist is None or self.colors is None:\n            super().paint(painter, option, index)\n            return\n\n        if option.widget is not None:\n            style = option.widget.style()\n        else:\n            style = QApplication.style()\n\n        self.initStyleOption(option, index)\n\n        text = option.text\n        metrics = option.fontMetrics\n\n        margin = style.pixelMetric(\n            QStyle.PM_FocusFrameHMargin, option, option.widget) + 1\n        bottommargin = min(margin, 1)\n        rect = option.rect.adjusted(margin, margin, -margin, -bottommargin)\n        option.text = \"\"\n        textrect = style.subElementRect(\n            QStyle.SE_ItemViewItemText, option, option.widget)\n        # Are the margins included in the subElementRect?? -> No!\n        textrect = textrect.adjusted(margin, margin, -margin, -bottommargin)\n        spacing = max(metrics.leading(), 1)\n\n        distheight = rect.height() - metrics.height() - spacing\n        distheight = min(max(distheight, 2), metrics.height())\n        painter.save()\n        painter.setClipRect(option.rect)\n        painter.setFont(option.font)\n        painter.setRenderHint(QPainter.Antialiasing)\n\n        style.drawPrimitive(\n            QStyle.PE_PanelItemViewRow, option, painter, option.widget)\n        style.drawPrimitive(\n            QStyle.PE_PanelItemViewItem, option, painter, option.widget)\n\n        textrect = textrect.adjusted(0, 0, 0, -distheight - spacing)\n        distrect = QRect(\n            textrect.bottomLeft() + QPoint(0, spacing),\n            QSize(rect.width(), distheight))\n        painter.setPen(QPen(Qt.lightGray, 0.3))\n        self.drawDistBar(painter, distrect, dist)\n        painter.restore()\n        if text:\n            option.text = text\n            self.drawViewItemText(style, painter, option, textrect)\n\n    def drawDistBar(self, painter, rect, distribution):\n        painter.save()\n        painter.translate(rect.topLeft())\n        for i in self.shown_probabilities:\n            if i is None:\n                continue\n            dvalue = distribution[i]\n            if not dvalue > 0:  # This also skips nans\n                continue\n            painter.setBrush(self.colors[i])\n            width = rect.width() * dvalue\n            painter.drawRoundedRect(QRectF(0, 0, width, rect.height()), 1, 2)\n            painter.translate(width, 0.0)\n        painter.restore()\n\n\nclass PredictionsModel(AbstractSortTableModel):\n    list_sorted = pyqtSignal()\n\n    def __init__(self, values=None, probs=None, headers=None, parent=None):\n        super().__init__(parent)\n        self._values = values\n        self._probs = probs\n        self.__probInd = None\n        if values is not None:\n            assert len(self._values) == len(self._probs) != 0\n            sizes = {len(x) for c in (values, probs) for x in c}\n            assert len(sizes) == 1\n            self.__columnCount = len(values)\n            self.__rowCount = sizes.pop()\n            if headers is None:\n                headers = [None] * self.__columnCount\n        else:\n            assert probs is None\n            assert headers is None\n            self.__columnCount = self.__rowCount = 0\n        self._header = headers\n\n    def rowCount(self, parent=QModelIndex()):\n        return 0 if parent.isValid() else self.__rowCount\n\n    def columnCount(self, parent=QModelIndex()):\n        return 0 if parent.isValid() else self.__columnCount\n\n    def data(self, index, role=Qt.DisplayRole):\n        if role in (Qt.DisplayRole, Qt.EditRole):\n            column = index.column()\n            row = self.mapToSourceRows(index.row())\n            return self._values[column][row], self._probs[column][row]\n        return None\n\n    def headerData(self, section, orientation, role=Qt.DisplayRole):\n        if role == Qt.DisplayRole:\n            if orientation == Qt.Vertical:\n                return str(section + 1)\n            elif self._header is not None and section < len(self._header):\n                return self._header[section]\n        return None\n\n    def setProbInd(self, indices):\n        self.__probInd = indices\n        self.sort(self.sortColumn())\n\n    def sortColumnData(self, column):\n        values = self._values[column]\n        probs = self._probs[column]\n        # Let us assume that probs can be None, numpy array or list of arrays\n        if probs is not None and len(probs) and len(probs[0]) \\\n                and self.__probInd is not None and len(self.__probInd):\n            return probs[:, self.__probInd]\n        else:\n            return values\n\n    def sort(self, column, order=Qt.AscendingOrder):\n        super().sort(column, order)\n        self.list_sorted.emit()\n\n\n# PredictionsModel and DataModel have the same signal and sort method, but\n# extracting it into a common base class would require diamond inheritance\n# because they're derived from different classes. It's not worth it.\nclass DataModel(TableModel):\n    list_sorted = pyqtSignal()\n\n    def sort(self, column, order=Qt.AscendingOrder):\n        super().sort(column, order)\n        self.list_sorted.emit()\n\n\nclass SharedSelectionStore:\n    \"\"\"\n    An object shared between multiple selection models\n\n    The object assumes that the underlying models are AbstractSortTableModel.\n    Internally, the object stores indices of unmapped, source rows (as int).\n\n    The class implements method `select` with the same signature as\n    QItemSelectionModel.select. Selection models that share this object\n    must call this method. After changing the selection, this method will\n    call `emit_selection_rows_changed` of all selection models, so they\n    can emit the signal selectionChanged.\n    \"\"\"\n    def __init__(self, model):\n        # unmapped indices of selected rows\n        self._rows: Set[int] = set()\n        self.model: AbstractSortTableModel = model\n        self._selection_models: List[SharedSelectionModel] = []\n\n    @property\n    def rows(self) -> Set[int]:\n        \"\"\"Indices of selected rows in the source model\"\"\"\n        return self._rows\n\n    def register(self, selection_model):\n        \"\"\"\n        Add a selection mode to the list of models\n\n        Args:\n            selection_model (SharedSelectionModel): a new model\n        \"\"\"\n        self._selection_models.append(selection_model)\n\n    def unregister(self, selection_model):\n        \"\"\"\n        Remove a selection mode to the list of models\n\n        Args:\n            selection_model (SharedSelectionModel): a model to remove\n        \"\"\"\n        if selection_model in self._selection_models:\n            self._selection_models.remove(selection_model)\n\n    def select(self, selection: Union[QModelIndex, QItemSelection], flags: int):\n        \"\"\"\n        (De)Select given rows\n\n        Args:\n            selection (QModelIndex or QItemSelection):\n                rows to select; indices are mapped to rows in the view\n            flags (QItemSelectionModel.SelectionFlags):\n                flags that tell whether to Clear, Select, Deselect or Toggle\n        \"\"\"\n        rows = set()\n        if isinstance(selection, QModelIndex):\n            if selection.model() is not None:\n                rows = {selection.model().mapToSourceRows(selection.row())}\n        else:\n            indices = selection.indexes()\n            if indices:\n                map_to = indices[0].model().mapToSourceRows\n                rows = set(map_to([index.row() for index in indices]))\n        self.select_rows(rows, flags)\n\n    def select_rows(self, rows: Set[int], flags):\n        \"\"\"\n        (De)Select given rows\n\n        Args:\n            selection (set of int):\n                rows to select; indices refer to unmapped rows in model, not view\n            flags (QItemSelectionModel.SelectionFlags):\n                flags that tell whether to Clear, Select, Deselect or Toggle\n        \"\"\"\n        with self._emit_changed():\n            if flags & QItemSelectionModel.Clear:\n                self._rows.clear()\n            if flags & QItemSelectionModel.Select:\n                self._rows |= rows\n            if flags & QItemSelectionModel.Deselect:\n                self._rows -= rows\n            if flags & QItemSelectionModel.Toggle:\n                self._rows ^= rows\n\n    def clear_selection(self):\n        \"\"\"Clear selection and emit changeSelection signal to all models\"\"\"\n        with self._emit_changed():\n            self._rows.clear()\n\n    def reset(self):\n        \"\"\"Clear selection without emiting a signal,\"\"\"\n        self._rows.clear()\n\n    @contextmanager\n    def _emit_changed(self):\n        \"\"\"\n        A context manager that calls `emit_selection_rows_changed after\n        changing a selection.\n        \"\"\"\n        def map_from_source(rows):\n            return self.model.mapFromSourceRows(list(rows))\n\n        old_rows = self._rows.copy()\n        try:\n            yield\n        finally:\n            if self.model.rowCount() != 0:\n                deselected = map_from_source(old_rows - self._rows)\n                selected = map_from_source(self._rows - old_rows)\n                if len(selected) != 0 or len(deselected) != 0:\n                    for model in self._selection_models:\n                        model.emit_selection_rows_changed(selected, deselected)\n\n\nclass SharedSelectionModel(QItemSelectionModel):\n    \"\"\"\n    A selection model that shares the selection with its peers.\n\n    It assumes that the underlying model is a AbstractTableModel.\n    \"\"\"\n    def __init__(self, shared_store, model, parent):\n        super().__init__(model, parent)\n        self.store: SharedSelectionStore = shared_store\n        self.store.register(self)\n\n    def select(self, selection, flags):\n        self.store.select(selection, flags)\n\n    def selection_from_rows(self, rows: Sequence[int]) -> QItemSelection:\n        \"\"\"\n        Return selection across all columns for given row indices (as ints)\n\n        Args:\n            rows (sequence of int): row indices, as shown in the view, not model\n\n        Returns: QItemSelection\n        \"\"\"\n        model = self.model()\n        index = model.index\n        last_col = model.columnCount() - 1\n        sel = QItemSelection()\n        for row in rows:\n            sel.select(index(row, 0), index(row, last_col))\n        return sel\n\n    def emit_selection_rows_changed(\n            self, selected: Sequence[int], deselected: Sequence[int]):\n        \"\"\"\n        Given a sequence of indices of selected and deselected rows,\n        emit a selectionChanged signal.\n\n        Args:\n            selected (Sequence[int]): indices of selected rows\n            deselected (Sequence[int]): indices of deselected rows\n        \"\"\"\n        self.selectionChanged.emit(\n            self.selection_from_rows(selected),\n            self.selection_from_rows(deselected))\n\n    def selection(self):\n        rows = self.model().mapFromSourceRows(list(self.store.rows))\n        return self.selection_from_rows(rows)\n\n    def hasSelection(self) -> bool:\n        return bool(self.store.rows)\n\n    def isColumnSelected(self, *_) -> bool:\n        return len(self.store.rows) == self.model().rowCount()\n\n    def isRowSelected(self, row, _parent=None) -> bool:\n        mapped_row = self.model().mapToSourceRows(row)\n        return mapped_row in self.store.rows\n\n    rowIntersectsSelection = isRowSelected\n\n    def isSelected(self, index) -> bool:\n        return self.model().mapToSourceRows(index.row()) in self.store.rows\n\n    def selectedColumns(self, row: int):\n        if self.isColumnSelected():\n            index = self.model().index\n            return [index(row, col)\n                    for col in range(self.model().columnCount())]\n        else:\n            return []\n\n    def _selected_rows_arr(self):\n        return numpy.fromiter(self.store.rows, int, len(self.store.rows))\n\n    def selectedRows(self, col: int):\n        index = self.model().index\n        rows = self.model().mapFromSourceRows(self._selected_rows_arr())\n        return [index(row, col) for row in rows]\n\n    def selectedIndexes(self):\n        index = self.model().index\n        rows = self.model().mapFromSourceRows(self._selected_rows_arr())\n        return [index(row, col)\n                for col in range(self.model().columnCount())\n                for row in rows]\n\n    def clearSelection(self):\n        self.store.clear_selection()\n\n    def reset(self):\n        self.store.reset()\n        self.clearCurrentIndex()\n\n\nclass TableView(gui.HScrollStepMixin, QTableView):\n    MaxSizeHintSamples = 1000\n\n    def sizeHintForColumn(self, column):\n        \"\"\"\n        Reimplemented from `QTableView.sizeHintForColumn`\n\n        Note: This does not match the QTableView's implementation,\n        in particular size hints from editor/index widgets are not taken\n        into account.\n\n        Parameters\n        ----------\n        column : int\n        \"\"\"\n        # This is probably not needed in Qt5?\n        if self.model() is None:\n            return -1\n\n        self.ensurePolished()\n        model = self.model()\n        vheader = self.verticalHeader()\n        top = vheader.visualIndexAt(0)\n        bottom = vheader.visualIndexAt(self.viewport().height())\n        if bottom < 0:\n            bottom = self.model().rowCount()\n\n        options = self.viewOptions()\n        options.widget = self\n\n        width = 0\n        sample_count = 0\n\n        for row in range(top, bottom):\n            if not vheader.isSectionHidden(vheader.logicalIndex(row)):\n                index = model.index(row, column)\n                delegate = self.itemDelegate(index)\n                if not delegate:\n                    continue\n                size = delegate.sizeHint(options, index)\n                width = max(size.width(), width)\n                sample_count += 1\n\n            if sample_count >= TableView.MaxSizeHintSamples:\n                break\n\n        return width + 1 if self.showGrid() else width\n\n\ndef tool_tip(value):\n    value, dist = value\n    if dist is not None:\n        return \"{!s} {!s}\".format(value, dist)\n    else:\n        return str(value)\n\n\nif __name__ == \"__main__\":  # pragma: no cover\n    filename = \"iris.tab\"\n    iris = Orange.data.Table(filename)\n    iris2 = iris[:100]\n\n    def pred_error(data, *args, **kwargs):\n        raise ValueError\n\n    pred_error.domain = iris.domain\n    pred_error.name = \"To err is human\"\n\n    if iris.domain.has_discrete_class:\n        idom = iris.domain\n        dom = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[1::-1])\n        )\n        iris2 = iris2.transform(dom)\n        predictors_ = [\n            Orange.classification.SVMLearner(probability=True)(iris2),\n            Orange.classification.LogisticRegressionLearner()(iris),\n            pred_error\n        ]\n    elif iris.domain.has_continuous_class:\n        predictors_ = [\n            Orange.regression.RidgeRegressionLearner(alpha=1.0)(iris),\n            Orange.regression.LinearRegressionLearner()(iris),\n            pred_error\n        ]\n    else:\n        predictors_ = [pred_error]\n\n    WidgetPreview(OWPredictions).run(\n        set_data=iris2,\n        insert_predictor=list(enumerate(predictors_)))\n",
            "new_file_content": "from contextlib import contextmanager\nfrom functools import partial\nfrom operator import itemgetter\nfrom itertools import chain\nfrom typing import Set, Sequence, Union, Optional, List, NamedTuple\n\nimport numpy\nfrom AnyQt.QtWidgets import (\n    QTableView, QListWidget, QSplitter, QToolTip, QStyle, QApplication,\n    QSizePolicy\n)\nfrom AnyQt.QtGui import QPainter, QStandardItem, QPen, QColor\nfrom AnyQt.QtCore import (\n    Qt, QSize, QRect, QRectF, QPoint, QLocale,\n    QModelIndex, pyqtSignal, QTimer,\n    QItemSelectionModel, QItemSelection)\n\nfrom orangewidget.report import plural\nfrom orangewidget.utils.itemmodels import AbstractSortTableModel\n\nimport Orange\nfrom Orange.evaluation import Results\nfrom Orange.base import Model\nfrom Orange.data import ContinuousVariable, DiscreteVariable, Domain\nfrom Orange.data.table import DomainTransformationError\nfrom Orange.data.util import get_unique_names\nfrom Orange.widgets import gui, settings\nfrom Orange.widgets.evaluate.utils import (\n    ScoreTable, usable_scorers, learner_name, scorer_caller)\nfrom Orange.widgets.utils.widgetpreview import WidgetPreview\nfrom Orange.widgets.widget import OWWidget, Msg, Input, Output, MultiInput\nfrom Orange.widgets.utils.itemmodels import TableModel\nfrom Orange.widgets.utils.sql import check_sql_input\nfrom Orange.widgets.utils.state_summary import format_summary_details\nfrom Orange.widgets.utils.colorpalettes import LimitedDiscretePalette\nfrom Orange.widgets.utils.itemdelegates import TableDataDelegate\n\n# Input slot for the Predictors channel\nPredictorSlot = NamedTuple(\n    \"PredictorSlot\", [\n        (\"predictor\", Model),  # The `Model` instance\n        (\"name\", str),       # Predictor name\n        (\"results\", Optional[Results]),    # Computed prediction results or None.\n])\n\n\nclass OWPredictions(OWWidget):\n    name = \"Predictions\"\n    icon = \"icons/Predictions.svg\"\n    priority = 200\n    description = \"Display predictions of models for an input dataset.\"\n    keywords = []\n\n    buttons_area_orientation = None\n\n    class Inputs:\n        data = Input(\"Data\", Orange.data.Table)\n        predictors = MultiInput(\"Predictors\", Model, filter_none=True)\n\n    class Outputs:\n        predictions = Output(\"Predictions\", Orange.data.Table)\n        evaluation_results = Output(\"Evaluation Results\", Results)\n\n    class Warning(OWWidget.Warning):\n        empty_data = Msg(\"Empty dataset\")\n        wrong_targets = Msg(\n            \"Some model(s) predict a different target (see more ...)\\n{}\")\n        missing_targets = Msg(\"Instances with missing targets \"\n                              \"are ignored while scoring.\")\n\n    class Error(OWWidget.Error):\n        predictor_failed = Msg(\"Some predictor(s) failed (see more ...)\\n{}\")\n        scorer_failed = Msg(\"Some scorer(s) failed (see more ...)\\n{}\")\n\n    settingsHandler = settings.ClassValuesContextHandler()\n    score_table = settings.SettingProvider(ScoreTable)\n\n    #: List of selected class value indices in the `class_values` list\n    selected_classes = settings.ContextSetting([])\n    selection = settings.Setting([], schema_only=True)\n\n    def __init__(self):\n        super().__init__()\n\n        self.data = None  # type: Optional[Orange.data.Table]\n        self.predictors = []  # type: List[PredictorSlot]\n        self.class_values = []  # type: List[str]\n        self._delegates = []\n        self.left_width = 10\n        self.selection_store = None\n        self.__pending_selection = self.selection\n\n        controlBox = gui.vBox(self.controlArea, \"Show probabilities for\")\n\n        gui.listBox(controlBox, self, \"selected_classes\", \"class_values\",\n                    callback=self._update_prediction_delegate,\n                    selectionMode=QListWidget.ExtendedSelection,\n                    sizePolicy=(QSizePolicy.Preferred, QSizePolicy.MinimumExpanding),\n                    sizeHint=QSize(1, 350),\n                    minimumHeight=100)\n        self.reset_button = gui.button(\n            controlBox, self, \"Restore Original Order\",\n            callback=self._reset_order,\n            tooltip=\"Show rows in the original order\")\n\n        table_opts = dict(horizontalScrollBarPolicy=Qt.ScrollBarAlwaysOn,\n                          horizontalScrollMode=QTableView.ScrollPerPixel,\n                          selectionMode=QTableView.ExtendedSelection,\n                          focusPolicy=Qt.StrongFocus)\n        self.dataview = TableView(\n            sortingEnabled=True,\n            verticalScrollBarPolicy=Qt.ScrollBarAlwaysOn,\n            **table_opts)\n        self.predictionsview = TableView(\n            sortingEnabled=True,\n            verticalScrollBarPolicy=Qt.ScrollBarAlwaysOff,\n            **table_opts)\n        self.dataview.verticalHeader().hide()\n        dsbar = self.dataview.verticalScrollBar()\n        psbar = self.predictionsview.verticalScrollBar()\n        psbar.valueChanged.connect(dsbar.setValue)\n        dsbar.valueChanged.connect(psbar.setValue)\n\n        self.dataview.verticalHeader().setDefaultSectionSize(22)\n        self.predictionsview.verticalHeader().setDefaultSectionSize(22)\n        self.dataview.verticalHeader().sectionResized.connect(\n            lambda index, _, size:\n            self.predictionsview.verticalHeader().resizeSection(index, size))\n\n        self.dataview.setItemDelegate(DataItemDelegate(self.dataview))\n\n        self.splitter = QSplitter(\n            orientation=Qt.Horizontal, childrenCollapsible=False, handleWidth=2)\n        self.splitter.splitterMoved.connect(self.splitter_resized)\n        self.splitter.addWidget(self.predictionsview)\n        self.splitter.addWidget(self.dataview)\n\n        self.score_table = ScoreTable(self)\n        self.vsplitter = gui.vBox(self.mainArea)\n        self.vsplitter.layout().addWidget(self.splitter)\n        self.vsplitter.layout().addWidget(self.score_table.view)\n\n    def get_selection_store(self, model):\n        # Both models map the same, so it doesn't matter which one is used\n        # to initialize SharedSelectionStore\n        if self.selection_store is None:\n            self.selection_store = SharedSelectionStore(model)\n        return self.selection_store\n\n    @Inputs.data\n    @check_sql_input\n    def set_data(self, data):\n        self.Warning.empty_data(shown=data is not None and not data)\n        self.data = data\n        self.selection_store = None\n        if not data:\n            self.dataview.setModel(None)\n            self.predictionsview.setModel(None)\n        else:\n            # force full reset of the view's HeaderView state\n            self.dataview.setModel(None)\n            model = DataModel(data, parent=None)\n            self.dataview.setModel(model)\n            sel_model = SharedSelectionModel(\n                self.get_selection_store(model), model, self.dataview)\n            self.dataview.setSelectionModel(sel_model)\n            if self.__pending_selection is not None:\n                self.selection = self.__pending_selection\n                self.__pending_selection = None\n                self.selection_store.select_rows(\n                    set(self.selection), QItemSelectionModel.ClearAndSelect)\n            sel_model.selectionChanged.connect(self.commit)\n            sel_model.selectionChanged.connect(self._store_selection)\n\n            self.dataview.model().list_sorted.connect(\n                partial(\n                    self._update_data_sort_order, self.dataview,\n                    self.predictionsview))\n\n        self._invalidate_predictions()\n\n    def _store_selection(self):\n        self.selection = list(self.selection_store.rows)\n\n    @property\n    def class_var(self):\n        return self.data and self.data.domain.class_var\n\n    @Inputs.predictors\n    def set_predictor(self, index, predictor: Model):\n        item = self.predictors[index]\n        self.predictors[index] = item._replace(\n            predictor=predictor, name=predictor.name, results=None\n        )\n\n    @Inputs.predictors.insert\n    def insert_predictor(self, index, predictor: Model):\n        item = PredictorSlot(predictor, predictor.name, None)\n        self.predictors.insert(index, item)\n\n    @Inputs.predictors.remove\n    def remove_predictor(self, index):\n        self.predictors.pop(index)\n\n    def _set_class_values(self):\n        class_values = []\n        for slot in self.predictors:\n            class_var = slot.predictor.domain.class_var\n            if class_var and class_var.is_discrete:\n                for value in class_var.values:\n                    if value not in class_values:\n                        class_values.append(value)\n\n        if self.class_var and self.class_var.is_discrete:\n            values = self.class_var.values\n            self.class_values = sorted(\n                class_values, key=lambda val: val not in values)\n            self.selected_classes = [\n                i for i, name in enumerate(class_values) if name in values]\n        else:\n            self.class_values = class_values  # This assignment updates listview\n            self.selected_classes = []\n\n    def handleNewSignals(self):\n        # Disconnect the model: the model and the delegate will be inconsistent\n        # between _set_class_values and update_predictions_model.\n        self.predictionsview.setModel(None)\n        self._set_class_values()\n        self._call_predictors()\n        self._update_scores()\n        self._update_predictions_model()\n        self._update_prediction_delegate()\n        self._set_errors()\n        self.commit()\n\n    def _call_predictors(self):\n        if not self.data:\n            return\n        if self.class_var:\n            domain = self.data.domain\n            classless_data = self.data.transform(\n                Domain(domain.attributes, None, domain.metas))\n        else:\n            classless_data = self.data\n\n        for index, slot in enumerate(self.predictors):\n            if isinstance(slot.results, Results):\n                continue\n\n            predictor = slot.predictor\n            try:\n                if predictor.domain.class_var.is_discrete:\n                    pred, prob = predictor(classless_data, Model.ValueProbs)\n                else:\n                    pred = predictor(classless_data, Model.Value)\n                    prob = numpy.zeros((len(pred), 0))\n            except (ValueError, DomainTransformationError) as err:\n                self.predictors[index] = \\\n                    slot._replace(results=f\"{predictor.name}: {err}\")\n                continue\n\n            results = Results()\n            results.data = self.data\n            results.domain = self.data.domain\n            results.row_indices = numpy.arange(len(self.data))\n            results.folds = (Ellipsis, )\n            results.actual = self.data.Y\n            results.unmapped_probabilities = prob\n            results.unmapped_predicted = pred\n            results.probabilities = results.predicted = None\n            self.predictors[index] = slot._replace(results=results)\n\n            target = predictor.domain.class_var\n            if target != self.class_var:\n                continue\n\n            if target is not self.class_var and target.is_discrete:\n                backmappers, n_values = predictor.get_backmappers(self.data)\n                prob = predictor.backmap_probs(prob, n_values, backmappers)\n                pred = predictor.backmap_value(pred, prob, n_values, backmappers)\n            results.predicted = pred.reshape((1, len(self.data)))\n            results.probabilities = prob.reshape((1,) + prob.shape)\n\n    def _update_scores(self):\n        model = self.score_table.model\n        model.clear()\n        scorers = usable_scorers(self.class_var) if self.class_var else []\n        self.score_table.update_header(scorers)\n        errors = []\n        for pred in self.predictors:\n            results = pred.results\n            if not isinstance(results, Results) or results.predicted is None:\n                continue\n            row = [QStandardItem(learner_name(pred.predictor)),\n                   QStandardItem(\"N/A\"), QStandardItem(\"N/A\")]\n\n            actual = results.actual\n            predicted = results.predicted\n            probabilities = results.probabilities\n\n            mask = numpy.isnan(results.actual)\n            no_targets = mask.sum() == len(results.actual)\n            results.actual = results.actual[~mask]\n            results.predicted = results.predicted[:, ~mask]\n            results.probabilities = results.probabilities[:, ~mask]\n\n            for scorer in scorers:\n                item = QStandardItem()\n                if no_targets:\n                    item.setText(\"NA\")\n                else:\n                    try:\n                        score = scorer_caller(scorer, results)()[0]\n                        item.setText(f\"{score:.3f}\")\n                    except Exception as exc:  # pylint: disable=broad-except\n                        item.setToolTip(str(exc))\n                        # false pos.; pylint: disable=unsupported-membership-test\n                        if scorer.name in self.score_table.shown_scores:\n                            errors.append(str(exc))\n                row.append(item)\n            self.score_table.model.appendRow(row)\n\n            results.actual = actual\n            results.predicted = predicted\n            results.probabilities = probabilities\n\n        view = self.score_table.view\n        if model.rowCount():\n            view.setVisible(True)\n            view.ensurePolished()\n            view.setFixedHeight(\n                5 + view.horizontalHeader().height() +\n                view.verticalHeader().sectionSize(0) * model.rowCount())\n        else:\n            view.setVisible(False)\n\n        self.Error.scorer_failed(\"\\n\".join(errors), shown=bool(errors))\n\n    def _set_errors(self):\n        # Not all predictors are run every time, so errors can't be collected\n        # in _call_predictors\n        errors = \"\\n\".join(\n            f\"- {p.predictor.name}: {p.results}\"\n            for p in self.predictors\n            if isinstance(p.results, str) and p.results)\n        self.Error.predictor_failed(errors, shown=bool(errors))\n\n        if self.class_var:\n            inv_targets = \"\\n\".join(\n                f\"- {pred.name} predicts '{pred.domain.class_var.name}'\"\n                for pred in (p.predictor for p in self.predictors\n                             if isinstance(p.results, Results)\n                             and p.results.probabilities is None))\n            self.Warning.wrong_targets(inv_targets, shown=bool(inv_targets))\n\n            show_warning = numpy.isnan(self.data.Y).any() and self.predictors\n            self.Warning.missing_targets(shown=show_warning)\n        else:\n            self.Warning.wrong_targets.clear()\n            self.Warning.missing_targets.clear()\n\n    def _get_details(self):\n        details = \"Data:<br>\"\n        details += format_summary_details(self.data, format=Qt.RichText)\n        details += \"<hr>\"\n        pred_names = [v.name for v in self.predictors]\n        n_predictors = len(self.predictors)\n        if n_predictors:\n            n_valid = len(self._non_errored_predictors())\n            details += plural(\"Model: {number} model{s}\", n_predictors)\n            if n_valid != n_predictors:\n                details += f\" ({n_predictors - n_valid} failed)\"\n            details += \"<ul>\"\n            for name in pred_names:\n                details += f\"<li>{name}</li>\"\n            details += \"</ul>\"\n        else:\n            details += \"Model:<br>No model on input.\"\n        return details\n\n    def _invalidate_predictions(self):\n        for i, pred in enumerate(self.predictors):\n            self.predictors[i] = pred._replace(results=None)\n\n    def _non_errored_predictors(self):\n        return [p for p in self.predictors\n                if isinstance(p.results, Results)]\n\n    def _reordered_probabilities(self, prediction):\n        cur_values = prediction.predictor.domain.class_var.values\n        new_ind = [self.class_values.index(x) for x in cur_values]\n        probs = prediction.results.unmapped_probabilities\n        new_probs = numpy.full(\n            (probs.shape[0], len(self.class_values)), numpy.nan)\n        new_probs[:, new_ind] = probs\n        return new_probs\n\n    def _update_predictions_model(self):\n        headers = []\n        all_values = []\n        all_probs = []\n        for p in self._non_errored_predictors():\n            values = p.results.unmapped_predicted\n            target = p.predictor.domain.class_var\n            if target.is_discrete:\n                # order probabilities in order from Show prob. for\n                prob = self._reordered_probabilities(p)\n                values = numpy.array(target.values)[values.astype(int)]\n            else:\n                prob = numpy.zeros((len(values), 0))\n            all_values.append(values)\n            all_probs.append(prob)\n            headers.append(p.predictor.name)\n\n        if all_values:\n            model = PredictionsModel(all_values, all_probs, headers)\n            model.list_sorted.connect(\n                partial(\n                    self._update_data_sort_order, self.predictionsview,\n                    self.dataview))\n        else:\n            model = None\n\n        if self.selection_store is not None:\n            self.selection_store.unregister(\n                self.predictionsview.selectionModel())\n\n        self.predictionsview.setModel(model)\n        if model is not None:\n            self.predictionsview.setSelectionModel(\n                SharedSelectionModel(self.get_selection_store(model),\n                                     model, self.predictionsview))\n\n        hheader = self.predictionsview.horizontalHeader()\n        hheader.setSortIndicatorShown(False)\n        hheader.setSectionsClickable(True)\n\n    def _update_data_sort_order(self, sort_source_view, sort_dest_view):\n        sort_source_view.horizontalHeader().setSortIndicatorShown(True)\n        sort_dest_view.horizontalHeader().setSortIndicatorShown(False)\n\n        sort_dest = sort_dest_view.model()\n        sort_source = sort_source_view.model()\n        if sort_dest is not None:\n            if sort_source is not None and sort_source.sortColumn() >= 0:\n                sort_dest.setSortIndices(sort_source.mapToSourceRows(...))\n            else:\n                sort_dest.setSortIndices(None)\n        self.commit()\n\n    def _reset_order(self):\n        datamodel = self.dataview.model()\n        predmodel = self.predictionsview.model()\n        if datamodel is not None:\n            datamodel.setSortIndices(None)\n            datamodel.sort(-1)\n        if predmodel is not None:\n            predmodel.setSortIndices(None)\n            predmodel.sort(-1)\n        self.predictionsview.horizontalHeader().setSortIndicatorShown(False)\n        self.dataview.horizontalHeader().setSortIndicatorShown(False)\n\n    def _all_color_values(self):\n        \"\"\"\n        Return list of colors together with their values from all predictors\n        classes. Colors and values are sorted according to the values order\n        for simpler comparison.\n        \"\"\"\n        predictors = self._non_errored_predictors()\n        color_values = [\n            list(zip(*sorted(zip(\n                p.predictor.domain.class_var.colors,\n                p.predictor.domain.class_var.values\n            ), key=itemgetter(1))))\n            for p in predictors if p.predictor.domain.class_var.is_discrete\n        ]\n        return color_values if color_values else [([], [])]\n\n    @staticmethod\n    def _colors_match(colors1, values1, color2, values2):\n        \"\"\"\n        Test whether colors for values match. Colors matches when all\n        values match for shorter list and colors match for shorter list.\n        It is assumed that values will be sorted together with their colors.\n        \"\"\"\n        shorter_length = min(len(colors1), len(color2))\n        return (values1[:shorter_length] == values2[:shorter_length]\n                and (numpy.array(colors1[:shorter_length]) ==\n                     numpy.array(color2[:shorter_length])).all())\n\n    def _get_colors(self):\n        \"\"\"\n        Defines colors for values. If colors match in all models use the union\n        otherwise use standard colors.\n        \"\"\"\n        all_colors_values = self._all_color_values()\n        base_color, base_values = all_colors_values[0]\n        for c, v in all_colors_values[1:]:\n            if not self._colors_match(base_color, base_values, c, v):\n                base_color = []\n                break\n            # replace base_color if longer\n            if len(v) > len(base_color):\n                base_color = c\n                base_values = v\n\n        if len(base_color) != len(self.class_values):\n            return LimitedDiscretePalette(len(self.class_values)).palette\n        # reorder colors to widgets order\n        colors = [None] * len(self.class_values)\n        for c, v in zip(base_color, base_values):\n            colors[self.class_values.index(v)] = c\n        return colors\n\n    def _update_prediction_delegate(self):\n        self._delegates.clear()\n        colors = self._get_colors()\n        for col, slot in enumerate(self.predictors):\n            target = slot.predictor.domain.class_var\n            shown_probs = (\n                () if target.is_continuous else\n                [val if self.class_values[val] in target.values else None\n                 for val in self.selected_classes]\n            )\n            delegate = PredictionsItemDelegate(\n                None if target.is_continuous else self.class_values,\n                colors,\n                shown_probs,\n                target.format_str if target.is_continuous else None,\n                parent=self.predictionsview\n            )\n            # QAbstractItemView does not take ownership of delegates, so we must\n            self._delegates.append(delegate)\n            self.predictionsview.setItemDelegateForColumn(col, delegate)\n            self.predictionsview.setColumnHidden(col, False)\n\n        self.predictionsview.resizeColumnsToContents()\n        self._recompute_splitter_sizes()\n        if self.predictionsview.model() is not None:\n            self.predictionsview.model().setProbInd(self.selected_classes)\n\n    def _recompute_splitter_sizes(self):\n        if not self.data:\n            return\n        view = self.predictionsview\n        self.left_width = \\\n            view.horizontalHeader().length() + view.verticalHeader().width()\n        self._update_splitter()\n\n    def _update_splitter(self):\n        w1, w2 = self.splitter.sizes()\n        self.splitter.setSizes([self.left_width, w1 + w2 - self.left_width])\n\n    def splitter_resized(self):\n        self.left_width = self.splitter.sizes()[0]\n\n    def commit(self):\n        self._commit_predictions()\n        self._commit_evaluation_results()\n\n    def _commit_evaluation_results(self):\n        slots = [p for p in self._non_errored_predictors()\n                 if p.results.predicted is not None]\n        if not slots:\n            self.Outputs.evaluation_results.send(None)\n            return\n\n        nanmask = numpy.isnan(self.data.get_column_view(self.class_var)[0])\n        data = self.data[~nanmask]\n        results = Results(data, store_data=True)\n        results.folds = None\n        results.row_indices = numpy.arange(len(data))\n        results.actual = data.Y.ravel()\n        results.predicted = numpy.vstack(\n            tuple(p.results.predicted[0][~nanmask] for p in slots))\n        if self.class_var and self.class_var.is_discrete:\n            results.probabilities = numpy.array(\n                [p.results.probabilities[0][~nanmask] for p in slots])\n        results.learner_names = [p.name for p in slots]\n        self.Outputs.evaluation_results.send(results)\n\n    def _commit_predictions(self):\n        if not self.data:\n            self.Outputs.predictions.send(None)\n            return\n\n        newmetas = []\n        newcolumns = []\n        for slot in self._non_errored_predictors():\n            if slot.predictor.domain.class_var.is_discrete:\n                self._add_classification_out_columns(slot, newmetas, newcolumns)\n            else:\n                self._add_regression_out_columns(slot, newmetas, newcolumns)\n\n        attrs = list(self.data.domain.attributes)\n        metas = list(self.data.domain.metas)\n        names = [var.name for var in chain(attrs, self.data.domain.class_vars, metas) if var]\n        uniq_newmetas = []\n        for new_ in newmetas:\n            uniq = get_unique_names(names, new_.name)\n            if uniq != new_.name:\n                new_ = new_.copy(name=uniq)\n            uniq_newmetas.append(new_)\n            names.append(uniq)\n\n        metas += uniq_newmetas\n        domain = Orange.data.Domain(attrs, self.class_var, metas=metas)\n        predictions = self.data.transform(domain)\n        if newcolumns:\n            newcolumns = numpy.hstack(\n                [numpy.atleast_2d(cols) for cols in newcolumns])\n            with predictions.unlocked(predictions.metas):\n                predictions.metas[:, -newcolumns.shape[1]:] = newcolumns\n\n        datamodel = self.dataview.model()\n        predmodel = self.predictionsview.model()\n        assert datamodel is not None  # because we have data\n        assert self.selection_store is not None\n        rows = numpy.array(list(self.selection_store.rows))\n        if rows.size:\n            # Reorder rows as they are ordered in view\n            shown_rows = datamodel.mapFromSourceRows(rows)\n            rows = rows[numpy.argsort(shown_rows)]\n            predictions = predictions[rows]\n        elif datamodel.sortColumn() >= 0 \\\n                or predmodel is not None and predmodel.sortColumn() > 0:\n            # No selection: output all, but in the shown order\n            predictions = predictions[datamodel.mapToSourceRows(...)]\n        self.Outputs.predictions.send(predictions)\n\n    @staticmethod\n    def _add_classification_out_columns(slot, newmetas, newcolumns):\n        # Mapped or unmapped predictions?!\n        # Or provide a checkbox so the user decides?\n        pred = slot.predictor\n        name = pred.name\n        values = pred.domain.class_var.values\n        newmetas.append(DiscreteVariable(name=name, values=values))\n        newcolumns.append(slot.results.unmapped_predicted.reshape(-1, 1))\n        newmetas += [ContinuousVariable(name=f\"{name} ({value})\")\n                     for value in values]\n        newcolumns.append(slot.results.unmapped_probabilities)\n\n    @staticmethod\n    def _add_regression_out_columns(slot, newmetas, newcolumns):\n        newmetas.append(ContinuousVariable(name=slot.predictor.name))\n        newcolumns.append(slot.results.unmapped_predicted.reshape((-1, 1)))\n\n    def send_report(self):\n        def merge_data_with_predictions():\n            data_model = self.dataview.model()\n            predictions_view = self.predictionsview\n            predictions_model = predictions_view.model()\n\n            # use ItemDelegate to style prediction values\n            delegates = [predictions_view.itemDelegateForColumn(i)\n                         for i in range(predictions_model.columnCount())]\n\n            # iterate only over visible columns of data's QTableView\n            iter_data_cols = list(filter(lambda x: not self.dataview.isColumnHidden(x),\n                                         range(data_model.columnCount())))\n\n            # print header\n            yield [''] + \\\n                  [predictions_model.headerData(col, Qt.Horizontal, Qt.DisplayRole)\n                   for col in range(predictions_model.columnCount())] + \\\n                  [data_model.headerData(col, Qt.Horizontal, Qt.DisplayRole)\n                   for col in iter_data_cols]\n\n            # print data & predictions\n            for i in range(data_model.rowCount()):\n                yield [data_model.headerData(i, Qt.Vertical, Qt.DisplayRole)] + \\\n                      [delegate.displayText(\n                          predictions_model.data(predictions_model.index(i, j)),\n                          QLocale())\n                       for j, delegate in enumerate(delegates)] + \\\n                      [data_model.data(data_model.index(i, j),\n                                       role=Qt.DisplayRole)\n                       for j in iter_data_cols]\n\n        if self.data:\n            text = self._get_details().replace('\\n', '<br>')\n            if self.selected_classes:\n                text += '<br>Showing probabilities for: '\n                text += ', '. join([self.class_values[i]\n                                    for i in self.selected_classes])\n            self.report_paragraph('Info', text)\n            self.report_table(\"Data & Predictions\", merge_data_with_predictions(),\n                              header_rows=1, header_columns=1)\n\n            self.report_table(\"Scores\", self.score_table.view)\n\n    def resizeEvent(self, event):\n        super().resizeEvent(event)\n        self._update_splitter()\n\n    def showEvent(self, event):\n        super().showEvent(event)\n        QTimer.singleShot(0, self._update_splitter)\n\n\nclass ItemDelegate(TableDataDelegate):\n    def initStyleOption(self, option, index):\n        super().initStyleOption(option, index)\n        if self.parent().selectionModel().isSelected(index):\n            option.state |= QStyle.State_Selected\n        if self.parent().window().isActiveWindow():\n            option.state |= QStyle.State_Active | QStyle.State_HasFocus\n\n\nclass DataItemDelegate(ItemDelegate):\n    pass\n\n\nclass PredictionsItemDelegate(ItemDelegate):\n    \"\"\"\n    A Item Delegate for custom formatting of predictions/probabilities\n    \"\"\"\n    #: Roles supplied by the `PredictionsModel`\n    DefaultRoles = (Qt.DisplayRole, )\n\n    def __init__(\n            self, class_values, colors, shown_probabilities=(),\n            target_format=None, parent=None,\n    ):\n        super().__init__(parent)\n        self.class_values = class_values  # will be None for continuous\n        self.colors = [QColor(*c) for c in colors]\n        self.target_format = target_format  # target format for cont. vars\n        self.shown_probabilities = self.fmt = self.tooltip = None  # set below\n        self.setFormat(shown_probabilities)\n\n    def setFormat(self, shown_probabilities=()):\n        self.shown_probabilities = shown_probabilities\n        if self.class_values is None:\n            # is continuous class\n            self.fmt = f\"{{value:{self.target_format[1:]}}}\"\n        else:\n            self.fmt = \" \\N{RIGHTWARDS ARROW} \".join(\n                [\" : \".join(f\"{{dist[{i}]:.2f}}\" if i is not None else \"-\"\n                            for i in shown_probabilities)]\n                * bool(shown_probabilities)\n                + [\"{value!s}\"])\n        self.tooltip = \"\"\n        if shown_probabilities:\n            val = ', '.join(\n                self.class_values[i] if i is not None else \"-\"\n                for i in shown_probabilities if i is not None\n            )\n            self.tooltip = f\"p({val})\"\n\n    def displayText(self, value, _):\n        try:\n            value, dist = value\n        except ValueError:\n            return \"\"\n        else:\n            return self.fmt.format(value=value, dist=dist)\n\n    def helpEvent(self, event, view, option, index):\n        if self.tooltip is not None:\n            # ... but can be an empty string, so the current tooltip is removed\n            QToolTip.showText(event.globalPos(), self.tooltip, view)\n            return True\n        else:\n            return super().helpEvent(event, view, option, index)\n\n    def initStyleOption(self, option, index):\n        super().initStyleOption(option, index)\n        if self.class_values is None:\n            option.displayAlignment = \\\n                (option.displayAlignment & Qt.AlignVertical_Mask) | \\\n                Qt.AlignRight\n\n    def sizeHint(self, option, index):\n        # reimplemented\n        sh = super().sizeHint(option, index)\n        if option.widget is not None:\n            style = option.widget.style()\n        else:\n            style = QApplication.style()\n        margin = style.pixelMetric(\n            QStyle.PM_FocusFrameHMargin, option, option.widget) + 1\n        metrics = option.fontMetrics\n        height = sh.height() + metrics.leading() + 2 * margin\n        return QSize(sh.width(), height)\n\n    def distribution(self, index):\n        value = self.cachedData(index, Qt.DisplayRole)\n        if isinstance(value, tuple) and len(value) == 2:\n            _, dist = value\n            return dist\n        else:\n            return None\n\n    def paint(self, painter, option, index):\n        dist = self.distribution(index)\n        if dist is None or self.colors is None:\n            super().paint(painter, option, index)\n            return\n\n        if option.widget is not None:\n            style = option.widget.style()\n        else:\n            style = QApplication.style()\n\n        self.initStyleOption(option, index)\n\n        text = option.text\n        metrics = option.fontMetrics\n\n        margin = style.pixelMetric(\n            QStyle.PM_FocusFrameHMargin, option, option.widget) + 1\n        bottommargin = min(margin, 1)\n        rect = option.rect.adjusted(margin, margin, -margin, -bottommargin)\n        option.text = \"\"\n        textrect = style.subElementRect(\n            QStyle.SE_ItemViewItemText, option, option.widget)\n        # Are the margins included in the subElementRect?? -> No!\n        textrect = textrect.adjusted(margin, margin, -margin, -bottommargin)\n        spacing = max(metrics.leading(), 1)\n\n        distheight = rect.height() - metrics.height() - spacing\n        distheight = min(max(distheight, 2), metrics.height())\n        painter.save()\n        painter.setClipRect(option.rect)\n        painter.setFont(option.font)\n        painter.setRenderHint(QPainter.Antialiasing)\n\n        style.drawPrimitive(\n            QStyle.PE_PanelItemViewRow, option, painter, option.widget)\n        style.drawPrimitive(\n            QStyle.PE_PanelItemViewItem, option, painter, option.widget)\n\n        textrect = textrect.adjusted(0, 0, 0, -distheight - spacing)\n        distrect = QRect(\n            textrect.bottomLeft() + QPoint(0, spacing),\n            QSize(rect.width(), distheight))\n        painter.setPen(QPen(Qt.lightGray, 0.3))\n        self.drawDistBar(painter, distrect, dist)\n        painter.restore()\n        if text:\n            option.text = text\n            self.drawViewItemText(style, painter, option, textrect)\n\n    def drawDistBar(self, painter, rect, distribution):\n        painter.save()\n        painter.translate(rect.topLeft())\n        for i in self.shown_probabilities:\n            if i is None:\n                continue\n            dvalue = distribution[i]\n            if not dvalue > 0:  # This also skips nans\n                continue\n            painter.setBrush(self.colors[i])\n            width = rect.width() * dvalue\n            painter.drawRoundedRect(QRectF(0, 0, width, rect.height()), 1, 2)\n            painter.translate(width, 0.0)\n        painter.restore()\n\n\nclass PredictionsModel(AbstractSortTableModel):\n    list_sorted = pyqtSignal()\n\n    def __init__(self, values=None, probs=None, headers=None, parent=None):\n        super().__init__(parent)\n        self._values = values\n        self._probs = probs\n        self.__probInd = None\n        if values is not None:\n            assert len(self._values) == len(self._probs) != 0\n            sizes = {len(x) for c in (values, probs) for x in c}\n            assert len(sizes) == 1\n            self.__columnCount = len(values)\n            self.__rowCount = sizes.pop()\n            if headers is None:\n                headers = [None] * self.__columnCount\n        else:\n            assert probs is None\n            assert headers is None\n            self.__columnCount = self.__rowCount = 0\n        self._header = headers\n\n    def rowCount(self, parent=QModelIndex()):\n        return 0 if parent.isValid() else self.__rowCount\n\n    def columnCount(self, parent=QModelIndex()):\n        return 0 if parent.isValid() else self.__columnCount\n\n    def data(self, index, role=Qt.DisplayRole):\n        if role in (Qt.DisplayRole, Qt.EditRole):\n            column = index.column()\n            row = self.mapToSourceRows(index.row())\n            return self._values[column][row], self._probs[column][row]\n        return None\n\n    def headerData(self, section, orientation, role=Qt.DisplayRole):\n        if role == Qt.DisplayRole:\n            if orientation == Qt.Vertical:\n                return str(section + 1)\n            elif self._header is not None and section < len(self._header):\n                return self._header[section]\n        return None\n\n    def setProbInd(self, indices):\n        self.__probInd = indices\n        self.sort(self.sortColumn())\n\n    def sortColumnData(self, column):\n        values = self._values[column]\n        probs = self._probs[column]\n        # Let us assume that probs can be None, numpy array or list of arrays\n        if probs is not None and len(probs) and len(probs[0]) \\\n                and self.__probInd is not None and len(self.__probInd):\n            return probs[:, self.__probInd]\n        else:\n            return values\n\n    def sort(self, column, order=Qt.AscendingOrder):\n        super().sort(column, order)\n        self.list_sorted.emit()\n\n\n# PredictionsModel and DataModel have the same signal and sort method, but\n# extracting it into a common base class would require diamond inheritance\n# because they're derived from different classes. It's not worth it.\nclass DataModel(TableModel):\n    list_sorted = pyqtSignal()\n\n    def sort(self, column, order=Qt.AscendingOrder):\n        super().sort(column, order)\n        self.list_sorted.emit()\n\n\nclass SharedSelectionStore:\n    \"\"\"\n    An object shared between multiple selection models\n\n    The object assumes that the underlying models are AbstractSortTableModel.\n    Internally, the object stores indices of unmapped, source rows (as int).\n\n    The class implements method `select` with the same signature as\n    QItemSelectionModel.select. Selection models that share this object\n    must call this method. After changing the selection, this method will\n    call `emit_selection_rows_changed` of all selection models, so they\n    can emit the signal selectionChanged.\n    \"\"\"\n    def __init__(self, model):\n        # unmapped indices of selected rows\n        self._rows: Set[int] = set()\n        self.model: AbstractSortTableModel = model\n        self._selection_models: List[SharedSelectionModel] = []\n\n    @property\n    def rows(self) -> Set[int]:\n        \"\"\"Indices of selected rows in the source model\"\"\"\n        return self._rows\n\n    def register(self, selection_model):\n        \"\"\"\n        Add a selection mode to the list of models\n\n        Args:\n            selection_model (SharedSelectionModel): a new model\n        \"\"\"\n        self._selection_models.append(selection_model)\n\n    def unregister(self, selection_model):\n        \"\"\"\n        Remove a selection mode to the list of models\n\n        Args:\n            selection_model (SharedSelectionModel): a model to remove\n        \"\"\"\n        if selection_model in self._selection_models:\n            self._selection_models.remove(selection_model)\n\n    def select(self, selection: Union[QModelIndex, QItemSelection], flags: int):\n        \"\"\"\n        (De)Select given rows\n\n        Args:\n            selection (QModelIndex or QItemSelection):\n                rows to select; indices are mapped to rows in the view\n            flags (QItemSelectionModel.SelectionFlags):\n                flags that tell whether to Clear, Select, Deselect or Toggle\n        \"\"\"\n        rows = set()\n        if isinstance(selection, QModelIndex):\n            if selection.model() is not None:\n                rows = {selection.model().mapToSourceRows(selection.row())}\n        else:\n            indices = selection.indexes()\n            if indices:\n                map_to = indices[0].model().mapToSourceRows\n                rows = set(map_to([index.row() for index in indices]))\n        self.select_rows(rows, flags)\n\n    def select_rows(self, rows: Set[int], flags):\n        \"\"\"\n        (De)Select given rows\n\n        Args:\n            selection (set of int):\n                rows to select; indices refer to unmapped rows in model, not view\n            flags (QItemSelectionModel.SelectionFlags):\n                flags that tell whether to Clear, Select, Deselect or Toggle\n        \"\"\"\n        with self._emit_changed():\n            if flags & QItemSelectionModel.Clear:\n                self._rows.clear()\n            if flags & QItemSelectionModel.Select:\n                self._rows |= rows\n            if flags & QItemSelectionModel.Deselect:\n                self._rows -= rows\n            if flags & QItemSelectionModel.Toggle:\n                self._rows ^= rows\n\n    def clear_selection(self):\n        \"\"\"Clear selection and emit changeSelection signal to all models\"\"\"\n        with self._emit_changed():\n            self._rows.clear()\n\n    def reset(self):\n        \"\"\"Clear selection without emiting a signal,\"\"\"\n        self._rows.clear()\n\n    @contextmanager\n    def _emit_changed(self):\n        \"\"\"\n        A context manager that calls `emit_selection_rows_changed after\n        changing a selection.\n        \"\"\"\n        def map_from_source(rows):\n            return self.model.mapFromSourceRows(list(rows))\n\n        old_rows = self._rows.copy()\n        try:\n            yield\n        finally:\n            if self.model.rowCount() != 0:\n                deselected = map_from_source(old_rows - self._rows)\n                selected = map_from_source(self._rows - old_rows)\n                if len(selected) != 0 or len(deselected) != 0:\n                    for model in self._selection_models:\n                        model.emit_selection_rows_changed(selected, deselected)\n\n\nclass SharedSelectionModel(QItemSelectionModel):\n    \"\"\"\n    A selection model that shares the selection with its peers.\n\n    It assumes that the underlying model is a AbstractTableModel.\n    \"\"\"\n    def __init__(self, shared_store, model, parent):\n        super().__init__(model, parent)\n        self.store: SharedSelectionStore = shared_store\n        self.store.register(self)\n\n    def select(self, selection, flags):\n        self.store.select(selection, flags)\n\n    def selection_from_rows(self, rows: Sequence[int]) -> QItemSelection:\n        \"\"\"\n        Return selection across all columns for given row indices (as ints)\n\n        Args:\n            rows (sequence of int): row indices, as shown in the view, not model\n\n        Returns: QItemSelection\n        \"\"\"\n        model = self.model()\n        index = model.index\n        last_col = model.columnCount() - 1\n        sel = QItemSelection()\n        for row in rows:\n            sel.select(index(row, 0), index(row, last_col))\n        return sel\n\n    def emit_selection_rows_changed(\n            self, selected: Sequence[int], deselected: Sequence[int]):\n        \"\"\"\n        Given a sequence of indices of selected and deselected rows,\n        emit a selectionChanged signal.\n\n        Args:\n            selected (Sequence[int]): indices of selected rows\n            deselected (Sequence[int]): indices of deselected rows\n        \"\"\"\n        self.selectionChanged.emit(\n            self.selection_from_rows(selected),\n            self.selection_from_rows(deselected))\n\n    def selection(self):\n        rows = self.model().mapFromSourceRows(list(self.store.rows))\n        return self.selection_from_rows(rows)\n\n    def hasSelection(self) -> bool:\n        return bool(self.store.rows)\n\n    def isColumnSelected(self, *_) -> bool:\n        return len(self.store.rows) == self.model().rowCount()\n\n    def isRowSelected(self, row, _parent=None) -> bool:\n        mapped_row = self.model().mapToSourceRows(row)\n        return mapped_row in self.store.rows\n\n    rowIntersectsSelection = isRowSelected\n\n    def isSelected(self, index) -> bool:\n        return self.model().mapToSourceRows(index.row()) in self.store.rows\n\n    def selectedColumns(self, row: int):\n        if self.isColumnSelected():\n            index = self.model().index\n            return [index(row, col)\n                    for col in range(self.model().columnCount())]\n        else:\n            return []\n\n    def _selected_rows_arr(self):\n        return numpy.fromiter(self.store.rows, int, len(self.store.rows))\n\n    def selectedRows(self, col: int):\n        index = self.model().index\n        rows = self.model().mapFromSourceRows(self._selected_rows_arr())\n        return [index(row, col) for row in rows]\n\n    def selectedIndexes(self):\n        index = self.model().index\n        rows = self.model().mapFromSourceRows(self._selected_rows_arr())\n        return [index(row, col)\n                for col in range(self.model().columnCount())\n                for row in rows]\n\n    def clearSelection(self):\n        self.store.clear_selection()\n\n    def reset(self):\n        self.store.reset()\n        self.clearCurrentIndex()\n\n\nclass TableView(gui.HScrollStepMixin, QTableView):\n    MaxSizeHintSamples = 1000\n\n    def sizeHintForColumn(self, column):\n        \"\"\"\n        Reimplemented from `QTableView.sizeHintForColumn`\n\n        Note: This does not match the QTableView's implementation,\n        in particular size hints from editor/index widgets are not taken\n        into account.\n\n        Parameters\n        ----------\n        column : int\n        \"\"\"\n        # This is probably not needed in Qt5?\n        if self.model() is None:\n            return -1\n\n        self.ensurePolished()\n        model = self.model()\n        vheader = self.verticalHeader()\n        top = vheader.visualIndexAt(0)\n        bottom = vheader.visualIndexAt(self.viewport().height())\n        if bottom < 0:\n            bottom = self.model().rowCount()\n\n        options = self.viewOptions()\n        options.widget = self\n\n        width = 0\n        sample_count = 0\n\n        for row in range(top, bottom):\n            if not vheader.isSectionHidden(vheader.logicalIndex(row)):\n                index = model.index(row, column)\n                delegate = self.itemDelegate(index)\n                if not delegate:\n                    continue\n                size = delegate.sizeHint(options, index)\n                width = max(size.width(), width)\n                sample_count += 1\n\n            if sample_count >= TableView.MaxSizeHintSamples:\n                break\n\n        return width + 1 if self.showGrid() else width\n\n\ndef tool_tip(value):\n    value, dist = value\n    if dist is not None:\n        return \"{!s} {!s}\".format(value, dist)\n    else:\n        return str(value)\n\n\nif __name__ == \"__main__\":  # pragma: no cover\n    filename = \"iris.tab\"\n    iris = Orange.data.Table(filename)\n    iris2 = iris[:100]\n\n    def pred_error(data, *args, **kwargs):\n        raise ValueError\n\n    pred_error.domain = iris.domain\n    pred_error.name = \"To err is human\"\n\n    if iris.domain.has_discrete_class:\n        idom = iris.domain\n        dom = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[1::-1])\n        )\n        iris2 = iris2.transform(dom)\n        predictors_ = [\n            Orange.classification.SVMLearner(probability=True)(iris2),\n            Orange.classification.LogisticRegressionLearner()(iris),\n            pred_error\n        ]\n    elif iris.domain.has_continuous_class:\n        predictors_ = [\n            Orange.regression.RidgeRegressionLearner(alpha=1.0)(iris),\n            Orange.regression.LinearRegressionLearner()(iris),\n            pred_error\n        ]\n    else:\n        predictors_ = [pred_error]\n\n    WidgetPreview(OWPredictions).run(\n        set_data=iris2,\n        insert_predictor=list(enumerate(predictors_)))\n",
            "header": {
                "file": {
                    "path": "Orange/widgets/evaluate/owpredictions.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "470c92b80",
                "new_commit_hash": "99b6df1bd",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/Orange/widgets/evaluate/owpredictions.py"
            },
            "plus_file": {
                "path": "b/Orange/widgets/evaluate/owpredictions.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 65,
                            "length": 6
                        },
                        "new_range": {
                            "start": 65,
                            "length": 8
                        },
                        "section": "class OWPredictions(OWWidget):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        empty_data = Msg(\"Empty dataset\")",
                                "type": "context"
                            },
                            {
                                "content": "        wrong_targets = Msg(",
                                "type": "context"
                            },
                            {
                                "content": "            \"Some model(s) predict a different target (see more ...)\\n{}\")",
                                "type": "context"
                            },
                            {
                                "content": "        missing_targets = Msg(\"Instances with missing targets \"",
                                "type": "added"
                            },
                            {
                                "content": "                              \"are ignored while scoring.\")",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    class Error(OWWidget.Error):",
                                "type": "context"
                            },
                            {
                                "content": "        predictor_failed = Msg(\"Some predictor(s) failed (see more ...)\\n{}\")",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "Orange/widgets/evaluate/owpredictions.py",
                            "type": "class",
                            "name": "OWPredictions",
                            "content": "class OWPredictions(OWWidget):\n    name = \"Predictions\"\n    icon = \"icons/Predictions.svg\"\n    priority = 200\n    description = \"Display predictions of models for an input dataset.\"\n    keywords = []\n\n    buttons_area_orientation = None\n\n    class Inputs:\n        data = Input(\"Data\", Orange.data.Table)\n        predictors = MultiInput(\"Predictors\", Model, filter_none=True)\n\n    class Outputs:\n        predictions = Output(\"Predictions\", Orange.data.Table)\n        evaluation_results = Output(\"Evaluation Results\", Results)\n\n    class Warning(OWWidget.Warning):\n        empty_data = Msg(\"Empty dataset\")\n        wrong_targets = Msg(\n            \"Some model(s) predict a different target (see more ...)\\n{}\")\n        missing_targets = Msg(\"Instances with missing targets \"\n                              \"are ignored while scoring.\")\n\n    class Error(OWWidget.Error):\n        predictor_failed = Msg(\"Some predictor(s) failed (see more ...)\\n{}\")\n        scorer_failed = Msg(\"Some scorer(s) failed (see more ...)\\n{}\")\n\n    settingsHandler = settings.ClassValuesContextHandler()\n    score_table = settings.SettingProvider(ScoreTable)\n\n    #: List of selected class value indices in the `class_values` list\n    selected_classes = settings.ContextSetting([])\n    selection = settings.Setting([], schema_only=True)\n\n    def __init__(self):\n        super().__init__()\n\n        self.data = None  # type: Optional[Orange.data.Table]\n        self.predictors = []  # type: List[PredictorSlot]\n        self.class_values = []  # type: List[str]\n        self._delegates = []\n        self.left_width = 10\n        self.selection_store = None\n        self.__pending_selection = self.selection\n\n        controlBox = gui.vBox(self.controlArea, \"Show probabilities for\")\n\n        gui.listBox(controlBox, self, \"selected_classes\", \"class_values\",\n                    callback=self._update_prediction_delegate,\n                    selectionMode=QListWidget.ExtendedSelection,\n                    sizePolicy=(QSizePolicy.Preferred, QSizePolicy.MinimumExpanding),\n                    sizeHint=QSize(1, 350),\n                    minimumHeight=100)\n        self.reset_button = gui.button(\n            controlBox, self, \"Restore Original Order\",\n            callback=self._reset_order,\n            tooltip=\"Show rows in the original order\")\n\n        table_opts = dict(horizontalScrollBarPolicy=Qt.ScrollBarAlwaysOn,\n                          horizontalScrollMode=QTableView.ScrollPerPixel,\n                          selectionMode=QTableView.ExtendedSelection,\n                          focusPolicy=Qt.StrongFocus)\n        self.dataview = TableView(\n            sortingEnabled=True,\n            verticalScrollBarPolicy=Qt.ScrollBarAlwaysOn,\n            **table_opts)\n        self.predictionsview = TableView(\n            sortingEnabled=True,\n            verticalScrollBarPolicy=Qt.ScrollBarAlwaysOff,\n            **table_opts)\n        self.dataview.verticalHeader().hide()\n        dsbar = self.dataview.verticalScrollBar()\n        psbar = self.predictionsview.verticalScrollBar()\n        psbar.valueChanged.connect(dsbar.setValue)\n        dsbar.valueChanged.connect(psbar.setValue)\n\n        self.dataview.verticalHeader().setDefaultSectionSize(22)\n        self.predictionsview.verticalHeader().setDefaultSectionSize(22)\n        self.dataview.verticalHeader().sectionResized.connect(\n            lambda index, _, size:\n            self.predictionsview.verticalHeader().resizeSection(index, size))\n\n        self.dataview.setItemDelegate(DataItemDelegate(self.dataview))\n\n        self.splitter = QSplitter(\n            orientation=Qt.Horizontal, childrenCollapsible=False, handleWidth=2)\n        self.splitter.splitterMoved.connect(self.splitter_resized)\n        self.splitter.addWidget(self.predictionsview)\n        self.splitter.addWidget(self.dataview)\n\n        self.score_table = ScoreTable(self)\n        self.vsplitter = gui.vBox(self.mainArea)\n        self.vsplitter.layout().addWidget(self.splitter)\n        self.vsplitter.layout().addWidget(self.score_table.view)\n\n    def get_selection_store(self, model):\n        # Both models map the same, so it doesn't matter which one is used\n        # to initialize SharedSelectionStore\n        if self.selection_store is None:\n            self.selection_store = SharedSelectionStore(model)\n        return self.selection_store\n\n    @Inputs.data\n    @check_sql_input\n    def set_data(self, data):\n        self.Warning.empty_data(shown=data is not None and not data)\n        self.data = data\n        self.selection_store = None\n        if not data:\n            self.dataview.setModel(None)\n            self.predictionsview.setModel(None)\n        else:\n            # force full reset of the view's HeaderView state\n            self.dataview.setModel(None)\n            model = DataModel(data, parent=None)\n            self.dataview.setModel(model)\n            sel_model = SharedSelectionModel(\n                self.get_selection_store(model), model, self.dataview)\n            self.dataview.setSelectionModel(sel_model)\n            if self.__pending_selection is not None:\n                self.selection = self.__pending_selection\n                self.__pending_selection = None\n                self.selection_store.select_rows(\n                    set(self.selection), QItemSelectionModel.ClearAndSelect)\n            sel_model.selectionChanged.connect(self.commit)\n            sel_model.selectionChanged.connect(self._store_selection)\n\n            self.dataview.model().list_sorted.connect(\n                partial(\n                    self._update_data_sort_order, self.dataview,\n                    self.predictionsview))\n\n        self._invalidate_predictions()\n\n    def _store_selection(self):\n        self.selection = list(self.selection_store.rows)\n\n    @property\n    def class_var(self):\n        return self.data and self.data.domain.class_var\n\n    @Inputs.predictors\n    def set_predictor(self, index, predictor: Model):\n        item = self.predictors[index]\n        self.predictors[index] = item._replace(\n            predictor=predictor, name=predictor.name, results=None\n        )\n\n    @Inputs.predictors.insert\n    def insert_predictor(self, index, predictor: Model):\n        item = PredictorSlot(predictor, predictor.name, None)\n        self.predictors.insert(index, item)\n\n    @Inputs.predictors.remove\n    def remove_predictor(self, index):\n        self.predictors.pop(index)\n\n    def _set_class_values(self):\n        class_values = []\n        for slot in self.predictors:\n            class_var = slot.predictor.domain.class_var\n            if class_var and class_var.is_discrete:\n                for value in class_var.values:\n                    if value not in class_values:\n                        class_values.append(value)\n\n        if self.class_var and self.class_var.is_discrete:\n            values = self.class_var.values\n            self.class_values = sorted(\n                class_values, key=lambda val: val not in values)\n            self.selected_classes = [\n                i for i, name in enumerate(class_values) if name in values]\n        else:\n            self.class_values = class_values  # This assignment updates listview\n            self.selected_classes = []\n\n    def handleNewSignals(self):\n        # Disconnect the model: the model and the delegate will be inconsistent\n        # between _set_class_values and update_predictions_model.\n        self.predictionsview.setModel(None)\n        self._set_class_values()\n        self._call_predictors()\n        self._update_scores()\n        self._update_predictions_model()\n        self._update_prediction_delegate()\n        self._set_errors()\n        self.commit()\n\n    def _call_predictors(self):\n        if not self.data:\n            return\n        if self.class_var:\n            domain = self.data.domain\n            classless_data = self.data.transform(\n                Domain(domain.attributes, None, domain.metas))\n        else:\n            classless_data = self.data\n\n        for index, slot in enumerate(self.predictors):\n            if isinstance(slot.results, Results):\n                continue\n\n            predictor = slot.predictor\n            try:\n                if predictor.domain.class_var.is_discrete:\n                    pred, prob = predictor(classless_data, Model.ValueProbs)\n                else:\n                    pred = predictor(classless_data, Model.Value)\n                    prob = numpy.zeros((len(pred), 0))\n            except (ValueError, DomainTransformationError) as err:\n                self.predictors[index] = \\\n                    slot._replace(results=f\"{predictor.name}: {err}\")\n                continue\n\n            results = Results()\n            results.data = self.data\n            results.domain = self.data.domain\n            results.row_indices = numpy.arange(len(self.data))\n            results.folds = (Ellipsis, )\n            results.actual = self.data.Y\n            results.unmapped_probabilities = prob\n            results.unmapped_predicted = pred\n            results.probabilities = results.predicted = None\n            self.predictors[index] = slot._replace(results=results)\n\n            target = predictor.domain.class_var\n            if target != self.class_var:\n                continue\n\n            if target is not self.class_var and target.is_discrete:\n                backmappers, n_values = predictor.get_backmappers(self.data)\n                prob = predictor.backmap_probs(prob, n_values, backmappers)\n                pred = predictor.backmap_value(pred, prob, n_values, backmappers)\n            results.predicted = pred.reshape((1, len(self.data)))\n            results.probabilities = prob.reshape((1,) + prob.shape)\n\n    def _update_scores(self):\n        model = self.score_table.model\n        model.clear()\n        scorers = usable_scorers(self.class_var) if self.class_var else []\n        self.score_table.update_header(scorers)\n        errors = []\n        for pred in self.predictors:\n            results = pred.results\n            if not isinstance(results, Results) or results.predicted is None:\n                continue\n            row = [QStandardItem(learner_name(pred.predictor)),\n                   QStandardItem(\"N/A\"), QStandardItem(\"N/A\")]\n\n            actual = results.actual\n            predicted = results.predicted\n            probabilities = results.probabilities\n\n            mask = numpy.isnan(results.actual)\n            no_targets = mask.sum() == len(results.actual)\n            results.actual = results.actual[~mask]\n            results.predicted = results.predicted[:, ~mask]\n            results.probabilities = results.probabilities[:, ~mask]\n\n            for scorer in scorers:\n                item = QStandardItem()\n                if no_targets:\n                    item.setText(\"NA\")\n                else:\n                    try:\n                        score = scorer_caller(scorer, results)()[0]\n                        item.setText(f\"{score:.3f}\")\n                    except Exception as exc:  # pylint: disable=broad-except\n                        item.setToolTip(str(exc))\n                        # false pos.; pylint: disable=unsupported-membership-test\n                        if scorer.name in self.score_table.shown_scores:\n                            errors.append(str(exc))\n                row.append(item)\n            self.score_table.model.appendRow(row)\n\n            results.actual = actual\n            results.predicted = predicted\n            results.probabilities = probabilities\n\n        view = self.score_table.view\n        if model.rowCount():\n            view.setVisible(True)\n            view.ensurePolished()\n            view.setFixedHeight(\n                5 + view.horizontalHeader().height() +\n                view.verticalHeader().sectionSize(0) * model.rowCount())\n        else:\n            view.setVisible(False)\n\n        self.Error.scorer_failed(\"\\n\".join(errors), shown=bool(errors))\n\n    def _set_errors(self):\n        # Not all predictors are run every time, so errors can't be collected\n        # in _call_predictors\n        errors = \"\\n\".join(\n            f\"- {p.predictor.name}: {p.results}\"\n            for p in self.predictors\n            if isinstance(p.results, str) and p.results)\n        self.Error.predictor_failed(errors, shown=bool(errors))\n\n        if self.class_var:\n            inv_targets = \"\\n\".join(\n                f\"- {pred.name} predicts '{pred.domain.class_var.name}'\"\n                for pred in (p.predictor for p in self.predictors\n                             if isinstance(p.results, Results)\n                             and p.results.probabilities is None))\n            self.Warning.wrong_targets(inv_targets, shown=bool(inv_targets))\n\n            show_warning = numpy.isnan(self.data.Y).any() and self.predictors\n            self.Warning.missing_targets(shown=show_warning)\n        else:\n            self.Warning.wrong_targets.clear()\n            self.Warning.missing_targets.clear()\n\n    def _get_details(self):\n        details = \"Data:<br>\"\n        details += format_summary_details(self.data, format=Qt.RichText)\n        details += \"<hr>\"\n        pred_names = [v.name for v in self.predictors]\n        n_predictors = len(self.predictors)\n        if n_predictors:\n            n_valid = len(self._non_errored_predictors())\n            details += plural(\"Model: {number} model{s}\", n_predictors)\n            if n_valid != n_predictors:\n                details += f\" ({n_predictors - n_valid} failed)\"\n            details += \"<ul>\"\n            for name in pred_names:\n                details += f\"<li>{name}</li>\"\n            details += \"</ul>\"\n        else:\n            details += \"Model:<br>No model on input.\"\n        return details\n\n    def _invalidate_predictions(self):\n        for i, pred in enumerate(self.predictors):\n            self.predictors[i] = pred._replace(results=None)\n\n    def _non_errored_predictors(self):\n        return [p for p in self.predictors\n                if isinstance(p.results, Results)]\n\n    def _reordered_probabilities(self, prediction):\n        cur_values = prediction.predictor.domain.class_var.values\n        new_ind = [self.class_values.index(x) for x in cur_values]\n        probs = prediction.results.unmapped_probabilities\n        new_probs = numpy.full(\n            (probs.shape[0], len(self.class_values)), numpy.nan)\n        new_probs[:, new_ind] = probs\n        return new_probs\n\n    def _update_predictions_model(self):\n        headers = []\n        all_values = []\n        all_probs = []\n        for p in self._non_errored_predictors():\n            values = p.results.unmapped_predicted\n            target = p.predictor.domain.class_var\n            if target.is_discrete:\n                # order probabilities in order from Show prob. for\n                prob = self._reordered_probabilities(p)\n                values = numpy.array(target.values)[values.astype(int)]\n            else:\n                prob = numpy.zeros((len(values), 0))\n            all_values.append(values)\n            all_probs.append(prob)\n            headers.append(p.predictor.name)\n\n        if all_values:\n            model = PredictionsModel(all_values, all_probs, headers)\n            model.list_sorted.connect(\n                partial(\n                    self._update_data_sort_order, self.predictionsview,\n                    self.dataview))\n        else:\n            model = None\n\n        if self.selection_store is not None:\n            self.selection_store.unregister(\n                self.predictionsview.selectionModel())\n\n        self.predictionsview.setModel(model)\n        if model is not None:\n            self.predictionsview.setSelectionModel(\n                SharedSelectionModel(self.get_selection_store(model),\n                                     model, self.predictionsview))\n\n        hheader = self.predictionsview.horizontalHeader()\n        hheader.setSortIndicatorShown(False)\n        hheader.setSectionsClickable(True)\n\n    def _update_data_sort_order(self, sort_source_view, sort_dest_view):\n        sort_source_view.horizontalHeader().setSortIndicatorShown(True)\n        sort_dest_view.horizontalHeader().setSortIndicatorShown(False)\n\n        sort_dest = sort_dest_view.model()\n        sort_source = sort_source_view.model()\n        if sort_dest is not None:\n            if sort_source is not None and sort_source.sortColumn() >= 0:\n                sort_dest.setSortIndices(sort_source.mapToSourceRows(...))\n            else:\n                sort_dest.setSortIndices(None)\n        self.commit()\n\n    def _reset_order(self):\n        datamodel = self.dataview.model()\n        predmodel = self.predictionsview.model()\n        if datamodel is not None:\n            datamodel.setSortIndices(None)\n            datamodel.sort(-1)\n        if predmodel is not None:\n            predmodel.setSortIndices(None)\n            predmodel.sort(-1)\n        self.predictionsview.horizontalHeader().setSortIndicatorShown(False)\n        self.dataview.horizontalHeader().setSortIndicatorShown(False)\n\n    def _all_color_values(self):\n        \"\"\"\n        Return list of colors together with their values from all predictors\n        classes. Colors and values are sorted according to the values order\n        for simpler comparison.\n        \"\"\"\n        predictors = self._non_errored_predictors()\n        color_values = [\n            list(zip(*sorted(zip(\n                p.predictor.domain.class_var.colors,\n                p.predictor.domain.class_var.values\n            ), key=itemgetter(1))))\n            for p in predictors if p.predictor.domain.class_var.is_discrete\n        ]\n        return color_values if color_values else [([], [])]\n\n    @staticmethod\n    def _colors_match(colors1, values1, color2, values2):\n        \"\"\"\n        Test whether colors for values match. Colors matches when all\n        values match for shorter list and colors match for shorter list.\n        It is assumed that values will be sorted together with their colors.\n        \"\"\"\n        shorter_length = min(len(colors1), len(color2))\n        return (values1[:shorter_length] == values2[:shorter_length]\n                and (numpy.array(colors1[:shorter_length]) ==\n                     numpy.array(color2[:shorter_length])).all())\n\n    def _get_colors(self):\n        \"\"\"\n        Defines colors for values. If colors match in all models use the union\n        otherwise use standard colors.\n        \"\"\"\n        all_colors_values = self._all_color_values()\n        base_color, base_values = all_colors_values[0]\n        for c, v in all_colors_values[1:]:\n            if not self._colors_match(base_color, base_values, c, v):\n                base_color = []\n                break\n            # replace base_color if longer\n            if len(v) > len(base_color):\n                base_color = c\n                base_values = v\n\n        if len(base_color) != len(self.class_values):\n            return LimitedDiscretePalette(len(self.class_values)).palette\n        # reorder colors to widgets order\n        colors = [None] * len(self.class_values)\n        for c, v in zip(base_color, base_values):\n            colors[self.class_values.index(v)] = c\n        return colors\n\n    def _update_prediction_delegate(self):\n        self._delegates.clear()\n        colors = self._get_colors()\n        for col, slot in enumerate(self.predictors):\n            target = slot.predictor.domain.class_var\n            shown_probs = (\n                () if target.is_continuous else\n                [val if self.class_values[val] in target.values else None\n                 for val in self.selected_classes]\n            )\n            delegate = PredictionsItemDelegate(\n                None if target.is_continuous else self.class_values,\n                colors,\n                shown_probs,\n                target.format_str if target.is_continuous else None,\n                parent=self.predictionsview\n            )\n            # QAbstractItemView does not take ownership of delegates, so we must\n            self._delegates.append(delegate)\n            self.predictionsview.setItemDelegateForColumn(col, delegate)\n            self.predictionsview.setColumnHidden(col, False)\n\n        self.predictionsview.resizeColumnsToContents()\n        self._recompute_splitter_sizes()\n        if self.predictionsview.model() is not None:\n            self.predictionsview.model().setProbInd(self.selected_classes)\n\n    def _recompute_splitter_sizes(self):\n        if not self.data:\n            return\n        view = self.predictionsview\n        self.left_width = \\\n            view.horizontalHeader().length() + view.verticalHeader().width()\n        self._update_splitter()\n\n    def _update_splitter(self):\n        w1, w2 = self.splitter.sizes()\n        self.splitter.setSizes([self.left_width, w1 + w2 - self.left_width])\n\n    def splitter_resized(self):\n        self.left_width = self.splitter.sizes()[0]\n\n    def commit(self):\n        self._commit_predictions()\n        self._commit_evaluation_results()\n\n    def _commit_evaluation_results(self):\n        slots = [p for p in self._non_errored_predictors()\n                 if p.results.predicted is not None]\n        if not slots:\n            self.Outputs.evaluation_results.send(None)\n            return\n\n        nanmask = numpy.isnan(self.data.get_column_view(self.class_var)[0])\n        data = self.data[~nanmask]\n        results = Results(data, store_data=True)\n        results.folds = None\n        results.row_indices = numpy.arange(len(data))\n        results.actual = data.Y.ravel()\n        results.predicted = numpy.vstack(\n            tuple(p.results.predicted[0][~nanmask] for p in slots))\n        if self.class_var and self.class_var.is_discrete:\n            results.probabilities = numpy.array(\n                [p.results.probabilities[0][~nanmask] for p in slots])\n        results.learner_names = [p.name for p in slots]\n        self.Outputs.evaluation_results.send(results)\n\n    def _commit_predictions(self):\n        if not self.data:\n            self.Outputs.predictions.send(None)\n            return\n\n        newmetas = []\n        newcolumns = []\n        for slot in self._non_errored_predictors():\n            if slot.predictor.domain.class_var.is_discrete:\n                self._add_classification_out_columns(slot, newmetas, newcolumns)\n            else:\n                self._add_regression_out_columns(slot, newmetas, newcolumns)\n\n        attrs = list(self.data.domain.attributes)\n        metas = list(self.data.domain.metas)\n        names = [var.name for var in chain(attrs, self.data.domain.class_vars, metas) if var]\n        uniq_newmetas = []\n        for new_ in newmetas:\n            uniq = get_unique_names(names, new_.name)\n            if uniq != new_.name:\n                new_ = new_.copy(name=uniq)\n            uniq_newmetas.append(new_)\n            names.append(uniq)\n\n        metas += uniq_newmetas\n        domain = Orange.data.Domain(attrs, self.class_var, metas=metas)\n        predictions = self.data.transform(domain)\n        if newcolumns:\n            newcolumns = numpy.hstack(\n                [numpy.atleast_2d(cols) for cols in newcolumns])\n            with predictions.unlocked(predictions.metas):\n                predictions.metas[:, -newcolumns.shape[1]:] = newcolumns\n\n        datamodel = self.dataview.model()\n        predmodel = self.predictionsview.model()\n        assert datamodel is not None  # because we have data\n        assert self.selection_store is not None\n        rows = numpy.array(list(self.selection_store.rows))\n        if rows.size:\n            # Reorder rows as they are ordered in view\n            shown_rows = datamodel.mapFromSourceRows(rows)\n            rows = rows[numpy.argsort(shown_rows)]\n            predictions = predictions[rows]\n        elif datamodel.sortColumn() >= 0 \\\n                or predmodel is not None and predmodel.sortColumn() > 0:\n            # No selection: output all, but in the shown order\n            predictions = predictions[datamodel.mapToSourceRows(...)]\n        self.Outputs.predictions.send(predictions)\n\n    @staticmethod\n    def _add_classification_out_columns(slot, newmetas, newcolumns):\n        # Mapped or unmapped predictions?!\n        # Or provide a checkbox so the user decides?\n        pred = slot.predictor\n        name = pred.name\n        values = pred.domain.class_var.values\n        newmetas.append(DiscreteVariable(name=name, values=values))\n        newcolumns.append(slot.results.unmapped_predicted.reshape(-1, 1))\n        newmetas += [ContinuousVariable(name=f\"{name} ({value})\")\n                     for value in values]\n        newcolumns.append(slot.results.unmapped_probabilities)\n\n    @staticmethod\n    def _add_regression_out_columns(slot, newmetas, newcolumns):\n        newmetas.append(ContinuousVariable(name=slot.predictor.name))\n        newcolumns.append(slot.results.unmapped_predicted.reshape((-1, 1)))\n\n    def send_report(self):\n        def merge_data_with_predictions():\n            data_model = self.dataview.model()\n            predictions_view = self.predictionsview\n            predictions_model = predictions_view.model()\n\n            # use ItemDelegate to style prediction values\n            delegates = [predictions_view.itemDelegateForColumn(i)\n                         for i in range(predictions_model.columnCount())]\n\n            # iterate only over visible columns of data's QTableView\n            iter_data_cols = list(filter(lambda x: not self.dataview.isColumnHidden(x),\n                                         range(data_model.columnCount())))\n\n            # print header\n            yield [''] + \\\n                  [predictions_model.headerData(col, Qt.Horizontal, Qt.DisplayRole)\n                   for col in range(predictions_model.columnCount())] + \\\n                  [data_model.headerData(col, Qt.Horizontal, Qt.DisplayRole)\n                   for col in iter_data_cols]\n\n            # print data & predictions\n            for i in range(data_model.rowCount()):\n                yield [data_model.headerData(i, Qt.Vertical, Qt.DisplayRole)] + \\\n                      [delegate.displayText(\n                          predictions_model.data(predictions_model.index(i, j)),\n                          QLocale())\n                       for j, delegate in enumerate(delegates)] + \\\n                      [data_model.data(data_model.index(i, j),\n                                       role=Qt.DisplayRole)\n                       for j in iter_data_cols]\n\n        if self.data:\n            text = self._get_details().replace('\\n', '<br>')\n            if self.selected_classes:\n                text += '<br>Showing probabilities for: '\n                text += ', '. join([self.class_values[i]\n                                    for i in self.selected_classes])\n            self.report_paragraph('Info', text)\n            self.report_table(\"Data & Predictions\", merge_data_with_predictions(),\n                              header_rows=1, header_columns=1)\n\n            self.report_table(\"Scores\", self.score_table.view)\n\n    def resizeEvent(self, event):\n        super().resizeEvent(event)\n        self._update_splitter()\n\n    def showEvent(self, event):\n        super().showEvent(event)\n        QTimer.singleShot(0, self._update_splitter)",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 47,
                            "end_lineno": 699,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 291,
                            "length": 19
                        },
                        "new_range": {
                            "start": 293,
                            "length": 37
                        },
                        "section": "class OWPredictions(OWWidget):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "                continue",
                                "type": "context"
                            },
                            {
                                "content": "            row = [QStandardItem(learner_name(pred.predictor)),",
                                "type": "context"
                            },
                            {
                                "content": "                   QStandardItem(\"N/A\"), QStandardItem(\"N/A\")]",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "            actual = results.actual",
                                "type": "added"
                            },
                            {
                                "content": "            predicted = results.predicted",
                                "type": "added"
                            },
                            {
                                "content": "            probabilities = results.probabilities",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "            mask = numpy.isnan(results.actual)",
                                "type": "added"
                            },
                            {
                                "content": "            no_targets = mask.sum() == len(results.actual)",
                                "type": "added"
                            },
                            {
                                "content": "            results.actual = results.actual[~mask]",
                                "type": "added"
                            },
                            {
                                "content": "            results.predicted = results.predicted[:, ~mask]",
                                "type": "added"
                            },
                            {
                                "content": "            results.probabilities = results.probabilities[:, ~mask]",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "            for scorer in scorers:",
                                "type": "context"
                            },
                            {
                                "content": "                item = QStandardItem()",
                                "type": "context"
                            },
                            {
                                "content": "                try:",
                                "type": "deleted"
                            },
                            {
                                "content": "                    score = scorer_caller(scorer, results)()[0]",
                                "type": "deleted"
                            },
                            {
                                "content": "                    item.setText(f\"{score:.3f}\")",
                                "type": "deleted"
                            },
                            {
                                "content": "                except Exception as exc:  # pylint: disable=broad-except",
                                "type": "deleted"
                            },
                            {
                                "content": "                    item.setToolTip(str(exc))",
                                "type": "deleted"
                            },
                            {
                                "content": "                    # false pos.; pylint: disable=unsupported-membership-test",
                                "type": "deleted"
                            },
                            {
                                "content": "                    if scorer.name in self.score_table.shown_scores:",
                                "type": "deleted"
                            },
                            {
                                "content": "                        errors.append(str(exc))",
                                "type": "deleted"
                            },
                            {
                                "content": "                if no_targets:",
                                "type": "added"
                            },
                            {
                                "content": "                    item.setText(\"NA\")",
                                "type": "added"
                            },
                            {
                                "content": "                else:",
                                "type": "added"
                            },
                            {
                                "content": "                    try:",
                                "type": "added"
                            },
                            {
                                "content": "                        score = scorer_caller(scorer, results)()[0]",
                                "type": "added"
                            },
                            {
                                "content": "                        item.setText(f\"{score:.3f}\")",
                                "type": "added"
                            },
                            {
                                "content": "                    except Exception as exc:  # pylint: disable=broad-except",
                                "type": "added"
                            },
                            {
                                "content": "                        item.setToolTip(str(exc))",
                                "type": "added"
                            },
                            {
                                "content": "                        # false pos.; pylint: disable=unsupported-membership-test",
                                "type": "added"
                            },
                            {
                                "content": "                        if scorer.name in self.score_table.shown_scores:",
                                "type": "added"
                            },
                            {
                                "content": "                            errors.append(str(exc))",
                                "type": "added"
                            },
                            {
                                "content": "                row.append(item)",
                                "type": "context"
                            },
                            {
                                "content": "            self.score_table.model.appendRow(row)",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "            results.actual = actual",
                                "type": "added"
                            },
                            {
                                "content": "            results.predicted = predicted",
                                "type": "added"
                            },
                            {
                                "content": "            results.probabilities = probabilities",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        view = self.score_table.view",
                                "type": "context"
                            },
                            {
                                "content": "        if model.rowCount():",
                                "type": "context"
                            },
                            {
                                "content": "            view.setVisible(True)",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "Orange/widgets/evaluate/owpredictions.py",
                            "type": "class",
                            "name": "OWPredictions",
                            "content": "class OWPredictions(OWWidget):\n    name = \"Predictions\"\n    icon = \"icons/Predictions.svg\"\n    priority = 200\n    description = \"Display predictions of models for an input dataset.\"\n    keywords = []\n\n    buttons_area_orientation = None\n\n    class Inputs:\n        data = Input(\"Data\", Orange.data.Table)\n        predictors = MultiInput(\"Predictors\", Model, filter_none=True)\n\n    class Outputs:\n        predictions = Output(\"Predictions\", Orange.data.Table)\n        evaluation_results = Output(\"Evaluation Results\", Results)\n\n    class Warning(OWWidget.Warning):\n        empty_data = Msg(\"Empty dataset\")\n        wrong_targets = Msg(\n            \"Some model(s) predict a different target (see more ...)\\n{}\")\n\n    class Error(OWWidget.Error):\n        predictor_failed = Msg(\"Some predictor(s) failed (see more ...)\\n{}\")\n        scorer_failed = Msg(\"Some scorer(s) failed (see more ...)\\n{}\")\n\n    settingsHandler = settings.ClassValuesContextHandler()\n    score_table = settings.SettingProvider(ScoreTable)\n\n    #: List of selected class value indices in the `class_values` list\n    selected_classes = settings.ContextSetting([])\n    selection = settings.Setting([], schema_only=True)\n\n    def __init__(self):\n        super().__init__()\n\n        self.data = None  # type: Optional[Orange.data.Table]\n        self.predictors = []  # type: List[PredictorSlot]\n        self.class_values = []  # type: List[str]\n        self._delegates = []\n        self.left_width = 10\n        self.selection_store = None\n        self.__pending_selection = self.selection\n\n        controlBox = gui.vBox(self.controlArea, \"Show probabilities for\")\n\n        gui.listBox(controlBox, self, \"selected_classes\", \"class_values\",\n                    callback=self._update_prediction_delegate,\n                    selectionMode=QListWidget.ExtendedSelection,\n                    sizePolicy=(QSizePolicy.Preferred, QSizePolicy.MinimumExpanding),\n                    sizeHint=QSize(1, 350),\n                    minimumHeight=100)\n        self.reset_button = gui.button(\n            controlBox, self, \"Restore Original Order\",\n            callback=self._reset_order,\n            tooltip=\"Show rows in the original order\")\n\n        table_opts = dict(horizontalScrollBarPolicy=Qt.ScrollBarAlwaysOn,\n                          horizontalScrollMode=QTableView.ScrollPerPixel,\n                          selectionMode=QTableView.ExtendedSelection,\n                          focusPolicy=Qt.StrongFocus)\n        self.dataview = TableView(\n            sortingEnabled=True,\n            verticalScrollBarPolicy=Qt.ScrollBarAlwaysOn,\n            **table_opts)\n        self.predictionsview = TableView(\n            sortingEnabled=True,\n            verticalScrollBarPolicy=Qt.ScrollBarAlwaysOff,\n            **table_opts)\n        self.dataview.verticalHeader().hide()\n        dsbar = self.dataview.verticalScrollBar()\n        psbar = self.predictionsview.verticalScrollBar()\n        psbar.valueChanged.connect(dsbar.setValue)\n        dsbar.valueChanged.connect(psbar.setValue)\n\n        self.dataview.verticalHeader().setDefaultSectionSize(22)\n        self.predictionsview.verticalHeader().setDefaultSectionSize(22)\n        self.dataview.verticalHeader().sectionResized.connect(\n            lambda index, _, size:\n            self.predictionsview.verticalHeader().resizeSection(index, size))\n\n        self.dataview.setItemDelegate(DataItemDelegate(self.dataview))\n\n        self.splitter = QSplitter(\n            orientation=Qt.Horizontal, childrenCollapsible=False, handleWidth=2)\n        self.splitter.splitterMoved.connect(self.splitter_resized)\n        self.splitter.addWidget(self.predictionsview)\n        self.splitter.addWidget(self.dataview)\n\n        self.score_table = ScoreTable(self)\n        self.vsplitter = gui.vBox(self.mainArea)\n        self.vsplitter.layout().addWidget(self.splitter)\n        self.vsplitter.layout().addWidget(self.score_table.view)\n\n    def get_selection_store(self, model):\n        # Both models map the same, so it doesn't matter which one is used\n        # to initialize SharedSelectionStore\n        if self.selection_store is None:\n            self.selection_store = SharedSelectionStore(model)\n        return self.selection_store\n\n    @Inputs.data\n    @check_sql_input\n    def set_data(self, data):\n        self.Warning.empty_data(shown=data is not None and not data)\n        self.data = data\n        self.selection_store = None\n        if not data:\n            self.dataview.setModel(None)\n            self.predictionsview.setModel(None)\n        else:\n            # force full reset of the view's HeaderView state\n            self.dataview.setModel(None)\n            model = DataModel(data, parent=None)\n            self.dataview.setModel(model)\n            sel_model = SharedSelectionModel(\n                self.get_selection_store(model), model, self.dataview)\n            self.dataview.setSelectionModel(sel_model)\n            if self.__pending_selection is not None:\n                self.selection = self.__pending_selection\n                self.__pending_selection = None\n                self.selection_store.select_rows(\n                    set(self.selection), QItemSelectionModel.ClearAndSelect)\n            sel_model.selectionChanged.connect(self.commit)\n            sel_model.selectionChanged.connect(self._store_selection)\n\n            self.dataview.model().list_sorted.connect(\n                partial(\n                    self._update_data_sort_order, self.dataview,\n                    self.predictionsview))\n\n        self._invalidate_predictions()\n\n    def _store_selection(self):\n        self.selection = list(self.selection_store.rows)\n\n    @property\n    def class_var(self):\n        return self.data and self.data.domain.class_var\n\n    @Inputs.predictors\n    def set_predictor(self, index, predictor: Model):\n        item = self.predictors[index]\n        self.predictors[index] = item._replace(\n            predictor=predictor, name=predictor.name, results=None\n        )\n\n    @Inputs.predictors.insert\n    def insert_predictor(self, index, predictor: Model):\n        item = PredictorSlot(predictor, predictor.name, None)\n        self.predictors.insert(index, item)\n\n    @Inputs.predictors.remove\n    def remove_predictor(self, index):\n        self.predictors.pop(index)\n\n    def _set_class_values(self):\n        class_values = []\n        for slot in self.predictors:\n            class_var = slot.predictor.domain.class_var\n            if class_var and class_var.is_discrete:\n                for value in class_var.values:\n                    if value not in class_values:\n                        class_values.append(value)\n\n        if self.class_var and self.class_var.is_discrete:\n            values = self.class_var.values\n            self.class_values = sorted(\n                class_values, key=lambda val: val not in values)\n            self.selected_classes = [\n                i for i, name in enumerate(class_values) if name in values]\n        else:\n            self.class_values = class_values  # This assignment updates listview\n            self.selected_classes = []\n\n    def handleNewSignals(self):\n        # Disconnect the model: the model and the delegate will be inconsistent\n        # between _set_class_values and update_predictions_model.\n        self.predictionsview.setModel(None)\n        self._set_class_values()\n        self._call_predictors()\n        self._update_scores()\n        self._update_predictions_model()\n        self._update_prediction_delegate()\n        self._set_errors()\n        self.commit()\n\n    def _call_predictors(self):\n        if not self.data:\n            return\n        if self.class_var:\n            domain = self.data.domain\n            classless_data = self.data.transform(\n                Domain(domain.attributes, None, domain.metas))\n        else:\n            classless_data = self.data\n\n        for index, slot in enumerate(self.predictors):\n            if isinstance(slot.results, Results):\n                continue\n\n            predictor = slot.predictor\n            try:\n                if predictor.domain.class_var.is_discrete:\n                    pred, prob = predictor(classless_data, Model.ValueProbs)\n                else:\n                    pred = predictor(classless_data, Model.Value)\n                    prob = numpy.zeros((len(pred), 0))\n            except (ValueError, DomainTransformationError) as err:\n                self.predictors[index] = \\\n                    slot._replace(results=f\"{predictor.name}: {err}\")\n                continue\n\n            results = Results()\n            results.data = self.data\n            results.domain = self.data.domain\n            results.row_indices = numpy.arange(len(self.data))\n            results.folds = (Ellipsis, )\n            results.actual = self.data.Y\n            results.unmapped_probabilities = prob\n            results.unmapped_predicted = pred\n            results.probabilities = results.predicted = None\n            self.predictors[index] = slot._replace(results=results)\n\n            target = predictor.domain.class_var\n            if target != self.class_var:\n                continue\n\n            if target is not self.class_var and target.is_discrete:\n                backmappers, n_values = predictor.get_backmappers(self.data)\n                prob = predictor.backmap_probs(prob, n_values, backmappers)\n                pred = predictor.backmap_value(pred, prob, n_values, backmappers)\n            results.predicted = pred.reshape((1, len(self.data)))\n            results.probabilities = prob.reshape((1,) + prob.shape)\n\n    def _update_scores(self):\n        model = self.score_table.model\n        model.clear()\n        scorers = usable_scorers(self.class_var) if self.class_var else []\n        self.score_table.update_header(scorers)\n        errors = []\n        for pred in self.predictors:\n            results = pred.results\n            if not isinstance(results, Results) or results.predicted is None:\n                continue\n            row = [QStandardItem(learner_name(pred.predictor)),\n                   QStandardItem(\"N/A\"), QStandardItem(\"N/A\")]\n            for scorer in scorers:\n                item = QStandardItem()\n                try:\n                    score = scorer_caller(scorer, results)()[0]\n                    item.setText(f\"{score:.3f}\")\n                except Exception as exc:  # pylint: disable=broad-except\n                    item.setToolTip(str(exc))\n                    # false pos.; pylint: disable=unsupported-membership-test\n                    if scorer.name in self.score_table.shown_scores:\n                        errors.append(str(exc))\n                row.append(item)\n            self.score_table.model.appendRow(row)\n\n        view = self.score_table.view\n        if model.rowCount():\n            view.setVisible(True)\n            view.ensurePolished()\n            view.setFixedHeight(\n                5 + view.horizontalHeader().height() +\n                view.verticalHeader().sectionSize(0) * model.rowCount())\n        else:\n            view.setVisible(False)\n\n        self.Error.scorer_failed(\"\\n\".join(errors), shown=bool(errors))\n\n    def _set_errors(self):\n        # Not all predictors are run every time, so errors can't be collected\n        # in _call_predictors\n        errors = \"\\n\".join(\n            f\"- {p.predictor.name}: {p.results}\"\n            for p in self.predictors\n            if isinstance(p.results, str) and p.results)\n        self.Error.predictor_failed(errors, shown=bool(errors))\n\n        if self.class_var:\n            inv_targets = \"\\n\".join(\n                f\"- {pred.name} predicts '{pred.domain.class_var.name}'\"\n                for pred in (p.predictor for p in self.predictors\n                             if isinstance(p.results, Results)\n                             and p.results.probabilities is None))\n            self.Warning.wrong_targets(inv_targets, shown=bool(inv_targets))\n        else:\n            self.Warning.wrong_targets.clear()\n\n    def _get_details(self):\n        details = \"Data:<br>\"\n        details += format_summary_details(self.data, format=Qt.RichText)\n        details += \"<hr>\"\n        pred_names = [v.name for v in self.predictors]\n        n_predictors = len(self.predictors)\n        if n_predictors:\n            n_valid = len(self._non_errored_predictors())\n            details += plural(\"Model: {number} model{s}\", n_predictors)\n            if n_valid != n_predictors:\n                details += f\" ({n_predictors - n_valid} failed)\"\n            details += \"<ul>\"\n            for name in pred_names:\n                details += f\"<li>{name}</li>\"\n            details += \"</ul>\"\n        else:\n            details += \"Model:<br>No model on input.\"\n        return details\n\n    def _invalidate_predictions(self):\n        for i, pred in enumerate(self.predictors):\n            self.predictors[i] = pred._replace(results=None)\n\n    def _non_errored_predictors(self):\n        return [p for p in self.predictors\n                if isinstance(p.results, Results)]\n\n    def _reordered_probabilities(self, prediction):\n        cur_values = prediction.predictor.domain.class_var.values\n        new_ind = [self.class_values.index(x) for x in cur_values]\n        probs = prediction.results.unmapped_probabilities\n        new_probs = numpy.full(\n            (probs.shape[0], len(self.class_values)), numpy.nan)\n        new_probs[:, new_ind] = probs\n        return new_probs\n\n    def _update_predictions_model(self):\n        headers = []\n        all_values = []\n        all_probs = []\n        for p in self._non_errored_predictors():\n            values = p.results.unmapped_predicted\n            target = p.predictor.domain.class_var\n            if target.is_discrete:\n                # order probabilities in order from Show prob. for\n                prob = self._reordered_probabilities(p)\n                values = numpy.array(target.values)[values.astype(int)]\n            else:\n                prob = numpy.zeros((len(values), 0))\n            all_values.append(values)\n            all_probs.append(prob)\n            headers.append(p.predictor.name)\n\n        if all_values:\n            model = PredictionsModel(all_values, all_probs, headers)\n            model.list_sorted.connect(\n                partial(\n                    self._update_data_sort_order, self.predictionsview,\n                    self.dataview))\n        else:\n            model = None\n\n        if self.selection_store is not None:\n            self.selection_store.unregister(\n                self.predictionsview.selectionModel())\n\n        self.predictionsview.setModel(model)\n        if model is not None:\n            self.predictionsview.setSelectionModel(\n                SharedSelectionModel(self.get_selection_store(model),\n                                     model, self.predictionsview))\n\n        hheader = self.predictionsview.horizontalHeader()\n        hheader.setSortIndicatorShown(False)\n        hheader.setSectionsClickable(True)\n\n    def _update_data_sort_order(self, sort_source_view, sort_dest_view):\n        sort_source_view.horizontalHeader().setSortIndicatorShown(True)\n        sort_dest_view.horizontalHeader().setSortIndicatorShown(False)\n\n        sort_dest = sort_dest_view.model()\n        sort_source = sort_source_view.model()\n        if sort_dest is not None:\n            if sort_source is not None and sort_source.sortColumn() >= 0:\n                sort_dest.setSortIndices(sort_source.mapToSourceRows(...))\n            else:\n                sort_dest.setSortIndices(None)\n        self.commit()\n\n    def _reset_order(self):\n        datamodel = self.dataview.model()\n        predmodel = self.predictionsview.model()\n        if datamodel is not None:\n            datamodel.setSortIndices(None)\n            datamodel.sort(-1)\n        if predmodel is not None:\n            predmodel.setSortIndices(None)\n            predmodel.sort(-1)\n        self.predictionsview.horizontalHeader().setSortIndicatorShown(False)\n        self.dataview.horizontalHeader().setSortIndicatorShown(False)\n\n    def _all_color_values(self):\n        \"\"\"\n        Return list of colors together with their values from all predictors\n        classes. Colors and values are sorted according to the values order\n        for simpler comparison.\n        \"\"\"\n        predictors = self._non_errored_predictors()\n        color_values = [\n            list(zip(*sorted(zip(\n                p.predictor.domain.class_var.colors,\n                p.predictor.domain.class_var.values\n            ), key=itemgetter(1))))\n            for p in predictors if p.predictor.domain.class_var.is_discrete\n        ]\n        return color_values if color_values else [([], [])]\n\n    @staticmethod\n    def _colors_match(colors1, values1, color2, values2):\n        \"\"\"\n        Test whether colors for values match. Colors matches when all\n        values match for shorter list and colors match for shorter list.\n        It is assumed that values will be sorted together with their colors.\n        \"\"\"\n        shorter_length = min(len(colors1), len(color2))\n        return (values1[:shorter_length] == values2[:shorter_length]\n                and (numpy.array(colors1[:shorter_length]) ==\n                     numpy.array(color2[:shorter_length])).all())\n\n    def _get_colors(self):\n        \"\"\"\n        Defines colors for values. If colors match in all models use the union\n        otherwise use standard colors.\n        \"\"\"\n        all_colors_values = self._all_color_values()\n        base_color, base_values = all_colors_values[0]\n        for c, v in all_colors_values[1:]:\n            if not self._colors_match(base_color, base_values, c, v):\n                base_color = []\n                break\n            # replace base_color if longer\n            if len(v) > len(base_color):\n                base_color = c\n                base_values = v\n\n        if len(base_color) != len(self.class_values):\n            return LimitedDiscretePalette(len(self.class_values)).palette\n        # reorder colors to widgets order\n        colors = [None] * len(self.class_values)\n        for c, v in zip(base_color, base_values):\n            colors[self.class_values.index(v)] = c\n        return colors\n\n    def _update_prediction_delegate(self):\n        self._delegates.clear()\n        colors = self._get_colors()\n        for col, slot in enumerate(self.predictors):\n            target = slot.predictor.domain.class_var\n            shown_probs = (\n                () if target.is_continuous else\n                [val if self.class_values[val] in target.values else None\n                 for val in self.selected_classes]\n            )\n            delegate = PredictionsItemDelegate(\n                None if target.is_continuous else self.class_values,\n                colors,\n                shown_probs,\n                target.format_str if target.is_continuous else None,\n                parent=self.predictionsview\n            )\n            # QAbstractItemView does not take ownership of delegates, so we must\n            self._delegates.append(delegate)\n            self.predictionsview.setItemDelegateForColumn(col, delegate)\n            self.predictionsview.setColumnHidden(col, False)\n\n        self.predictionsview.resizeColumnsToContents()\n        self._recompute_splitter_sizes()\n        if self.predictionsview.model() is not None:\n            self.predictionsview.model().setProbInd(self.selected_classes)\n\n    def _recompute_splitter_sizes(self):\n        if not self.data:\n            return\n        view = self.predictionsview\n        self.left_width = \\\n            view.horizontalHeader().length() + view.verticalHeader().width()\n        self._update_splitter()\n\n    def _update_splitter(self):\n        w1, w2 = self.splitter.sizes()\n        self.splitter.setSizes([self.left_width, w1 + w2 - self.left_width])\n\n    def splitter_resized(self):\n        self.left_width = self.splitter.sizes()[0]\n\n    def commit(self):\n        self._commit_predictions()\n        self._commit_evaluation_results()\n\n    def _commit_evaluation_results(self):\n        slots = [p for p in self._non_errored_predictors()\n                 if p.results.predicted is not None]\n        if not slots:\n            self.Outputs.evaluation_results.send(None)\n            return\n\n        nanmask = numpy.isnan(self.data.get_column_view(self.class_var)[0])\n        data = self.data[~nanmask]\n        results = Results(data, store_data=True)\n        results.folds = None\n        results.row_indices = numpy.arange(len(data))\n        results.actual = data.Y.ravel()\n        results.predicted = numpy.vstack(\n            tuple(p.results.predicted[0][~nanmask] for p in slots))\n        if self.class_var and self.class_var.is_discrete:\n            results.probabilities = numpy.array(\n                [p.results.probabilities[0][~nanmask] for p in slots])\n        results.learner_names = [p.name for p in slots]\n        self.Outputs.evaluation_results.send(results)\n\n    def _commit_predictions(self):\n        if not self.data:\n            self.Outputs.predictions.send(None)\n            return\n\n        newmetas = []\n        newcolumns = []\n        for slot in self._non_errored_predictors():\n            if slot.predictor.domain.class_var.is_discrete:\n                self._add_classification_out_columns(slot, newmetas, newcolumns)\n            else:\n                self._add_regression_out_columns(slot, newmetas, newcolumns)\n\n        attrs = list(self.data.domain.attributes)\n        metas = list(self.data.domain.metas)\n        names = [var.name for var in chain(attrs, self.data.domain.class_vars, metas) if var]\n        uniq_newmetas = []\n        for new_ in newmetas:\n            uniq = get_unique_names(names, new_.name)\n            if uniq != new_.name:\n                new_ = new_.copy(name=uniq)\n            uniq_newmetas.append(new_)\n            names.append(uniq)\n\n        metas += uniq_newmetas\n        domain = Orange.data.Domain(attrs, self.class_var, metas=metas)\n        predictions = self.data.transform(domain)\n        if newcolumns:\n            newcolumns = numpy.hstack(\n                [numpy.atleast_2d(cols) for cols in newcolumns])\n            with predictions.unlocked(predictions.metas):\n                predictions.metas[:, -newcolumns.shape[1]:] = newcolumns\n\n        datamodel = self.dataview.model()\n        predmodel = self.predictionsview.model()\n        assert datamodel is not None  # because we have data\n        assert self.selection_store is not None\n        rows = numpy.array(list(self.selection_store.rows))\n        if rows.size:\n            # Reorder rows as they are ordered in view\n            shown_rows = datamodel.mapFromSourceRows(rows)\n            rows = rows[numpy.argsort(shown_rows)]\n            predictions = predictions[rows]\n        elif datamodel.sortColumn() >= 0 \\\n                or predmodel is not None and predmodel.sortColumn() > 0:\n            # No selection: output all, but in the shown order\n            predictions = predictions[datamodel.mapToSourceRows(...)]\n        self.Outputs.predictions.send(predictions)\n\n    @staticmethod\n    def _add_classification_out_columns(slot, newmetas, newcolumns):\n        # Mapped or unmapped predictions?!\n        # Or provide a checkbox so the user decides?\n        pred = slot.predictor\n        name = pred.name\n        values = pred.domain.class_var.values\n        newmetas.append(DiscreteVariable(name=name, values=values))\n        newcolumns.append(slot.results.unmapped_predicted.reshape(-1, 1))\n        newmetas += [ContinuousVariable(name=f\"{name} ({value})\")\n                     for value in values]\n        newcolumns.append(slot.results.unmapped_probabilities)\n\n    @staticmethod\n    def _add_regression_out_columns(slot, newmetas, newcolumns):\n        newmetas.append(ContinuousVariable(name=slot.predictor.name))\n        newcolumns.append(slot.results.unmapped_predicted.reshape((-1, 1)))\n\n    def send_report(self):\n        def merge_data_with_predictions():\n            data_model = self.dataview.model()\n            predictions_view = self.predictionsview\n            predictions_model = predictions_view.model()\n\n            # use ItemDelegate to style prediction values\n            delegates = [predictions_view.itemDelegateForColumn(i)\n                         for i in range(predictions_model.columnCount())]\n\n            # iterate only over visible columns of data's QTableView\n            iter_data_cols = list(filter(lambda x: not self.dataview.isColumnHidden(x),\n                                         range(data_model.columnCount())))\n\n            # print header\n            yield [''] + \\\n                  [predictions_model.headerData(col, Qt.Horizontal, Qt.DisplayRole)\n                   for col in range(predictions_model.columnCount())] + \\\n                  [data_model.headerData(col, Qt.Horizontal, Qt.DisplayRole)\n                   for col in iter_data_cols]\n\n            # print data & predictions\n            for i in range(data_model.rowCount()):\n                yield [data_model.headerData(i, Qt.Vertical, Qt.DisplayRole)] + \\\n                      [delegate.displayText(\n                          predictions_model.data(predictions_model.index(i, j)),\n                          QLocale())\n                       for j, delegate in enumerate(delegates)] + \\\n                      [data_model.data(data_model.index(i, j),\n                                       role=Qt.DisplayRole)\n                       for j in iter_data_cols]\n\n        if self.data:\n            text = self._get_details().replace('\\n', '<br>')\n            if self.selected_classes:\n                text += '<br>Showing probabilities for: '\n                text += ', '. join([self.class_values[i]\n                                    for i in self.selected_classes])\n            self.report_paragraph('Info', text)\n            self.report_table(\"Data & Predictions\", merge_data_with_predictions(),\n                              header_rows=1, header_columns=1)\n\n            self.report_table(\"Scores\", self.score_table.view)\n\n    def resizeEvent(self, event):\n        super().resizeEvent(event)\n        self._update_splitter()\n\n    def showEvent(self, event):\n        super().showEvent(event)\n        QTimer.singleShot(0, self._update_splitter)",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 47,
                            "end_lineno": 675,
                            "parent": null
                        },
                        {
                            "file_name": "Orange/widgets/evaluate/owpredictions.py",
                            "type": "method",
                            "name": "OWPredictions._update_scores",
                            "content": "class OWPredictions(OWWidget):\n    name = \"Predictions\"\n    icon = \"icons/Predictions.svg\"\n    priority = 200\n    description = \"Display predictions of models for an input dataset.\"\n    keywords = []\n\n    buttons_area_orientation = None\n\n    class Inputs:\n        data = Input(\"Data\", Orange.data.Table)\n        predictors = MultiInput(\"Predictors\", Model, filter_none=True)\n\n    class Outputs:\n        predictions = Output(\"Predictions\", Orange.data.Table)\n        evaluation_results = Output(\"Evaluation Results\", Results)\n\n    class Warning(OWWidget.Warning):\n        empty_data = Msg(\"Empty dataset\")\n        wrong_targets = Msg(\n            \"Some model(s) predict a different target (see more ...)\\n{}\")\n\n    class Error(OWWidget.Error):\n        predictor_failed = Msg(\"Some predictor(s) failed (see more ...)\\n{}\")\n        scorer_failed = Msg(\"Some scorer(s) failed (see more ...)\\n{}\")\n\n    settingsHandler = settings.ClassValuesContextHandler()\n    score_table = settings.SettingProvider(ScoreTable)\n\n    #: List of selected class value indices in the `class_values` list\n    selected_classes = settings.ContextSetting([])\n    selection = settings.Setting([], schema_only=True)\n\n    def __init__(self):\n        super().__init__()\n\n        self.data = None  # type: Optional[Orange.data.Table]\n        self.predictors = []  # type: List[PredictorSlot]\n        self.class_values = []  # type: List[str]\n        self._delegates = []\n        self.left_width = 10\n        self.selection_store = None\n        self.__pending_selection = self.selection\n\n        controlBox = gui.vBox(self.controlArea, \"Show probabilities for\")\n\n        gui.listBox(controlBox, self, \"selected_classes\", \"class_values\",\n                    callback=self._update_prediction_delegate,\n                    selectionMode=QListWidget.ExtendedSelection,\n                    sizePolicy=(QSizePolicy.Preferred, QSizePolicy.MinimumExpanding),\n                    sizeHint=QSize(1, 350),\n                    minimumHeight=100)\n        self.reset_button = gui.button(\n            controlBox, self, \"Restore Original Order\",\n            callback=self._reset_order,\n            tooltip=\"Show rows in the original order\")\n\n        table_opts = dict(horizontalScrollBarPolicy=Qt.ScrollBarAlwaysOn,\n                          horizontalScrollMode=QTableView.ScrollPerPixel,\n                          selectionMode=QTableView.ExtendedSelection,\n                          focusPolicy=Qt.StrongFocus)\n        self.dataview = TableView(\n            sortingEnabled=True,\n            verticalScrollBarPolicy=Qt.ScrollBarAlwaysOn,\n            **table_opts)\n        self.predictionsview = TableView(\n            sortingEnabled=True,\n            verticalScrollBarPolicy=Qt.ScrollBarAlwaysOff,\n            **table_opts)\n        self.dataview.verticalHeader().hide()\n        dsbar = self.dataview.verticalScrollBar()\n        psbar = self.predictionsview.verticalScrollBar()\n        psbar.valueChanged.connect(dsbar.setValue)\n        dsbar.valueChanged.connect(psbar.setValue)\n\n        self.dataview.verticalHeader().setDefaultSectionSize(22)\n        self.predictionsview.verticalHeader().setDefaultSectionSize(22)\n        self.dataview.verticalHeader().sectionResized.connect(\n            lambda index, _, size:\n            self.predictionsview.verticalHeader().resizeSection(index, size))\n\n        self.dataview.setItemDelegate(DataItemDelegate(self.dataview))\n\n        self.splitter = QSplitter(\n            orientation=Qt.Horizontal, childrenCollapsible=False, handleWidth=2)\n        self.splitter.splitterMoved.connect(self.splitter_resized)\n        self.splitter.addWidget(self.predictionsview)\n        self.splitter.addWidget(self.dataview)\n\n        self.score_table = ScoreTable(self)\n        self.vsplitter = gui.vBox(self.mainArea)\n        self.vsplitter.layout().addWidget(self.splitter)\n        self.vsplitter.layout().addWidget(self.score_table.view)\n\n    def get_selection_store(self, model):\n        # Both models map the same, so it doesn't matter which one is used\n        # to initialize SharedSelectionStore\n        if self.selection_store is None:\n            self.selection_store = SharedSelectionStore(model)\n        return self.selection_store\n\n    @Inputs.data\n    @check_sql_input\n    def set_data(self, data):\n        self.Warning.empty_data(shown=data is not None and not data)\n        self.data = data\n        self.selection_store = None\n        if not data:\n            self.dataview.setModel(None)\n            self.predictionsview.setModel(None)\n        else:\n            # force full reset of the view's HeaderView state\n            self.dataview.setModel(None)\n            model = DataModel(data, parent=None)\n            self.dataview.setModel(model)\n            sel_model = SharedSelectionModel(\n                self.get_selection_store(model), model, self.dataview)\n            self.dataview.setSelectionModel(sel_model)\n            if self.__pending_selection is not None:\n                self.selection = self.__pending_selection\n                self.__pending_selection = None\n                self.selection_store.select_rows(\n                    set(self.selection), QItemSelectionModel.ClearAndSelect)\n            sel_model.selectionChanged.connect(self.commit)\n            sel_model.selectionChanged.connect(self._store_selection)\n\n            self.dataview.model().list_sorted.connect(\n                partial(\n                    self._update_data_sort_order, self.dataview,\n                    self.predictionsview))\n\n        self._invalidate_predictions()\n\n    def _store_selection(self):\n        self.selection = list(self.selection_store.rows)\n\n    @property\n    def class_var(self):\n        return self.data and self.data.domain.class_var\n\n    @Inputs.predictors\n    def set_predictor(self, index, predictor: Model):\n        item = self.predictors[index]\n        self.predictors[index] = item._replace(\n            predictor=predictor, name=predictor.name, results=None\n        )\n\n    @Inputs.predictors.insert\n    def insert_predictor(self, index, predictor: Model):\n        item = PredictorSlot(predictor, predictor.name, None)\n        self.predictors.insert(index, item)\n\n    @Inputs.predictors.remove\n    def remove_predictor(self, index):\n        self.predictors.pop(index)\n\n    def _set_class_values(self):\n        class_values = []\n        for slot in self.predictors:\n            class_var = slot.predictor.domain.class_var\n            if class_var and class_var.is_discrete:\n                for value in class_var.values:\n                    if value not in class_values:\n                        class_values.append(value)\n\n        if self.class_var and self.class_var.is_discrete:\n            values = self.class_var.values\n            self.class_values = sorted(\n                class_values, key=lambda val: val not in values)\n            self.selected_classes = [\n                i for i, name in enumerate(class_values) if name in values]\n        else:\n            self.class_values = class_values  # This assignment updates listview\n            self.selected_classes = []\n\n    def handleNewSignals(self):\n        # Disconnect the model: the model and the delegate will be inconsistent\n        # between _set_class_values and update_predictions_model.\n        self.predictionsview.setModel(None)\n        self._set_class_values()\n        self._call_predictors()\n        self._update_scores()\n        self._update_predictions_model()\n        self._update_prediction_delegate()\n        self._set_errors()\n        self.commit()\n\n    def _call_predictors(self):\n        if not self.data:\n            return\n        if self.class_var:\n            domain = self.data.domain\n            classless_data = self.data.transform(\n                Domain(domain.attributes, None, domain.metas))\n        else:\n            classless_data = self.data\n\n        for index, slot in enumerate(self.predictors):\n            if isinstance(slot.results, Results):\n                continue\n\n            predictor = slot.predictor\n            try:\n                if predictor.domain.class_var.is_discrete:\n                    pred, prob = predictor(classless_data, Model.ValueProbs)\n                else:\n                    pred = predictor(classless_data, Model.Value)\n                    prob = numpy.zeros((len(pred), 0))\n            except (ValueError, DomainTransformationError) as err:\n                self.predictors[index] = \\\n                    slot._replace(results=f\"{predictor.name}: {err}\")\n                continue\n\n            results = Results()\n            results.data = self.data\n            results.domain = self.data.domain\n            results.row_indices = numpy.arange(len(self.data))\n            results.folds = (Ellipsis, )\n            results.actual = self.data.Y\n            results.unmapped_probabilities = prob\n            results.unmapped_predicted = pred\n            results.probabilities = results.predicted = None\n            self.predictors[index] = slot._replace(results=results)\n\n            target = predictor.domain.class_var\n            if target != self.class_var:\n                continue\n\n            if target is not self.class_var and target.is_discrete:\n                backmappers, n_values = predictor.get_backmappers(self.data)\n                prob = predictor.backmap_probs(prob, n_values, backmappers)\n                pred = predictor.backmap_value(pred, prob, n_values, backmappers)\n            results.predicted = pred.reshape((1, len(self.data)))\n            results.probabilities = prob.reshape((1,) + prob.shape)\n\n    def _update_scores(self):\n        model = self.score_table.model\n        model.clear()\n        scorers = usable_scorers(self.class_var) if self.class_var else []\n        self.score_table.update_header(scorers)\n        errors = []\n        for pred in self.predictors:\n            results = pred.results\n            if not isinstance(results, Results) or results.predicted is None:\n                continue\n            row = [QStandardItem(learner_name(pred.predictor)),\n                   QStandardItem(\"N/A\"), QStandardItem(\"N/A\")]\n            for scorer in scorers:\n                item = QStandardItem()\n                try:\n                    score = scorer_caller(scorer, results)()[0]\n                    item.setText(f\"{score:.3f}\")\n                except Exception as exc:  # pylint: disable=broad-except\n                    item.setToolTip(str(exc))\n                    # false pos.; pylint: disable=unsupported-membership-test\n                    if scorer.name in self.score_table.shown_scores:\n                        errors.append(str(exc))\n                row.append(item)\n            self.score_table.model.appendRow(row)\n\n        view = self.score_table.view\n        if model.rowCount():\n            view.setVisible(True)\n            view.ensurePolished()\n            view.setFixedHeight(\n                5 + view.horizontalHeader().height() +\n                view.verticalHeader().sectionSize(0) * model.rowCount())\n        else:\n            view.setVisible(False)\n\n        self.Error.scorer_failed(\"\\n\".join(errors), shown=bool(errors))\n\n    def _set_errors(self):\n        # Not all predictors are run every time, so errors can't be collected\n        # in _call_predictors\n        errors = \"\\n\".join(\n            f\"- {p.predictor.name}: {p.results}\"\n            for p in self.predictors\n            if isinstance(p.results, str) and p.results)\n        self.Error.predictor_failed(errors, shown=bool(errors))\n\n        if self.class_var:\n            inv_targets = \"\\n\".join(\n                f\"- {pred.name} predicts '{pred.domain.class_var.name}'\"\n                for pred in (p.predictor for p in self.predictors\n                             if isinstance(p.results, Results)\n                             and p.results.probabilities is None))\n            self.Warning.wrong_targets(inv_targets, shown=bool(inv_targets))\n        else:\n            self.Warning.wrong_targets.clear()\n\n    def _get_details(self):\n        details = \"Data:<br>\"\n        details += format_summary_details(self.data, format=Qt.RichText)\n        details += \"<hr>\"\n        pred_names = [v.name for v in self.predictors]\n        n_predictors = len(self.predictors)\n        if n_predictors:\n            n_valid = len(self._non_errored_predictors())\n            details += plural(\"Model: {number} model{s}\", n_predictors)\n            if n_valid != n_predictors:\n                details += f\" ({n_predictors - n_valid} failed)\"\n            details += \"<ul>\"\n            for name in pred_names:\n                details += f\"<li>{name}</li>\"\n            details += \"</ul>\"\n        else:\n            details += \"Model:<br>No model on input.\"\n        return details\n\n    def _invalidate_predictions(self):\n        for i, pred in enumerate(self.predictors):\n            self.predictors[i] = pred._replace(results=None)\n\n    def _non_errored_predictors(self):\n        return [p for p in self.predictors\n                if isinstance(p.results, Results)]\n\n    def _reordered_probabilities(self, prediction):\n        cur_values = prediction.predictor.domain.class_var.values\n        new_ind = [self.class_values.index(x) for x in cur_values]\n        probs = prediction.results.unmapped_probabilities\n        new_probs = numpy.full(\n            (probs.shape[0], len(self.class_values)), numpy.nan)\n        new_probs[:, new_ind] = probs\n        return new_probs\n\n    def _update_predictions_model(self):\n        headers = []\n        all_values = []\n        all_probs = []\n        for p in self._non_errored_predictors():\n            values = p.results.unmapped_predicted\n            target = p.predictor.domain.class_var\n            if target.is_discrete:\n                # order probabilities in order from Show prob. for\n                prob = self._reordered_probabilities(p)\n                values = numpy.array(target.values)[values.astype(int)]\n            else:\n                prob = numpy.zeros((len(values), 0))\n            all_values.append(values)\n            all_probs.append(prob)\n            headers.append(p.predictor.name)\n\n        if all_values:\n            model = PredictionsModel(all_values, all_probs, headers)\n            model.list_sorted.connect(\n                partial(\n                    self._update_data_sort_order, self.predictionsview,\n                    self.dataview))\n        else:\n            model = None\n\n        if self.selection_store is not None:\n            self.selection_store.unregister(\n                self.predictionsview.selectionModel())\n\n        self.predictionsview.setModel(model)\n        if model is not None:\n            self.predictionsview.setSelectionModel(\n                SharedSelectionModel(self.get_selection_store(model),\n                                     model, self.predictionsview))\n\n        hheader = self.predictionsview.horizontalHeader()\n        hheader.setSortIndicatorShown(False)\n        hheader.setSectionsClickable(True)\n\n    def _update_data_sort_order(self, sort_source_view, sort_dest_view):\n        sort_source_view.horizontalHeader().setSortIndicatorShown(True)\n        sort_dest_view.horizontalHeader().setSortIndicatorShown(False)\n\n        sort_dest = sort_dest_view.model()\n        sort_source = sort_source_view.model()\n        if sort_dest is not None:\n            if sort_source is not None and sort_source.sortColumn() >= 0:\n                sort_dest.setSortIndices(sort_source.mapToSourceRows(...))\n            else:\n                sort_dest.setSortIndices(None)\n        self.commit()\n\n    def _reset_order(self):\n        datamodel = self.dataview.model()\n        predmodel = self.predictionsview.model()\n        if datamodel is not None:\n            datamodel.setSortIndices(None)\n            datamodel.sort(-1)\n        if predmodel is not None:\n            predmodel.setSortIndices(None)\n            predmodel.sort(-1)\n        self.predictionsview.horizontalHeader().setSortIndicatorShown(False)\n        self.dataview.horizontalHeader().setSortIndicatorShown(False)\n\n    def _all_color_values(self):\n        \"\"\"\n        Return list of colors together with their values from all predictors\n        classes. Colors and values are sorted according to the values order\n        for simpler comparison.\n        \"\"\"\n        predictors = self._non_errored_predictors()\n        color_values = [\n            list(zip(*sorted(zip(\n                p.predictor.domain.class_var.colors,\n                p.predictor.domain.class_var.values\n            ), key=itemgetter(1))))\n            for p in predictors if p.predictor.domain.class_var.is_discrete\n        ]\n        return color_values if color_values else [([], [])]\n\n    @staticmethod\n    def _colors_match(colors1, values1, color2, values2):\n        \"\"\"\n        Test whether colors for values match. Colors matches when all\n        values match for shorter list and colors match for shorter list.\n        It is assumed that values will be sorted together with their colors.\n        \"\"\"\n        shorter_length = min(len(colors1), len(color2))\n        return (values1[:shorter_length] == values2[:shorter_length]\n                and (numpy.array(colors1[:shorter_length]) ==\n                     numpy.array(color2[:shorter_length])).all())\n\n    def _get_colors(self):\n        \"\"\"\n        Defines colors for values. If colors match in all models use the union\n        otherwise use standard colors.\n        \"\"\"\n        all_colors_values = self._all_color_values()\n        base_color, base_values = all_colors_values[0]\n        for c, v in all_colors_values[1:]:\n            if not self._colors_match(base_color, base_values, c, v):\n                base_color = []\n                break\n            # replace base_color if longer\n            if len(v) > len(base_color):\n                base_color = c\n                base_values = v\n\n        if len(base_color) != len(self.class_values):\n            return LimitedDiscretePalette(len(self.class_values)).palette\n        # reorder colors to widgets order\n        colors = [None] * len(self.class_values)\n        for c, v in zip(base_color, base_values):\n            colors[self.class_values.index(v)] = c\n        return colors\n\n    def _update_prediction_delegate(self):\n        self._delegates.clear()\n        colors = self._get_colors()\n        for col, slot in enumerate(self.predictors):\n            target = slot.predictor.domain.class_var\n            shown_probs = (\n                () if target.is_continuous else\n                [val if self.class_values[val] in target.values else None\n                 for val in self.selected_classes]\n            )\n            delegate = PredictionsItemDelegate(\n                None if target.is_continuous else self.class_values,\n                colors,\n                shown_probs,\n                target.format_str if target.is_continuous else None,\n                parent=self.predictionsview\n            )\n            # QAbstractItemView does not take ownership of delegates, so we must\n            self._delegates.append(delegate)\n            self.predictionsview.setItemDelegateForColumn(col, delegate)\n            self.predictionsview.setColumnHidden(col, False)\n\n        self.predictionsview.resizeColumnsToContents()\n        self._recompute_splitter_sizes()\n        if self.predictionsview.model() is not None:\n            self.predictionsview.model().setProbInd(self.selected_classes)\n\n    def _recompute_splitter_sizes(self):\n        if not self.data:\n            return\n        view = self.predictionsview\n        self.left_width = \\\n            view.horizontalHeader().length() + view.verticalHeader().width()\n        self._update_splitter()\n\n    def _update_splitter(self):\n        w1, w2 = self.splitter.sizes()\n        self.splitter.setSizes([self.left_width, w1 + w2 - self.left_width])\n\n    def splitter_resized(self):\n        self.left_width = self.splitter.sizes()[0]\n\n    def commit(self):\n        self._commit_predictions()\n        self._commit_evaluation_results()\n\n    def _commit_evaluation_results(self):\n        slots = [p for p in self._non_errored_predictors()\n                 if p.results.predicted is not None]\n        if not slots:\n            self.Outputs.evaluation_results.send(None)\n            return\n\n        nanmask = numpy.isnan(self.data.get_column_view(self.class_var)[0])\n        data = self.data[~nanmask]\n        results = Results(data, store_data=True)\n        results.folds = None\n        results.row_indices = numpy.arange(len(data))\n        results.actual = data.Y.ravel()\n        results.predicted = numpy.vstack(\n            tuple(p.results.predicted[0][~nanmask] for p in slots))\n        if self.class_var and self.class_var.is_discrete:\n            results.probabilities = numpy.array(\n                [p.results.probabilities[0][~nanmask] for p in slots])\n        results.learner_names = [p.name for p in slots]\n        self.Outputs.evaluation_results.send(results)\n\n    def _commit_predictions(self):\n        if not self.data:\n            self.Outputs.predictions.send(None)\n            return\n\n        newmetas = []\n        newcolumns = []\n        for slot in self._non_errored_predictors():\n            if slot.predictor.domain.class_var.is_discrete:\n                self._add_classification_out_columns(slot, newmetas, newcolumns)\n            else:\n                self._add_regression_out_columns(slot, newmetas, newcolumns)\n\n        attrs = list(self.data.domain.attributes)\n        metas = list(self.data.domain.metas)\n        names = [var.name for var in chain(attrs, self.data.domain.class_vars, metas) if var]\n        uniq_newmetas = []\n        for new_ in newmetas:\n            uniq = get_unique_names(names, new_.name)\n            if uniq != new_.name:\n                new_ = new_.copy(name=uniq)\n            uniq_newmetas.append(new_)\n            names.append(uniq)\n\n        metas += uniq_newmetas\n        domain = Orange.data.Domain(attrs, self.class_var, metas=metas)\n        predictions = self.data.transform(domain)\n        if newcolumns:\n            newcolumns = numpy.hstack(\n                [numpy.atleast_2d(cols) for cols in newcolumns])\n            with predictions.unlocked(predictions.metas):\n                predictions.metas[:, -newcolumns.shape[1]:] = newcolumns\n\n        datamodel = self.dataview.model()\n        predmodel = self.predictionsview.model()\n        assert datamodel is not None  # because we have data\n        assert self.selection_store is not None\n        rows = numpy.array(list(self.selection_store.rows))\n        if rows.size:\n            # Reorder rows as they are ordered in view\n            shown_rows = datamodel.mapFromSourceRows(rows)\n            rows = rows[numpy.argsort(shown_rows)]\n            predictions = predictions[rows]\n        elif datamodel.sortColumn() >= 0 \\\n                or predmodel is not None and predmodel.sortColumn() > 0:\n            # No selection: output all, but in the shown order\n            predictions = predictions[datamodel.mapToSourceRows(...)]\n        self.Outputs.predictions.send(predictions)\n\n    @staticmethod\n    def _add_classification_out_columns(slot, newmetas, newcolumns):\n        # Mapped or unmapped predictions?!\n        # Or provide a checkbox so the user decides?\n        pred = slot.predictor\n        name = pred.name\n        values = pred.domain.class_var.values\n        newmetas.append(DiscreteVariable(name=name, values=values))\n        newcolumns.append(slot.results.unmapped_predicted.reshape(-1, 1))\n        newmetas += [ContinuousVariable(name=f\"{name} ({value})\")\n                     for value in values]\n        newcolumns.append(slot.results.unmapped_probabilities)\n\n    @staticmethod\n    def _add_regression_out_columns(slot, newmetas, newcolumns):\n        newmetas.append(ContinuousVariable(name=slot.predictor.name))\n        newcolumns.append(slot.results.unmapped_predicted.reshape((-1, 1)))\n\n    def send_report(self):\n        def merge_data_with_predictions():\n            data_model = self.dataview.model()\n            predictions_view = self.predictionsview\n            predictions_model = predictions_view.model()\n\n            # use ItemDelegate to style prediction values\n            delegates = [predictions_view.itemDelegateForColumn(i)\n                         for i in range(predictions_model.columnCount())]\n\n            # iterate only over visible columns of data's QTableView\n            iter_data_cols = list(filter(lambda x: not self.dataview.isColumnHidden(x),\n                                         range(data_model.columnCount())))\n\n            # print header\n            yield [''] + \\\n                  [predictions_model.headerData(col, Qt.Horizontal, Qt.DisplayRole)\n                   for col in range(predictions_model.columnCount())] + \\\n                  [data_model.headerData(col, Qt.Horizontal, Qt.DisplayRole)\n                   for col in iter_data_cols]\n\n            # print data & predictions\n            for i in range(data_model.rowCount()):\n                yield [data_model.headerData(i, Qt.Vertical, Qt.DisplayRole)] + \\\n                      [delegate.displayText(\n                          predictions_model.data(predictions_model.index(i, j)),\n                          QLocale())\n                       for j, delegate in enumerate(delegates)] + \\\n                      [data_model.data(data_model.index(i, j),\n                                       role=Qt.DisplayRole)\n                       for j in iter_data_cols]\n\n        if self.data:\n            text = self._get_details().replace('\\n', '<br>')\n            if self.selected_classes:\n                text += '<br>Showing probabilities for: '\n                text += ', '. join([self.class_values[i]\n                                    for i in self.selected_classes])\n            self.report_paragraph('Info', text)\n            self.report_table(\"Data & Predictions\", merge_data_with_predictions(),\n                              header_rows=1, header_columns=1)\n\n            self.report_table(\"Scores\", self.score_table.view)\n\n    def resizeEvent(self, event):\n        super().resizeEvent(event)\n        self._update_splitter()\n\n    def showEvent(self, event):\n        super().showEvent(event)\n        QTimer.singleShot(0, self._update_splitter)",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 282,
                            "end_lineno": 317,
                            "parent": {
                                "file_name": "Orange/widgets/evaluate/owpredictions.py",
                                "type": "class",
                                "name": "OWPredictions",
                                "content": "class OWPredictions(OWWidget):\n    name = \"Predictions\"\n    icon = \"icons/Predictions.svg\"\n    priority = 200\n    description = \"Display predictions of models for an input dataset.\"\n    keywords = []\n\n    buttons_area_orientation = None\n\n    class Inputs:\n        data = Input(\"Data\", Orange.data.Table)\n        predictors = MultiInput(\"Predictors\", Model, filter_none=True)\n\n    class Outputs:\n        predictions = Output(\"Predictions\", Orange.data.Table)\n        evaluation_results = Output(\"Evaluation Results\", Results)\n\n    class Warning(OWWidget.Warning):\n        empty_data = Msg(\"Empty dataset\")\n        wrong_targets = Msg(\n            \"Some model(s) predict a different target (see more ...)\\n{}\")\n\n    class Error(OWWidget.Error):\n        predictor_failed = Msg(\"Some predictor(s) failed (see more ...)\\n{}\")\n        scorer_failed = Msg(\"Some scorer(s) failed (see more ...)\\n{}\")\n\n    settingsHandler = settings.ClassValuesContextHandler()\n    score_table = settings.SettingProvider(ScoreTable)\n\n    #: List of selected class value indices in the `class_values` list\n    selected_classes = settings.ContextSetting([])\n    selection = settings.Setting([], schema_only=True)\n\n    def __init__(self):\n        super().__init__()\n\n        self.data = None  # type: Optional[Orange.data.Table]\n        self.predictors = []  # type: List[PredictorSlot]\n        self.class_values = []  # type: List[str]\n        self._delegates = []\n        self.left_width = 10\n        self.selection_store = None\n        self.__pending_selection = self.selection\n\n        controlBox = gui.vBox(self.controlArea, \"Show probabilities for\")\n\n        gui.listBox(controlBox, self, \"selected_classes\", \"class_values\",\n                    callback=self._update_prediction_delegate,\n                    selectionMode=QListWidget.ExtendedSelection,\n                    sizePolicy=(QSizePolicy.Preferred, QSizePolicy.MinimumExpanding),\n                    sizeHint=QSize(1, 350),\n                    minimumHeight=100)\n        self.reset_button = gui.button(\n            controlBox, self, \"Restore Original Order\",\n            callback=self._reset_order,\n            tooltip=\"Show rows in the original order\")\n\n        table_opts = dict(horizontalScrollBarPolicy=Qt.ScrollBarAlwaysOn,\n                          horizontalScrollMode=QTableView.ScrollPerPixel,\n                          selectionMode=QTableView.ExtendedSelection,\n                          focusPolicy=Qt.StrongFocus)\n        self.dataview = TableView(\n            sortingEnabled=True,\n            verticalScrollBarPolicy=Qt.ScrollBarAlwaysOn,\n            **table_opts)\n        self.predictionsview = TableView(\n            sortingEnabled=True,\n            verticalScrollBarPolicy=Qt.ScrollBarAlwaysOff,\n            **table_opts)\n        self.dataview.verticalHeader().hide()\n        dsbar = self.dataview.verticalScrollBar()\n        psbar = self.predictionsview.verticalScrollBar()\n        psbar.valueChanged.connect(dsbar.setValue)\n        dsbar.valueChanged.connect(psbar.setValue)\n\n        self.dataview.verticalHeader().setDefaultSectionSize(22)\n        self.predictionsview.verticalHeader().setDefaultSectionSize(22)\n        self.dataview.verticalHeader().sectionResized.connect(\n            lambda index, _, size:\n            self.predictionsview.verticalHeader().resizeSection(index, size))\n\n        self.dataview.setItemDelegate(DataItemDelegate(self.dataview))\n\n        self.splitter = QSplitter(\n            orientation=Qt.Horizontal, childrenCollapsible=False, handleWidth=2)\n        self.splitter.splitterMoved.connect(self.splitter_resized)\n        self.splitter.addWidget(self.predictionsview)\n        self.splitter.addWidget(self.dataview)\n\n        self.score_table = ScoreTable(self)\n        self.vsplitter = gui.vBox(self.mainArea)\n        self.vsplitter.layout().addWidget(self.splitter)\n        self.vsplitter.layout().addWidget(self.score_table.view)\n\n    def get_selection_store(self, model):\n        # Both models map the same, so it doesn't matter which one is used\n        # to initialize SharedSelectionStore\n        if self.selection_store is None:\n            self.selection_store = SharedSelectionStore(model)\n        return self.selection_store\n\n    @Inputs.data\n    @check_sql_input\n    def set_data(self, data):\n        self.Warning.empty_data(shown=data is not None and not data)\n        self.data = data\n        self.selection_store = None\n        if not data:\n            self.dataview.setModel(None)\n            self.predictionsview.setModel(None)\n        else:\n            # force full reset of the view's HeaderView state\n            self.dataview.setModel(None)\n            model = DataModel(data, parent=None)\n            self.dataview.setModel(model)\n            sel_model = SharedSelectionModel(\n                self.get_selection_store(model), model, self.dataview)\n            self.dataview.setSelectionModel(sel_model)\n            if self.__pending_selection is not None:\n                self.selection = self.__pending_selection\n                self.__pending_selection = None\n                self.selection_store.select_rows(\n                    set(self.selection), QItemSelectionModel.ClearAndSelect)\n            sel_model.selectionChanged.connect(self.commit)\n            sel_model.selectionChanged.connect(self._store_selection)\n\n            self.dataview.model().list_sorted.connect(\n                partial(\n                    self._update_data_sort_order, self.dataview,\n                    self.predictionsview))\n\n        self._invalidate_predictions()\n\n    def _store_selection(self):\n        self.selection = list(self.selection_store.rows)\n\n    @property\n    def class_var(self):\n        return self.data and self.data.domain.class_var\n\n    @Inputs.predictors\n    def set_predictor(self, index, predictor: Model):\n        item = self.predictors[index]\n        self.predictors[index] = item._replace(\n            predictor=predictor, name=predictor.name, results=None\n        )\n\n    @Inputs.predictors.insert\n    def insert_predictor(self, index, predictor: Model):\n        item = PredictorSlot(predictor, predictor.name, None)\n        self.predictors.insert(index, item)\n\n    @Inputs.predictors.remove\n    def remove_predictor(self, index):\n        self.predictors.pop(index)\n\n    def _set_class_values(self):\n        class_values = []\n        for slot in self.predictors:\n            class_var = slot.predictor.domain.class_var\n            if class_var and class_var.is_discrete:\n                for value in class_var.values:\n                    if value not in class_values:\n                        class_values.append(value)\n\n        if self.class_var and self.class_var.is_discrete:\n            values = self.class_var.values\n            self.class_values = sorted(\n                class_values, key=lambda val: val not in values)\n            self.selected_classes = [\n                i for i, name in enumerate(class_values) if name in values]\n        else:\n            self.class_values = class_values  # This assignment updates listview\n            self.selected_classes = []\n\n    def handleNewSignals(self):\n        # Disconnect the model: the model and the delegate will be inconsistent\n        # between _set_class_values and update_predictions_model.\n        self.predictionsview.setModel(None)\n        self._set_class_values()\n        self._call_predictors()\n        self._update_scores()\n        self._update_predictions_model()\n        self._update_prediction_delegate()\n        self._set_errors()\n        self.commit()\n\n    def _call_predictors(self):\n        if not self.data:\n            return\n        if self.class_var:\n            domain = self.data.domain\n            classless_data = self.data.transform(\n                Domain(domain.attributes, None, domain.metas))\n        else:\n            classless_data = self.data\n\n        for index, slot in enumerate(self.predictors):\n            if isinstance(slot.results, Results):\n                continue\n\n            predictor = slot.predictor\n            try:\n                if predictor.domain.class_var.is_discrete:\n                    pred, prob = predictor(classless_data, Model.ValueProbs)\n                else:\n                    pred = predictor(classless_data, Model.Value)\n                    prob = numpy.zeros((len(pred), 0))\n            except (ValueError, DomainTransformationError) as err:\n                self.predictors[index] = \\\n                    slot._replace(results=f\"{predictor.name}: {err}\")\n                continue\n\n            results = Results()\n            results.data = self.data\n            results.domain = self.data.domain\n            results.row_indices = numpy.arange(len(self.data))\n            results.folds = (Ellipsis, )\n            results.actual = self.data.Y\n            results.unmapped_probabilities = prob\n            results.unmapped_predicted = pred\n            results.probabilities = results.predicted = None\n            self.predictors[index] = slot._replace(results=results)\n\n            target = predictor.domain.class_var\n            if target != self.class_var:\n                continue\n\n            if target is not self.class_var and target.is_discrete:\n                backmappers, n_values = predictor.get_backmappers(self.data)\n                prob = predictor.backmap_probs(prob, n_values, backmappers)\n                pred = predictor.backmap_value(pred, prob, n_values, backmappers)\n            results.predicted = pred.reshape((1, len(self.data)))\n            results.probabilities = prob.reshape((1,) + prob.shape)\n\n    def _update_scores(self):\n        model = self.score_table.model\n        model.clear()\n        scorers = usable_scorers(self.class_var) if self.class_var else []\n        self.score_table.update_header(scorers)\n        errors = []\n        for pred in self.predictors:\n            results = pred.results\n            if not isinstance(results, Results) or results.predicted is None:\n                continue\n            row = [QStandardItem(learner_name(pred.predictor)),\n                   QStandardItem(\"N/A\"), QStandardItem(\"N/A\")]\n            for scorer in scorers:\n                item = QStandardItem()\n                try:\n                    score = scorer_caller(scorer, results)()[0]\n                    item.setText(f\"{score:.3f}\")\n                except Exception as exc:  # pylint: disable=broad-except\n                    item.setToolTip(str(exc))\n                    # false pos.; pylint: disable=unsupported-membership-test\n                    if scorer.name in self.score_table.shown_scores:\n                        errors.append(str(exc))\n                row.append(item)\n            self.score_table.model.appendRow(row)\n\n        view = self.score_table.view\n        if model.rowCount():\n            view.setVisible(True)\n            view.ensurePolished()\n            view.setFixedHeight(\n                5 + view.horizontalHeader().height() +\n                view.verticalHeader().sectionSize(0) * model.rowCount())\n        else:\n            view.setVisible(False)\n\n        self.Error.scorer_failed(\"\\n\".join(errors), shown=bool(errors))\n\n    def _set_errors(self):\n        # Not all predictors are run every time, so errors can't be collected\n        # in _call_predictors\n        errors = \"\\n\".join(\n            f\"- {p.predictor.name}: {p.results}\"\n            for p in self.predictors\n            if isinstance(p.results, str) and p.results)\n        self.Error.predictor_failed(errors, shown=bool(errors))\n\n        if self.class_var:\n            inv_targets = \"\\n\".join(\n                f\"- {pred.name} predicts '{pred.domain.class_var.name}'\"\n                for pred in (p.predictor for p in self.predictors\n                             if isinstance(p.results, Results)\n                             and p.results.probabilities is None))\n            self.Warning.wrong_targets(inv_targets, shown=bool(inv_targets))\n        else:\n            self.Warning.wrong_targets.clear()\n\n    def _get_details(self):\n        details = \"Data:<br>\"\n        details += format_summary_details(self.data, format=Qt.RichText)\n        details += \"<hr>\"\n        pred_names = [v.name for v in self.predictors]\n        n_predictors = len(self.predictors)\n        if n_predictors:\n            n_valid = len(self._non_errored_predictors())\n            details += plural(\"Model: {number} model{s}\", n_predictors)\n            if n_valid != n_predictors:\n                details += f\" ({n_predictors - n_valid} failed)\"\n            details += \"<ul>\"\n            for name in pred_names:\n                details += f\"<li>{name}</li>\"\n            details += \"</ul>\"\n        else:\n            details += \"Model:<br>No model on input.\"\n        return details\n\n    def _invalidate_predictions(self):\n        for i, pred in enumerate(self.predictors):\n            self.predictors[i] = pred._replace(results=None)\n\n    def _non_errored_predictors(self):\n        return [p for p in self.predictors\n                if isinstance(p.results, Results)]\n\n    def _reordered_probabilities(self, prediction):\n        cur_values = prediction.predictor.domain.class_var.values\n        new_ind = [self.class_values.index(x) for x in cur_values]\n        probs = prediction.results.unmapped_probabilities\n        new_probs = numpy.full(\n            (probs.shape[0], len(self.class_values)), numpy.nan)\n        new_probs[:, new_ind] = probs\n        return new_probs\n\n    def _update_predictions_model(self):\n        headers = []\n        all_values = []\n        all_probs = []\n        for p in self._non_errored_predictors():\n            values = p.results.unmapped_predicted\n            target = p.predictor.domain.class_var\n            if target.is_discrete:\n                # order probabilities in order from Show prob. for\n                prob = self._reordered_probabilities(p)\n                values = numpy.array(target.values)[values.astype(int)]\n            else:\n                prob = numpy.zeros((len(values), 0))\n            all_values.append(values)\n            all_probs.append(prob)\n            headers.append(p.predictor.name)\n\n        if all_values:\n            model = PredictionsModel(all_values, all_probs, headers)\n            model.list_sorted.connect(\n                partial(\n                    self._update_data_sort_order, self.predictionsview,\n                    self.dataview))\n        else:\n            model = None\n\n        if self.selection_store is not None:\n            self.selection_store.unregister(\n                self.predictionsview.selectionModel())\n\n        self.predictionsview.setModel(model)\n        if model is not None:\n            self.predictionsview.setSelectionModel(\n                SharedSelectionModel(self.get_selection_store(model),\n                                     model, self.predictionsview))\n\n        hheader = self.predictionsview.horizontalHeader()\n        hheader.setSortIndicatorShown(False)\n        hheader.setSectionsClickable(True)\n\n    def _update_data_sort_order(self, sort_source_view, sort_dest_view):\n        sort_source_view.horizontalHeader().setSortIndicatorShown(True)\n        sort_dest_view.horizontalHeader().setSortIndicatorShown(False)\n\n        sort_dest = sort_dest_view.model()\n        sort_source = sort_source_view.model()\n        if sort_dest is not None:\n            if sort_source is not None and sort_source.sortColumn() >= 0:\n                sort_dest.setSortIndices(sort_source.mapToSourceRows(...))\n            else:\n                sort_dest.setSortIndices(None)\n        self.commit()\n\n    def _reset_order(self):\n        datamodel = self.dataview.model()\n        predmodel = self.predictionsview.model()\n        if datamodel is not None:\n            datamodel.setSortIndices(None)\n            datamodel.sort(-1)\n        if predmodel is not None:\n            predmodel.setSortIndices(None)\n            predmodel.sort(-1)\n        self.predictionsview.horizontalHeader().setSortIndicatorShown(False)\n        self.dataview.horizontalHeader().setSortIndicatorShown(False)\n\n    def _all_color_values(self):\n        \"\"\"\n        Return list of colors together with their values from all predictors\n        classes. Colors and values are sorted according to the values order\n        for simpler comparison.\n        \"\"\"\n        predictors = self._non_errored_predictors()\n        color_values = [\n            list(zip(*sorted(zip(\n                p.predictor.domain.class_var.colors,\n                p.predictor.domain.class_var.values\n            ), key=itemgetter(1))))\n            for p in predictors if p.predictor.domain.class_var.is_discrete\n        ]\n        return color_values if color_values else [([], [])]\n\n    @staticmethod\n    def _colors_match(colors1, values1, color2, values2):\n        \"\"\"\n        Test whether colors for values match. Colors matches when all\n        values match for shorter list and colors match for shorter list.\n        It is assumed that values will be sorted together with their colors.\n        \"\"\"\n        shorter_length = min(len(colors1), len(color2))\n        return (values1[:shorter_length] == values2[:shorter_length]\n                and (numpy.array(colors1[:shorter_length]) ==\n                     numpy.array(color2[:shorter_length])).all())\n\n    def _get_colors(self):\n        \"\"\"\n        Defines colors for values. If colors match in all models use the union\n        otherwise use standard colors.\n        \"\"\"\n        all_colors_values = self._all_color_values()\n        base_color, base_values = all_colors_values[0]\n        for c, v in all_colors_values[1:]:\n            if not self._colors_match(base_color, base_values, c, v):\n                base_color = []\n                break\n            # replace base_color if longer\n            if len(v) > len(base_color):\n                base_color = c\n                base_values = v\n\n        if len(base_color) != len(self.class_values):\n            return LimitedDiscretePalette(len(self.class_values)).palette\n        # reorder colors to widgets order\n        colors = [None] * len(self.class_values)\n        for c, v in zip(base_color, base_values):\n            colors[self.class_values.index(v)] = c\n        return colors\n\n    def _update_prediction_delegate(self):\n        self._delegates.clear()\n        colors = self._get_colors()\n        for col, slot in enumerate(self.predictors):\n            target = slot.predictor.domain.class_var\n            shown_probs = (\n                () if target.is_continuous else\n                [val if self.class_values[val] in target.values else None\n                 for val in self.selected_classes]\n            )\n            delegate = PredictionsItemDelegate(\n                None if target.is_continuous else self.class_values,\n                colors,\n                shown_probs,\n                target.format_str if target.is_continuous else None,\n                parent=self.predictionsview\n            )\n            # QAbstractItemView does not take ownership of delegates, so we must\n            self._delegates.append(delegate)\n            self.predictionsview.setItemDelegateForColumn(col, delegate)\n            self.predictionsview.setColumnHidden(col, False)\n\n        self.predictionsview.resizeColumnsToContents()\n        self._recompute_splitter_sizes()\n        if self.predictionsview.model() is not None:\n            self.predictionsview.model().setProbInd(self.selected_classes)\n\n    def _recompute_splitter_sizes(self):\n        if not self.data:\n            return\n        view = self.predictionsview\n        self.left_width = \\\n            view.horizontalHeader().length() + view.verticalHeader().width()\n        self._update_splitter()\n\n    def _update_splitter(self):\n        w1, w2 = self.splitter.sizes()\n        self.splitter.setSizes([self.left_width, w1 + w2 - self.left_width])\n\n    def splitter_resized(self):\n        self.left_width = self.splitter.sizes()[0]\n\n    def commit(self):\n        self._commit_predictions()\n        self._commit_evaluation_results()\n\n    def _commit_evaluation_results(self):\n        slots = [p for p in self._non_errored_predictors()\n                 if p.results.predicted is not None]\n        if not slots:\n            self.Outputs.evaluation_results.send(None)\n            return\n\n        nanmask = numpy.isnan(self.data.get_column_view(self.class_var)[0])\n        data = self.data[~nanmask]\n        results = Results(data, store_data=True)\n        results.folds = None\n        results.row_indices = numpy.arange(len(data))\n        results.actual = data.Y.ravel()\n        results.predicted = numpy.vstack(\n            tuple(p.results.predicted[0][~nanmask] for p in slots))\n        if self.class_var and self.class_var.is_discrete:\n            results.probabilities = numpy.array(\n                [p.results.probabilities[0][~nanmask] for p in slots])\n        results.learner_names = [p.name for p in slots]\n        self.Outputs.evaluation_results.send(results)\n\n    def _commit_predictions(self):\n        if not self.data:\n            self.Outputs.predictions.send(None)\n            return\n\n        newmetas = []\n        newcolumns = []\n        for slot in self._non_errored_predictors():\n            if slot.predictor.domain.class_var.is_discrete:\n                self._add_classification_out_columns(slot, newmetas, newcolumns)\n            else:\n                self._add_regression_out_columns(slot, newmetas, newcolumns)\n\n        attrs = list(self.data.domain.attributes)\n        metas = list(self.data.domain.metas)\n        names = [var.name for var in chain(attrs, self.data.domain.class_vars, metas) if var]\n        uniq_newmetas = []\n        for new_ in newmetas:\n            uniq = get_unique_names(names, new_.name)\n            if uniq != new_.name:\n                new_ = new_.copy(name=uniq)\n            uniq_newmetas.append(new_)\n            names.append(uniq)\n\n        metas += uniq_newmetas\n        domain = Orange.data.Domain(attrs, self.class_var, metas=metas)\n        predictions = self.data.transform(domain)\n        if newcolumns:\n            newcolumns = numpy.hstack(\n                [numpy.atleast_2d(cols) for cols in newcolumns])\n            with predictions.unlocked(predictions.metas):\n                predictions.metas[:, -newcolumns.shape[1]:] = newcolumns\n\n        datamodel = self.dataview.model()\n        predmodel = self.predictionsview.model()\n        assert datamodel is not None  # because we have data\n        assert self.selection_store is not None\n        rows = numpy.array(list(self.selection_store.rows))\n        if rows.size:\n            # Reorder rows as they are ordered in view\n            shown_rows = datamodel.mapFromSourceRows(rows)\n            rows = rows[numpy.argsort(shown_rows)]\n            predictions = predictions[rows]\n        elif datamodel.sortColumn() >= 0 \\\n                or predmodel is not None and predmodel.sortColumn() > 0:\n            # No selection: output all, but in the shown order\n            predictions = predictions[datamodel.mapToSourceRows(...)]\n        self.Outputs.predictions.send(predictions)\n\n    @staticmethod\n    def _add_classification_out_columns(slot, newmetas, newcolumns):\n        # Mapped or unmapped predictions?!\n        # Or provide a checkbox so the user decides?\n        pred = slot.predictor\n        name = pred.name\n        values = pred.domain.class_var.values\n        newmetas.append(DiscreteVariable(name=name, values=values))\n        newcolumns.append(slot.results.unmapped_predicted.reshape(-1, 1))\n        newmetas += [ContinuousVariable(name=f\"{name} ({value})\")\n                     for value in values]\n        newcolumns.append(slot.results.unmapped_probabilities)\n\n    @staticmethod\n    def _add_regression_out_columns(slot, newmetas, newcolumns):\n        newmetas.append(ContinuousVariable(name=slot.predictor.name))\n        newcolumns.append(slot.results.unmapped_predicted.reshape((-1, 1)))\n\n    def send_report(self):\n        def merge_data_with_predictions():\n            data_model = self.dataview.model()\n            predictions_view = self.predictionsview\n            predictions_model = predictions_view.model()\n\n            # use ItemDelegate to style prediction values\n            delegates = [predictions_view.itemDelegateForColumn(i)\n                         for i in range(predictions_model.columnCount())]\n\n            # iterate only over visible columns of data's QTableView\n            iter_data_cols = list(filter(lambda x: not self.dataview.isColumnHidden(x),\n                                         range(data_model.columnCount())))\n\n            # print header\n            yield [''] + \\\n                  [predictions_model.headerData(col, Qt.Horizontal, Qt.DisplayRole)\n                   for col in range(predictions_model.columnCount())] + \\\n                  [data_model.headerData(col, Qt.Horizontal, Qt.DisplayRole)\n                   for col in iter_data_cols]\n\n            # print data & predictions\n            for i in range(data_model.rowCount()):\n                yield [data_model.headerData(i, Qt.Vertical, Qt.DisplayRole)] + \\\n                      [delegate.displayText(\n                          predictions_model.data(predictions_model.index(i, j)),\n                          QLocale())\n                       for j, delegate in enumerate(delegates)] + \\\n                      [data_model.data(data_model.index(i, j),\n                                       role=Qt.DisplayRole)\n                       for j in iter_data_cols]\n\n        if self.data:\n            text = self._get_details().replace('\\n', '<br>')\n            if self.selected_classes:\n                text += '<br>Showing probabilities for: '\n                text += ', '. join([self.class_values[i]\n                                    for i in self.selected_classes])\n            self.report_paragraph('Info', text)\n            self.report_table(\"Data & Predictions\", merge_data_with_predictions(),\n                              header_rows=1, header_columns=1)\n\n            self.report_table(\"Scores\", self.score_table.view)\n\n    def resizeEvent(self, event):\n        super().resizeEvent(event)\n        self._update_splitter()\n\n    def showEvent(self, event):\n        super().showEvent(event)\n        QTimer.singleShot(0, self._update_splitter)",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 47,
                                "end_lineno": 675,
                                "parent": null
                            }
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 332,
                            "length": 8
                        },
                        "new_range": {
                            "start": 352,
                            "length": 12
                        },
                        "section": "class OWPredictions(OWWidget):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "                             if isinstance(p.results, Results)",
                                "type": "context"
                            },
                            {
                                "content": "                             and p.results.probabilities is None))",
                                "type": "context"
                            },
                            {
                                "content": "            self.Warning.wrong_targets(inv_targets, shown=bool(inv_targets))",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "            show_warning = numpy.isnan(self.data.Y).any() and self.predictors",
                                "type": "added"
                            },
                            {
                                "content": "            self.Warning.missing_targets(shown=show_warning)",
                                "type": "added"
                            },
                            {
                                "content": "        else:",
                                "type": "context"
                            },
                            {
                                "content": "            self.Warning.wrong_targets.clear()",
                                "type": "context"
                            },
                            {
                                "content": "            self.Warning.missing_targets.clear()",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    def _get_details(self):",
                                "type": "context"
                            },
                            {
                                "content": "        details = \"Data:<br>\"",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "Orange/widgets/evaluate/owpredictions.py",
                            "type": "method",
                            "name": "OWPredictions._set_errors",
                            "content": "class OWPredictions(OWWidget):\n    name = \"Predictions\"\n    icon = \"icons/Predictions.svg\"\n    priority = 200\n    description = \"Display predictions of models for an input dataset.\"\n    keywords = []\n\n    buttons_area_orientation = None\n\n    class Inputs:\n        data = Input(\"Data\", Orange.data.Table)\n        predictors = MultiInput(\"Predictors\", Model, filter_none=True)\n\n    class Outputs:\n        predictions = Output(\"Predictions\", Orange.data.Table)\n        evaluation_results = Output(\"Evaluation Results\", Results)\n\n    class Warning(OWWidget.Warning):\n        empty_data = Msg(\"Empty dataset\")\n        wrong_targets = Msg(\n            \"Some model(s) predict a different target (see more ...)\\n{}\")\n        missing_targets = Msg(\"Instances with missing targets \"\n                              \"are ignored while scoring.\")\n\n    class Error(OWWidget.Error):\n        predictor_failed = Msg(\"Some predictor(s) failed (see more ...)\\n{}\")\n        scorer_failed = Msg(\"Some scorer(s) failed (see more ...)\\n{}\")\n\n    settingsHandler = settings.ClassValuesContextHandler()\n    score_table = settings.SettingProvider(ScoreTable)\n\n    #: List of selected class value indices in the `class_values` list\n    selected_classes = settings.ContextSetting([])\n    selection = settings.Setting([], schema_only=True)\n\n    def __init__(self):\n        super().__init__()\n\n        self.data = None  # type: Optional[Orange.data.Table]\n        self.predictors = []  # type: List[PredictorSlot]\n        self.class_values = []  # type: List[str]\n        self._delegates = []\n        self.left_width = 10\n        self.selection_store = None\n        self.__pending_selection = self.selection\n\n        controlBox = gui.vBox(self.controlArea, \"Show probabilities for\")\n\n        gui.listBox(controlBox, self, \"selected_classes\", \"class_values\",\n                    callback=self._update_prediction_delegate,\n                    selectionMode=QListWidget.ExtendedSelection,\n                    sizePolicy=(QSizePolicy.Preferred, QSizePolicy.MinimumExpanding),\n                    sizeHint=QSize(1, 350),\n                    minimumHeight=100)\n        self.reset_button = gui.button(\n            controlBox, self, \"Restore Original Order\",\n            callback=self._reset_order,\n            tooltip=\"Show rows in the original order\")\n\n        table_opts = dict(horizontalScrollBarPolicy=Qt.ScrollBarAlwaysOn,\n                          horizontalScrollMode=QTableView.ScrollPerPixel,\n                          selectionMode=QTableView.ExtendedSelection,\n                          focusPolicy=Qt.StrongFocus)\n        self.dataview = TableView(\n            sortingEnabled=True,\n            verticalScrollBarPolicy=Qt.ScrollBarAlwaysOn,\n            **table_opts)\n        self.predictionsview = TableView(\n            sortingEnabled=True,\n            verticalScrollBarPolicy=Qt.ScrollBarAlwaysOff,\n            **table_opts)\n        self.dataview.verticalHeader().hide()\n        dsbar = self.dataview.verticalScrollBar()\n        psbar = self.predictionsview.verticalScrollBar()\n        psbar.valueChanged.connect(dsbar.setValue)\n        dsbar.valueChanged.connect(psbar.setValue)\n\n        self.dataview.verticalHeader().setDefaultSectionSize(22)\n        self.predictionsview.verticalHeader().setDefaultSectionSize(22)\n        self.dataview.verticalHeader().sectionResized.connect(\n            lambda index, _, size:\n            self.predictionsview.verticalHeader().resizeSection(index, size))\n\n        self.dataview.setItemDelegate(DataItemDelegate(self.dataview))\n\n        self.splitter = QSplitter(\n            orientation=Qt.Horizontal, childrenCollapsible=False, handleWidth=2)\n        self.splitter.splitterMoved.connect(self.splitter_resized)\n        self.splitter.addWidget(self.predictionsview)\n        self.splitter.addWidget(self.dataview)\n\n        self.score_table = ScoreTable(self)\n        self.vsplitter = gui.vBox(self.mainArea)\n        self.vsplitter.layout().addWidget(self.splitter)\n        self.vsplitter.layout().addWidget(self.score_table.view)\n\n    def get_selection_store(self, model):\n        # Both models map the same, so it doesn't matter which one is used\n        # to initialize SharedSelectionStore\n        if self.selection_store is None:\n            self.selection_store = SharedSelectionStore(model)\n        return self.selection_store\n\n    @Inputs.data\n    @check_sql_input\n    def set_data(self, data):\n        self.Warning.empty_data(shown=data is not None and not data)\n        self.data = data\n        self.selection_store = None\n        if not data:\n            self.dataview.setModel(None)\n            self.predictionsview.setModel(None)\n        else:\n            # force full reset of the view's HeaderView state\n            self.dataview.setModel(None)\n            model = DataModel(data, parent=None)\n            self.dataview.setModel(model)\n            sel_model = SharedSelectionModel(\n                self.get_selection_store(model), model, self.dataview)\n            self.dataview.setSelectionModel(sel_model)\n            if self.__pending_selection is not None:\n                self.selection = self.__pending_selection\n                self.__pending_selection = None\n                self.selection_store.select_rows(\n                    set(self.selection), QItemSelectionModel.ClearAndSelect)\n            sel_model.selectionChanged.connect(self.commit)\n            sel_model.selectionChanged.connect(self._store_selection)\n\n            self.dataview.model().list_sorted.connect(\n                partial(\n                    self._update_data_sort_order, self.dataview,\n                    self.predictionsview))\n\n        self._invalidate_predictions()\n\n    def _store_selection(self):\n        self.selection = list(self.selection_store.rows)\n\n    @property\n    def class_var(self):\n        return self.data and self.data.domain.class_var\n\n    @Inputs.predictors\n    def set_predictor(self, index, predictor: Model):\n        item = self.predictors[index]\n        self.predictors[index] = item._replace(\n            predictor=predictor, name=predictor.name, results=None\n        )\n\n    @Inputs.predictors.insert\n    def insert_predictor(self, index, predictor: Model):\n        item = PredictorSlot(predictor, predictor.name, None)\n        self.predictors.insert(index, item)\n\n    @Inputs.predictors.remove\n    def remove_predictor(self, index):\n        self.predictors.pop(index)\n\n    def _set_class_values(self):\n        class_values = []\n        for slot in self.predictors:\n            class_var = slot.predictor.domain.class_var\n            if class_var and class_var.is_discrete:\n                for value in class_var.values:\n                    if value not in class_values:\n                        class_values.append(value)\n\n        if self.class_var and self.class_var.is_discrete:\n            values = self.class_var.values\n            self.class_values = sorted(\n                class_values, key=lambda val: val not in values)\n            self.selected_classes = [\n                i for i, name in enumerate(class_values) if name in values]\n        else:\n            self.class_values = class_values  # This assignment updates listview\n            self.selected_classes = []\n\n    def handleNewSignals(self):\n        # Disconnect the model: the model and the delegate will be inconsistent\n        # between _set_class_values and update_predictions_model.\n        self.predictionsview.setModel(None)\n        self._set_class_values()\n        self._call_predictors()\n        self._update_scores()\n        self._update_predictions_model()\n        self._update_prediction_delegate()\n        self._set_errors()\n        self.commit()\n\n    def _call_predictors(self):\n        if not self.data:\n            return\n        if self.class_var:\n            domain = self.data.domain\n            classless_data = self.data.transform(\n                Domain(domain.attributes, None, domain.metas))\n        else:\n            classless_data = self.data\n\n        for index, slot in enumerate(self.predictors):\n            if isinstance(slot.results, Results):\n                continue\n\n            predictor = slot.predictor\n            try:\n                if predictor.domain.class_var.is_discrete:\n                    pred, prob = predictor(classless_data, Model.ValueProbs)\n                else:\n                    pred = predictor(classless_data, Model.Value)\n                    prob = numpy.zeros((len(pred), 0))\n            except (ValueError, DomainTransformationError) as err:\n                self.predictors[index] = \\\n                    slot._replace(results=f\"{predictor.name}: {err}\")\n                continue\n\n            results = Results()\n            results.data = self.data\n            results.domain = self.data.domain\n            results.row_indices = numpy.arange(len(self.data))\n            results.folds = (Ellipsis, )\n            results.actual = self.data.Y\n            results.unmapped_probabilities = prob\n            results.unmapped_predicted = pred\n            results.probabilities = results.predicted = None\n            self.predictors[index] = slot._replace(results=results)\n\n            target = predictor.domain.class_var\n            if target != self.class_var:\n                continue\n\n            if target is not self.class_var and target.is_discrete:\n                backmappers, n_values = predictor.get_backmappers(self.data)\n                prob = predictor.backmap_probs(prob, n_values, backmappers)\n                pred = predictor.backmap_value(pred, prob, n_values, backmappers)\n            results.predicted = pred.reshape((1, len(self.data)))\n            results.probabilities = prob.reshape((1,) + prob.shape)\n\n    def _update_scores(self):\n        model = self.score_table.model\n        model.clear()\n        scorers = usable_scorers(self.class_var) if self.class_var else []\n        self.score_table.update_header(scorers)\n        errors = []\n        for pred in self.predictors:\n            results = pred.results\n            if not isinstance(results, Results) or results.predicted is None:\n                continue\n            row = [QStandardItem(learner_name(pred.predictor)),\n                   QStandardItem(\"N/A\"), QStandardItem(\"N/A\")]\n\n            actual = results.actual\n            predicted = results.predicted\n            probabilities = results.probabilities\n\n            mask = numpy.isnan(results.actual)\n            no_targets = mask.sum() == len(results.actual)\n            results.actual = results.actual[~mask]\n            results.predicted = results.predicted[:, ~mask]\n            results.probabilities = results.probabilities[:, ~mask]\n\n            for scorer in scorers:\n                item = QStandardItem()\n                if no_targets:\n                    item.setText(\"NA\")\n                else:\n                    try:\n                        score = scorer_caller(scorer, results)()[0]\n                        item.setText(f\"{score:.3f}\")\n                    except Exception as exc:  # pylint: disable=broad-except\n                        item.setToolTip(str(exc))\n                        # false pos.; pylint: disable=unsupported-membership-test\n                        if scorer.name in self.score_table.shown_scores:\n                            errors.append(str(exc))\n                row.append(item)\n            self.score_table.model.appendRow(row)\n\n            results.actual = actual\n            results.predicted = predicted\n            results.probabilities = probabilities\n\n        view = self.score_table.view\n        if model.rowCount():\n            view.setVisible(True)\n            view.ensurePolished()\n            view.setFixedHeight(\n                5 + view.horizontalHeader().height() +\n                view.verticalHeader().sectionSize(0) * model.rowCount())\n        else:\n            view.setVisible(False)\n\n        self.Error.scorer_failed(\"\\n\".join(errors), shown=bool(errors))\n\n    def _set_errors(self):\n        # Not all predictors are run every time, so errors can't be collected\n        # in _call_predictors\n        errors = \"\\n\".join(\n            f\"- {p.predictor.name}: {p.results}\"\n            for p in self.predictors\n            if isinstance(p.results, str) and p.results)\n        self.Error.predictor_failed(errors, shown=bool(errors))\n\n        if self.class_var:\n            inv_targets = \"\\n\".join(\n                f\"- {pred.name} predicts '{pred.domain.class_var.name}'\"\n                for pred in (p.predictor for p in self.predictors\n                             if isinstance(p.results, Results)\n                             and p.results.probabilities is None))\n            self.Warning.wrong_targets(inv_targets, shown=bool(inv_targets))\n\n            show_warning = numpy.isnan(self.data.Y).any() and self.predictors\n            self.Warning.missing_targets(shown=show_warning)\n        else:\n            self.Warning.wrong_targets.clear()\n            self.Warning.missing_targets.clear()\n\n    def _get_details(self):\n        details = \"Data:<br>\"\n        details += format_summary_details(self.data, format=Qt.RichText)\n        details += \"<hr>\"\n        pred_names = [v.name for v in self.predictors]\n        n_predictors = len(self.predictors)\n        if n_predictors:\n            n_valid = len(self._non_errored_predictors())\n            details += plural(\"Model: {number} model{s}\", n_predictors)\n            if n_valid != n_predictors:\n                details += f\" ({n_predictors - n_valid} failed)\"\n            details += \"<ul>\"\n            for name in pred_names:\n                details += f\"<li>{name}</li>\"\n            details += \"</ul>\"\n        else:\n            details += \"Model:<br>No model on input.\"\n        return details\n\n    def _invalidate_predictions(self):\n        for i, pred in enumerate(self.predictors):\n            self.predictors[i] = pred._replace(results=None)\n\n    def _non_errored_predictors(self):\n        return [p for p in self.predictors\n                if isinstance(p.results, Results)]\n\n    def _reordered_probabilities(self, prediction):\n        cur_values = prediction.predictor.domain.class_var.values\n        new_ind = [self.class_values.index(x) for x in cur_values]\n        probs = prediction.results.unmapped_probabilities\n        new_probs = numpy.full(\n            (probs.shape[0], len(self.class_values)), numpy.nan)\n        new_probs[:, new_ind] = probs\n        return new_probs\n\n    def _update_predictions_model(self):\n        headers = []\n        all_values = []\n        all_probs = []\n        for p in self._non_errored_predictors():\n            values = p.results.unmapped_predicted\n            target = p.predictor.domain.class_var\n            if target.is_discrete:\n                # order probabilities in order from Show prob. for\n                prob = self._reordered_probabilities(p)\n                values = numpy.array(target.values)[values.astype(int)]\n            else:\n                prob = numpy.zeros((len(values), 0))\n            all_values.append(values)\n            all_probs.append(prob)\n            headers.append(p.predictor.name)\n\n        if all_values:\n            model = PredictionsModel(all_values, all_probs, headers)\n            model.list_sorted.connect(\n                partial(\n                    self._update_data_sort_order, self.predictionsview,\n                    self.dataview))\n        else:\n            model = None\n\n        if self.selection_store is not None:\n            self.selection_store.unregister(\n                self.predictionsview.selectionModel())\n\n        self.predictionsview.setModel(model)\n        if model is not None:\n            self.predictionsview.setSelectionModel(\n                SharedSelectionModel(self.get_selection_store(model),\n                                     model, self.predictionsview))\n\n        hheader = self.predictionsview.horizontalHeader()\n        hheader.setSortIndicatorShown(False)\n        hheader.setSectionsClickable(True)\n\n    def _update_data_sort_order(self, sort_source_view, sort_dest_view):\n        sort_source_view.horizontalHeader().setSortIndicatorShown(True)\n        sort_dest_view.horizontalHeader().setSortIndicatorShown(False)\n\n        sort_dest = sort_dest_view.model()\n        sort_source = sort_source_view.model()\n        if sort_dest is not None:\n            if sort_source is not None and sort_source.sortColumn() >= 0:\n                sort_dest.setSortIndices(sort_source.mapToSourceRows(...))\n            else:\n                sort_dest.setSortIndices(None)\n        self.commit()\n\n    def _reset_order(self):\n        datamodel = self.dataview.model()\n        predmodel = self.predictionsview.model()\n        if datamodel is not None:\n            datamodel.setSortIndices(None)\n            datamodel.sort(-1)\n        if predmodel is not None:\n            predmodel.setSortIndices(None)\n            predmodel.sort(-1)\n        self.predictionsview.horizontalHeader().setSortIndicatorShown(False)\n        self.dataview.horizontalHeader().setSortIndicatorShown(False)\n\n    def _all_color_values(self):\n        \"\"\"\n        Return list of colors together with their values from all predictors\n        classes. Colors and values are sorted according to the values order\n        for simpler comparison.\n        \"\"\"\n        predictors = self._non_errored_predictors()\n        color_values = [\n            list(zip(*sorted(zip(\n                p.predictor.domain.class_var.colors,\n                p.predictor.domain.class_var.values\n            ), key=itemgetter(1))))\n            for p in predictors if p.predictor.domain.class_var.is_discrete\n        ]\n        return color_values if color_values else [([], [])]\n\n    @staticmethod\n    def _colors_match(colors1, values1, color2, values2):\n        \"\"\"\n        Test whether colors for values match. Colors matches when all\n        values match for shorter list and colors match for shorter list.\n        It is assumed that values will be sorted together with their colors.\n        \"\"\"\n        shorter_length = min(len(colors1), len(color2))\n        return (values1[:shorter_length] == values2[:shorter_length]\n                and (numpy.array(colors1[:shorter_length]) ==\n                     numpy.array(color2[:shorter_length])).all())\n\n    def _get_colors(self):\n        \"\"\"\n        Defines colors for values. If colors match in all models use the union\n        otherwise use standard colors.\n        \"\"\"\n        all_colors_values = self._all_color_values()\n        base_color, base_values = all_colors_values[0]\n        for c, v in all_colors_values[1:]:\n            if not self._colors_match(base_color, base_values, c, v):\n                base_color = []\n                break\n            # replace base_color if longer\n            if len(v) > len(base_color):\n                base_color = c\n                base_values = v\n\n        if len(base_color) != len(self.class_values):\n            return LimitedDiscretePalette(len(self.class_values)).palette\n        # reorder colors to widgets order\n        colors = [None] * len(self.class_values)\n        for c, v in zip(base_color, base_values):\n            colors[self.class_values.index(v)] = c\n        return colors\n\n    def _update_prediction_delegate(self):\n        self._delegates.clear()\n        colors = self._get_colors()\n        for col, slot in enumerate(self.predictors):\n            target = slot.predictor.domain.class_var\n            shown_probs = (\n                () if target.is_continuous else\n                [val if self.class_values[val] in target.values else None\n                 for val in self.selected_classes]\n            )\n            delegate = PredictionsItemDelegate(\n                None if target.is_continuous else self.class_values,\n                colors,\n                shown_probs,\n                target.format_str if target.is_continuous else None,\n                parent=self.predictionsview\n            )\n            # QAbstractItemView does not take ownership of delegates, so we must\n            self._delegates.append(delegate)\n            self.predictionsview.setItemDelegateForColumn(col, delegate)\n            self.predictionsview.setColumnHidden(col, False)\n\n        self.predictionsview.resizeColumnsToContents()\n        self._recompute_splitter_sizes()\n        if self.predictionsview.model() is not None:\n            self.predictionsview.model().setProbInd(self.selected_classes)\n\n    def _recompute_splitter_sizes(self):\n        if not self.data:\n            return\n        view = self.predictionsview\n        self.left_width = \\\n            view.horizontalHeader().length() + view.verticalHeader().width()\n        self._update_splitter()\n\n    def _update_splitter(self):\n        w1, w2 = self.splitter.sizes()\n        self.splitter.setSizes([self.left_width, w1 + w2 - self.left_width])\n\n    def splitter_resized(self):\n        self.left_width = self.splitter.sizes()[0]\n\n    def commit(self):\n        self._commit_predictions()\n        self._commit_evaluation_results()\n\n    def _commit_evaluation_results(self):\n        slots = [p for p in self._non_errored_predictors()\n                 if p.results.predicted is not None]\n        if not slots:\n            self.Outputs.evaluation_results.send(None)\n            return\n\n        nanmask = numpy.isnan(self.data.get_column_view(self.class_var)[0])\n        data = self.data[~nanmask]\n        results = Results(data, store_data=True)\n        results.folds = None\n        results.row_indices = numpy.arange(len(data))\n        results.actual = data.Y.ravel()\n        results.predicted = numpy.vstack(\n            tuple(p.results.predicted[0][~nanmask] for p in slots))\n        if self.class_var and self.class_var.is_discrete:\n            results.probabilities = numpy.array(\n                [p.results.probabilities[0][~nanmask] for p in slots])\n        results.learner_names = [p.name for p in slots]\n        self.Outputs.evaluation_results.send(results)\n\n    def _commit_predictions(self):\n        if not self.data:\n            self.Outputs.predictions.send(None)\n            return\n\n        newmetas = []\n        newcolumns = []\n        for slot in self._non_errored_predictors():\n            if slot.predictor.domain.class_var.is_discrete:\n                self._add_classification_out_columns(slot, newmetas, newcolumns)\n            else:\n                self._add_regression_out_columns(slot, newmetas, newcolumns)\n\n        attrs = list(self.data.domain.attributes)\n        metas = list(self.data.domain.metas)\n        names = [var.name for var in chain(attrs, self.data.domain.class_vars, metas) if var]\n        uniq_newmetas = []\n        for new_ in newmetas:\n            uniq = get_unique_names(names, new_.name)\n            if uniq != new_.name:\n                new_ = new_.copy(name=uniq)\n            uniq_newmetas.append(new_)\n            names.append(uniq)\n\n        metas += uniq_newmetas\n        domain = Orange.data.Domain(attrs, self.class_var, metas=metas)\n        predictions = self.data.transform(domain)\n        if newcolumns:\n            newcolumns = numpy.hstack(\n                [numpy.atleast_2d(cols) for cols in newcolumns])\n            with predictions.unlocked(predictions.metas):\n                predictions.metas[:, -newcolumns.shape[1]:] = newcolumns\n\n        datamodel = self.dataview.model()\n        predmodel = self.predictionsview.model()\n        assert datamodel is not None  # because we have data\n        assert self.selection_store is not None\n        rows = numpy.array(list(self.selection_store.rows))\n        if rows.size:\n            # Reorder rows as they are ordered in view\n            shown_rows = datamodel.mapFromSourceRows(rows)\n            rows = rows[numpy.argsort(shown_rows)]\n            predictions = predictions[rows]\n        elif datamodel.sortColumn() >= 0 \\\n                or predmodel is not None and predmodel.sortColumn() > 0:\n            # No selection: output all, but in the shown order\n            predictions = predictions[datamodel.mapToSourceRows(...)]\n        self.Outputs.predictions.send(predictions)\n\n    @staticmethod\n    def _add_classification_out_columns(slot, newmetas, newcolumns):\n        # Mapped or unmapped predictions?!\n        # Or provide a checkbox so the user decides?\n        pred = slot.predictor\n        name = pred.name\n        values = pred.domain.class_var.values\n        newmetas.append(DiscreteVariable(name=name, values=values))\n        newcolumns.append(slot.results.unmapped_predicted.reshape(-1, 1))\n        newmetas += [ContinuousVariable(name=f\"{name} ({value})\")\n                     for value in values]\n        newcolumns.append(slot.results.unmapped_probabilities)\n\n    @staticmethod\n    def _add_regression_out_columns(slot, newmetas, newcolumns):\n        newmetas.append(ContinuousVariable(name=slot.predictor.name))\n        newcolumns.append(slot.results.unmapped_predicted.reshape((-1, 1)))\n\n    def send_report(self):\n        def merge_data_with_predictions():\n            data_model = self.dataview.model()\n            predictions_view = self.predictionsview\n            predictions_model = predictions_view.model()\n\n            # use ItemDelegate to style prediction values\n            delegates = [predictions_view.itemDelegateForColumn(i)\n                         for i in range(predictions_model.columnCount())]\n\n            # iterate only over visible columns of data's QTableView\n            iter_data_cols = list(filter(lambda x: not self.dataview.isColumnHidden(x),\n                                         range(data_model.columnCount())))\n\n            # print header\n            yield [''] + \\\n                  [predictions_model.headerData(col, Qt.Horizontal, Qt.DisplayRole)\n                   for col in range(predictions_model.columnCount())] + \\\n                  [data_model.headerData(col, Qt.Horizontal, Qt.DisplayRole)\n                   for col in iter_data_cols]\n\n            # print data & predictions\n            for i in range(data_model.rowCount()):\n                yield [data_model.headerData(i, Qt.Vertical, Qt.DisplayRole)] + \\\n                      [delegate.displayText(\n                          predictions_model.data(predictions_model.index(i, j)),\n                          QLocale())\n                       for j, delegate in enumerate(delegates)] + \\\n                      [data_model.data(data_model.index(i, j),\n                                       role=Qt.DisplayRole)\n                       for j in iter_data_cols]\n\n        if self.data:\n            text = self._get_details().replace('\\n', '<br>')\n            if self.selected_classes:\n                text += '<br>Showing probabilities for: '\n                text += ', '. join([self.class_values[i]\n                                    for i in self.selected_classes])\n            self.report_paragraph('Info', text)\n            self.report_table(\"Data & Predictions\", merge_data_with_predictions(),\n                              header_rows=1, header_columns=1)\n\n            self.report_table(\"Scores\", self.score_table.view)\n\n    def resizeEvent(self, event):\n        super().resizeEvent(event)\n        self._update_splitter()\n\n    def showEvent(self, event):\n        super().showEvent(event)\n        QTimer.singleShot(0, self._update_splitter)",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 339,
                            "end_lineno": 360,
                            "parent": {
                                "file_name": "Orange/widgets/evaluate/owpredictions.py",
                                "type": "class",
                                "name": "OWPredictions",
                                "content": "class OWPredictions(OWWidget):\n    name = \"Predictions\"\n    icon = \"icons/Predictions.svg\"\n    priority = 200\n    description = \"Display predictions of models for an input dataset.\"\n    keywords = []\n\n    buttons_area_orientation = None\n\n    class Inputs:\n        data = Input(\"Data\", Orange.data.Table)\n        predictors = MultiInput(\"Predictors\", Model, filter_none=True)\n\n    class Outputs:\n        predictions = Output(\"Predictions\", Orange.data.Table)\n        evaluation_results = Output(\"Evaluation Results\", Results)\n\n    class Warning(OWWidget.Warning):\n        empty_data = Msg(\"Empty dataset\")\n        wrong_targets = Msg(\n            \"Some model(s) predict a different target (see more ...)\\n{}\")\n        missing_targets = Msg(\"Instances with missing targets \"\n                              \"are ignored while scoring.\")\n\n    class Error(OWWidget.Error):\n        predictor_failed = Msg(\"Some predictor(s) failed (see more ...)\\n{}\")\n        scorer_failed = Msg(\"Some scorer(s) failed (see more ...)\\n{}\")\n\n    settingsHandler = settings.ClassValuesContextHandler()\n    score_table = settings.SettingProvider(ScoreTable)\n\n    #: List of selected class value indices in the `class_values` list\n    selected_classes = settings.ContextSetting([])\n    selection = settings.Setting([], schema_only=True)\n\n    def __init__(self):\n        super().__init__()\n\n        self.data = None  # type: Optional[Orange.data.Table]\n        self.predictors = []  # type: List[PredictorSlot]\n        self.class_values = []  # type: List[str]\n        self._delegates = []\n        self.left_width = 10\n        self.selection_store = None\n        self.__pending_selection = self.selection\n\n        controlBox = gui.vBox(self.controlArea, \"Show probabilities for\")\n\n        gui.listBox(controlBox, self, \"selected_classes\", \"class_values\",\n                    callback=self._update_prediction_delegate,\n                    selectionMode=QListWidget.ExtendedSelection,\n                    sizePolicy=(QSizePolicy.Preferred, QSizePolicy.MinimumExpanding),\n                    sizeHint=QSize(1, 350),\n                    minimumHeight=100)\n        self.reset_button = gui.button(\n            controlBox, self, \"Restore Original Order\",\n            callback=self._reset_order,\n            tooltip=\"Show rows in the original order\")\n\n        table_opts = dict(horizontalScrollBarPolicy=Qt.ScrollBarAlwaysOn,\n                          horizontalScrollMode=QTableView.ScrollPerPixel,\n                          selectionMode=QTableView.ExtendedSelection,\n                          focusPolicy=Qt.StrongFocus)\n        self.dataview = TableView(\n            sortingEnabled=True,\n            verticalScrollBarPolicy=Qt.ScrollBarAlwaysOn,\n            **table_opts)\n        self.predictionsview = TableView(\n            sortingEnabled=True,\n            verticalScrollBarPolicy=Qt.ScrollBarAlwaysOff,\n            **table_opts)\n        self.dataview.verticalHeader().hide()\n        dsbar = self.dataview.verticalScrollBar()\n        psbar = self.predictionsview.verticalScrollBar()\n        psbar.valueChanged.connect(dsbar.setValue)\n        dsbar.valueChanged.connect(psbar.setValue)\n\n        self.dataview.verticalHeader().setDefaultSectionSize(22)\n        self.predictionsview.verticalHeader().setDefaultSectionSize(22)\n        self.dataview.verticalHeader().sectionResized.connect(\n            lambda index, _, size:\n            self.predictionsview.verticalHeader().resizeSection(index, size))\n\n        self.dataview.setItemDelegate(DataItemDelegate(self.dataview))\n\n        self.splitter = QSplitter(\n            orientation=Qt.Horizontal, childrenCollapsible=False, handleWidth=2)\n        self.splitter.splitterMoved.connect(self.splitter_resized)\n        self.splitter.addWidget(self.predictionsview)\n        self.splitter.addWidget(self.dataview)\n\n        self.score_table = ScoreTable(self)\n        self.vsplitter = gui.vBox(self.mainArea)\n        self.vsplitter.layout().addWidget(self.splitter)\n        self.vsplitter.layout().addWidget(self.score_table.view)\n\n    def get_selection_store(self, model):\n        # Both models map the same, so it doesn't matter which one is used\n        # to initialize SharedSelectionStore\n        if self.selection_store is None:\n            self.selection_store = SharedSelectionStore(model)\n        return self.selection_store\n\n    @Inputs.data\n    @check_sql_input\n    def set_data(self, data):\n        self.Warning.empty_data(shown=data is not None and not data)\n        self.data = data\n        self.selection_store = None\n        if not data:\n            self.dataview.setModel(None)\n            self.predictionsview.setModel(None)\n        else:\n            # force full reset of the view's HeaderView state\n            self.dataview.setModel(None)\n            model = DataModel(data, parent=None)\n            self.dataview.setModel(model)\n            sel_model = SharedSelectionModel(\n                self.get_selection_store(model), model, self.dataview)\n            self.dataview.setSelectionModel(sel_model)\n            if self.__pending_selection is not None:\n                self.selection = self.__pending_selection\n                self.__pending_selection = None\n                self.selection_store.select_rows(\n                    set(self.selection), QItemSelectionModel.ClearAndSelect)\n            sel_model.selectionChanged.connect(self.commit)\n            sel_model.selectionChanged.connect(self._store_selection)\n\n            self.dataview.model().list_sorted.connect(\n                partial(\n                    self._update_data_sort_order, self.dataview,\n                    self.predictionsview))\n\n        self._invalidate_predictions()\n\n    def _store_selection(self):\n        self.selection = list(self.selection_store.rows)\n\n    @property\n    def class_var(self):\n        return self.data and self.data.domain.class_var\n\n    @Inputs.predictors\n    def set_predictor(self, index, predictor: Model):\n        item = self.predictors[index]\n        self.predictors[index] = item._replace(\n            predictor=predictor, name=predictor.name, results=None\n        )\n\n    @Inputs.predictors.insert\n    def insert_predictor(self, index, predictor: Model):\n        item = PredictorSlot(predictor, predictor.name, None)\n        self.predictors.insert(index, item)\n\n    @Inputs.predictors.remove\n    def remove_predictor(self, index):\n        self.predictors.pop(index)\n\n    def _set_class_values(self):\n        class_values = []\n        for slot in self.predictors:\n            class_var = slot.predictor.domain.class_var\n            if class_var and class_var.is_discrete:\n                for value in class_var.values:\n                    if value not in class_values:\n                        class_values.append(value)\n\n        if self.class_var and self.class_var.is_discrete:\n            values = self.class_var.values\n            self.class_values = sorted(\n                class_values, key=lambda val: val not in values)\n            self.selected_classes = [\n                i for i, name in enumerate(class_values) if name in values]\n        else:\n            self.class_values = class_values  # This assignment updates listview\n            self.selected_classes = []\n\n    def handleNewSignals(self):\n        # Disconnect the model: the model and the delegate will be inconsistent\n        # between _set_class_values and update_predictions_model.\n        self.predictionsview.setModel(None)\n        self._set_class_values()\n        self._call_predictors()\n        self._update_scores()\n        self._update_predictions_model()\n        self._update_prediction_delegate()\n        self._set_errors()\n        self.commit()\n\n    def _call_predictors(self):\n        if not self.data:\n            return\n        if self.class_var:\n            domain = self.data.domain\n            classless_data = self.data.transform(\n                Domain(domain.attributes, None, domain.metas))\n        else:\n            classless_data = self.data\n\n        for index, slot in enumerate(self.predictors):\n            if isinstance(slot.results, Results):\n                continue\n\n            predictor = slot.predictor\n            try:\n                if predictor.domain.class_var.is_discrete:\n                    pred, prob = predictor(classless_data, Model.ValueProbs)\n                else:\n                    pred = predictor(classless_data, Model.Value)\n                    prob = numpy.zeros((len(pred), 0))\n            except (ValueError, DomainTransformationError) as err:\n                self.predictors[index] = \\\n                    slot._replace(results=f\"{predictor.name}: {err}\")\n                continue\n\n            results = Results()\n            results.data = self.data\n            results.domain = self.data.domain\n            results.row_indices = numpy.arange(len(self.data))\n            results.folds = (Ellipsis, )\n            results.actual = self.data.Y\n            results.unmapped_probabilities = prob\n            results.unmapped_predicted = pred\n            results.probabilities = results.predicted = None\n            self.predictors[index] = slot._replace(results=results)\n\n            target = predictor.domain.class_var\n            if target != self.class_var:\n                continue\n\n            if target is not self.class_var and target.is_discrete:\n                backmappers, n_values = predictor.get_backmappers(self.data)\n                prob = predictor.backmap_probs(prob, n_values, backmappers)\n                pred = predictor.backmap_value(pred, prob, n_values, backmappers)\n            results.predicted = pred.reshape((1, len(self.data)))\n            results.probabilities = prob.reshape((1,) + prob.shape)\n\n    def _update_scores(self):\n        model = self.score_table.model\n        model.clear()\n        scorers = usable_scorers(self.class_var) if self.class_var else []\n        self.score_table.update_header(scorers)\n        errors = []\n        for pred in self.predictors:\n            results = pred.results\n            if not isinstance(results, Results) or results.predicted is None:\n                continue\n            row = [QStandardItem(learner_name(pred.predictor)),\n                   QStandardItem(\"N/A\"), QStandardItem(\"N/A\")]\n\n            actual = results.actual\n            predicted = results.predicted\n            probabilities = results.probabilities\n\n            mask = numpy.isnan(results.actual)\n            no_targets = mask.sum() == len(results.actual)\n            results.actual = results.actual[~mask]\n            results.predicted = results.predicted[:, ~mask]\n            results.probabilities = results.probabilities[:, ~mask]\n\n            for scorer in scorers:\n                item = QStandardItem()\n                if no_targets:\n                    item.setText(\"NA\")\n                else:\n                    try:\n                        score = scorer_caller(scorer, results)()[0]\n                        item.setText(f\"{score:.3f}\")\n                    except Exception as exc:  # pylint: disable=broad-except\n                        item.setToolTip(str(exc))\n                        # false pos.; pylint: disable=unsupported-membership-test\n                        if scorer.name in self.score_table.shown_scores:\n                            errors.append(str(exc))\n                row.append(item)\n            self.score_table.model.appendRow(row)\n\n            results.actual = actual\n            results.predicted = predicted\n            results.probabilities = probabilities\n\n        view = self.score_table.view\n        if model.rowCount():\n            view.setVisible(True)\n            view.ensurePolished()\n            view.setFixedHeight(\n                5 + view.horizontalHeader().height() +\n                view.verticalHeader().sectionSize(0) * model.rowCount())\n        else:\n            view.setVisible(False)\n\n        self.Error.scorer_failed(\"\\n\".join(errors), shown=bool(errors))\n\n    def _set_errors(self):\n        # Not all predictors are run every time, so errors can't be collected\n        # in _call_predictors\n        errors = \"\\n\".join(\n            f\"- {p.predictor.name}: {p.results}\"\n            for p in self.predictors\n            if isinstance(p.results, str) and p.results)\n        self.Error.predictor_failed(errors, shown=bool(errors))\n\n        if self.class_var:\n            inv_targets = \"\\n\".join(\n                f\"- {pred.name} predicts '{pred.domain.class_var.name}'\"\n                for pred in (p.predictor for p in self.predictors\n                             if isinstance(p.results, Results)\n                             and p.results.probabilities is None))\n            self.Warning.wrong_targets(inv_targets, shown=bool(inv_targets))\n\n            show_warning = numpy.isnan(self.data.Y).any() and self.predictors\n            self.Warning.missing_targets(shown=show_warning)\n        else:\n            self.Warning.wrong_targets.clear()\n            self.Warning.missing_targets.clear()\n\n    def _get_details(self):\n        details = \"Data:<br>\"\n        details += format_summary_details(self.data, format=Qt.RichText)\n        details += \"<hr>\"\n        pred_names = [v.name for v in self.predictors]\n        n_predictors = len(self.predictors)\n        if n_predictors:\n            n_valid = len(self._non_errored_predictors())\n            details += plural(\"Model: {number} model{s}\", n_predictors)\n            if n_valid != n_predictors:\n                details += f\" ({n_predictors - n_valid} failed)\"\n            details += \"<ul>\"\n            for name in pred_names:\n                details += f\"<li>{name}</li>\"\n            details += \"</ul>\"\n        else:\n            details += \"Model:<br>No model on input.\"\n        return details\n\n    def _invalidate_predictions(self):\n        for i, pred in enumerate(self.predictors):\n            self.predictors[i] = pred._replace(results=None)\n\n    def _non_errored_predictors(self):\n        return [p for p in self.predictors\n                if isinstance(p.results, Results)]\n\n    def _reordered_probabilities(self, prediction):\n        cur_values = prediction.predictor.domain.class_var.values\n        new_ind = [self.class_values.index(x) for x in cur_values]\n        probs = prediction.results.unmapped_probabilities\n        new_probs = numpy.full(\n            (probs.shape[0], len(self.class_values)), numpy.nan)\n        new_probs[:, new_ind] = probs\n        return new_probs\n\n    def _update_predictions_model(self):\n        headers = []\n        all_values = []\n        all_probs = []\n        for p in self._non_errored_predictors():\n            values = p.results.unmapped_predicted\n            target = p.predictor.domain.class_var\n            if target.is_discrete:\n                # order probabilities in order from Show prob. for\n                prob = self._reordered_probabilities(p)\n                values = numpy.array(target.values)[values.astype(int)]\n            else:\n                prob = numpy.zeros((len(values), 0))\n            all_values.append(values)\n            all_probs.append(prob)\n            headers.append(p.predictor.name)\n\n        if all_values:\n            model = PredictionsModel(all_values, all_probs, headers)\n            model.list_sorted.connect(\n                partial(\n                    self._update_data_sort_order, self.predictionsview,\n                    self.dataview))\n        else:\n            model = None\n\n        if self.selection_store is not None:\n            self.selection_store.unregister(\n                self.predictionsview.selectionModel())\n\n        self.predictionsview.setModel(model)\n        if model is not None:\n            self.predictionsview.setSelectionModel(\n                SharedSelectionModel(self.get_selection_store(model),\n                                     model, self.predictionsview))\n\n        hheader = self.predictionsview.horizontalHeader()\n        hheader.setSortIndicatorShown(False)\n        hheader.setSectionsClickable(True)\n\n    def _update_data_sort_order(self, sort_source_view, sort_dest_view):\n        sort_source_view.horizontalHeader().setSortIndicatorShown(True)\n        sort_dest_view.horizontalHeader().setSortIndicatorShown(False)\n\n        sort_dest = sort_dest_view.model()\n        sort_source = sort_source_view.model()\n        if sort_dest is not None:\n            if sort_source is not None and sort_source.sortColumn() >= 0:\n                sort_dest.setSortIndices(sort_source.mapToSourceRows(...))\n            else:\n                sort_dest.setSortIndices(None)\n        self.commit()\n\n    def _reset_order(self):\n        datamodel = self.dataview.model()\n        predmodel = self.predictionsview.model()\n        if datamodel is not None:\n            datamodel.setSortIndices(None)\n            datamodel.sort(-1)\n        if predmodel is not None:\n            predmodel.setSortIndices(None)\n            predmodel.sort(-1)\n        self.predictionsview.horizontalHeader().setSortIndicatorShown(False)\n        self.dataview.horizontalHeader().setSortIndicatorShown(False)\n\n    def _all_color_values(self):\n        \"\"\"\n        Return list of colors together with their values from all predictors\n        classes. Colors and values are sorted according to the values order\n        for simpler comparison.\n        \"\"\"\n        predictors = self._non_errored_predictors()\n        color_values = [\n            list(zip(*sorted(zip(\n                p.predictor.domain.class_var.colors,\n                p.predictor.domain.class_var.values\n            ), key=itemgetter(1))))\n            for p in predictors if p.predictor.domain.class_var.is_discrete\n        ]\n        return color_values if color_values else [([], [])]\n\n    @staticmethod\n    def _colors_match(colors1, values1, color2, values2):\n        \"\"\"\n        Test whether colors for values match. Colors matches when all\n        values match for shorter list and colors match for shorter list.\n        It is assumed that values will be sorted together with their colors.\n        \"\"\"\n        shorter_length = min(len(colors1), len(color2))\n        return (values1[:shorter_length] == values2[:shorter_length]\n                and (numpy.array(colors1[:shorter_length]) ==\n                     numpy.array(color2[:shorter_length])).all())\n\n    def _get_colors(self):\n        \"\"\"\n        Defines colors for values. If colors match in all models use the union\n        otherwise use standard colors.\n        \"\"\"\n        all_colors_values = self._all_color_values()\n        base_color, base_values = all_colors_values[0]\n        for c, v in all_colors_values[1:]:\n            if not self._colors_match(base_color, base_values, c, v):\n                base_color = []\n                break\n            # replace base_color if longer\n            if len(v) > len(base_color):\n                base_color = c\n                base_values = v\n\n        if len(base_color) != len(self.class_values):\n            return LimitedDiscretePalette(len(self.class_values)).palette\n        # reorder colors to widgets order\n        colors = [None] * len(self.class_values)\n        for c, v in zip(base_color, base_values):\n            colors[self.class_values.index(v)] = c\n        return colors\n\n    def _update_prediction_delegate(self):\n        self._delegates.clear()\n        colors = self._get_colors()\n        for col, slot in enumerate(self.predictors):\n            target = slot.predictor.domain.class_var\n            shown_probs = (\n                () if target.is_continuous else\n                [val if self.class_values[val] in target.values else None\n                 for val in self.selected_classes]\n            )\n            delegate = PredictionsItemDelegate(\n                None if target.is_continuous else self.class_values,\n                colors,\n                shown_probs,\n                target.format_str if target.is_continuous else None,\n                parent=self.predictionsview\n            )\n            # QAbstractItemView does not take ownership of delegates, so we must\n            self._delegates.append(delegate)\n            self.predictionsview.setItemDelegateForColumn(col, delegate)\n            self.predictionsview.setColumnHidden(col, False)\n\n        self.predictionsview.resizeColumnsToContents()\n        self._recompute_splitter_sizes()\n        if self.predictionsview.model() is not None:\n            self.predictionsview.model().setProbInd(self.selected_classes)\n\n    def _recompute_splitter_sizes(self):\n        if not self.data:\n            return\n        view = self.predictionsview\n        self.left_width = \\\n            view.horizontalHeader().length() + view.verticalHeader().width()\n        self._update_splitter()\n\n    def _update_splitter(self):\n        w1, w2 = self.splitter.sizes()\n        self.splitter.setSizes([self.left_width, w1 + w2 - self.left_width])\n\n    def splitter_resized(self):\n        self.left_width = self.splitter.sizes()[0]\n\n    def commit(self):\n        self._commit_predictions()\n        self._commit_evaluation_results()\n\n    def _commit_evaluation_results(self):\n        slots = [p for p in self._non_errored_predictors()\n                 if p.results.predicted is not None]\n        if not slots:\n            self.Outputs.evaluation_results.send(None)\n            return\n\n        nanmask = numpy.isnan(self.data.get_column_view(self.class_var)[0])\n        data = self.data[~nanmask]\n        results = Results(data, store_data=True)\n        results.folds = None\n        results.row_indices = numpy.arange(len(data))\n        results.actual = data.Y.ravel()\n        results.predicted = numpy.vstack(\n            tuple(p.results.predicted[0][~nanmask] for p in slots))\n        if self.class_var and self.class_var.is_discrete:\n            results.probabilities = numpy.array(\n                [p.results.probabilities[0][~nanmask] for p in slots])\n        results.learner_names = [p.name for p in slots]\n        self.Outputs.evaluation_results.send(results)\n\n    def _commit_predictions(self):\n        if not self.data:\n            self.Outputs.predictions.send(None)\n            return\n\n        newmetas = []\n        newcolumns = []\n        for slot in self._non_errored_predictors():\n            if slot.predictor.domain.class_var.is_discrete:\n                self._add_classification_out_columns(slot, newmetas, newcolumns)\n            else:\n                self._add_regression_out_columns(slot, newmetas, newcolumns)\n\n        attrs = list(self.data.domain.attributes)\n        metas = list(self.data.domain.metas)\n        names = [var.name for var in chain(attrs, self.data.domain.class_vars, metas) if var]\n        uniq_newmetas = []\n        for new_ in newmetas:\n            uniq = get_unique_names(names, new_.name)\n            if uniq != new_.name:\n                new_ = new_.copy(name=uniq)\n            uniq_newmetas.append(new_)\n            names.append(uniq)\n\n        metas += uniq_newmetas\n        domain = Orange.data.Domain(attrs, self.class_var, metas=metas)\n        predictions = self.data.transform(domain)\n        if newcolumns:\n            newcolumns = numpy.hstack(\n                [numpy.atleast_2d(cols) for cols in newcolumns])\n            with predictions.unlocked(predictions.metas):\n                predictions.metas[:, -newcolumns.shape[1]:] = newcolumns\n\n        datamodel = self.dataview.model()\n        predmodel = self.predictionsview.model()\n        assert datamodel is not None  # because we have data\n        assert self.selection_store is not None\n        rows = numpy.array(list(self.selection_store.rows))\n        if rows.size:\n            # Reorder rows as they are ordered in view\n            shown_rows = datamodel.mapFromSourceRows(rows)\n            rows = rows[numpy.argsort(shown_rows)]\n            predictions = predictions[rows]\n        elif datamodel.sortColumn() >= 0 \\\n                or predmodel is not None and predmodel.sortColumn() > 0:\n            # No selection: output all, but in the shown order\n            predictions = predictions[datamodel.mapToSourceRows(...)]\n        self.Outputs.predictions.send(predictions)\n\n    @staticmethod\n    def _add_classification_out_columns(slot, newmetas, newcolumns):\n        # Mapped or unmapped predictions?!\n        # Or provide a checkbox so the user decides?\n        pred = slot.predictor\n        name = pred.name\n        values = pred.domain.class_var.values\n        newmetas.append(DiscreteVariable(name=name, values=values))\n        newcolumns.append(slot.results.unmapped_predicted.reshape(-1, 1))\n        newmetas += [ContinuousVariable(name=f\"{name} ({value})\")\n                     for value in values]\n        newcolumns.append(slot.results.unmapped_probabilities)\n\n    @staticmethod\n    def _add_regression_out_columns(slot, newmetas, newcolumns):\n        newmetas.append(ContinuousVariable(name=slot.predictor.name))\n        newcolumns.append(slot.results.unmapped_predicted.reshape((-1, 1)))\n\n    def send_report(self):\n        def merge_data_with_predictions():\n            data_model = self.dataview.model()\n            predictions_view = self.predictionsview\n            predictions_model = predictions_view.model()\n\n            # use ItemDelegate to style prediction values\n            delegates = [predictions_view.itemDelegateForColumn(i)\n                         for i in range(predictions_model.columnCount())]\n\n            # iterate only over visible columns of data's QTableView\n            iter_data_cols = list(filter(lambda x: not self.dataview.isColumnHidden(x),\n                                         range(data_model.columnCount())))\n\n            # print header\n            yield [''] + \\\n                  [predictions_model.headerData(col, Qt.Horizontal, Qt.DisplayRole)\n                   for col in range(predictions_model.columnCount())] + \\\n                  [data_model.headerData(col, Qt.Horizontal, Qt.DisplayRole)\n                   for col in iter_data_cols]\n\n            # print data & predictions\n            for i in range(data_model.rowCount()):\n                yield [data_model.headerData(i, Qt.Vertical, Qt.DisplayRole)] + \\\n                      [delegate.displayText(\n                          predictions_model.data(predictions_model.index(i, j)),\n                          QLocale())\n                       for j, delegate in enumerate(delegates)] + \\\n                      [data_model.data(data_model.index(i, j),\n                                       role=Qt.DisplayRole)\n                       for j in iter_data_cols]\n\n        if self.data:\n            text = self._get_details().replace('\\n', '<br>')\n            if self.selected_classes:\n                text += '<br>Showing probabilities for: '\n                text += ', '. join([self.class_values[i]\n                                    for i in self.selected_classes])\n            self.report_paragraph('Info', text)\n            self.report_table(\"Data & Predictions\", merge_data_with_predictions(),\n                              header_rows=1, header_columns=1)\n\n            self.report_table(\"Scores\", self.score_table.view)\n\n    def resizeEvent(self, event):\n        super().resizeEvent(event)\n        self._update_splitter()\n\n    def showEvent(self, event):\n        super().showEvent(event)\n        QTimer.singleShot(0, self._update_splitter)",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 47,
                                "end_lineno": 699,
                                "parent": null
                            }
                        },
                        {
                            "file_name": "Orange/widgets/evaluate/owpredictions.py",
                            "type": "class",
                            "name": "OWPredictions",
                            "content": "class OWPredictions(OWWidget):\n    name = \"Predictions\"\n    icon = \"icons/Predictions.svg\"\n    priority = 200\n    description = \"Display predictions of models for an input dataset.\"\n    keywords = []\n\n    buttons_area_orientation = None\n\n    class Inputs:\n        data = Input(\"Data\", Orange.data.Table)\n        predictors = MultiInput(\"Predictors\", Model, filter_none=True)\n\n    class Outputs:\n        predictions = Output(\"Predictions\", Orange.data.Table)\n        evaluation_results = Output(\"Evaluation Results\", Results)\n\n    class Warning(OWWidget.Warning):\n        empty_data = Msg(\"Empty dataset\")\n        wrong_targets = Msg(\n            \"Some model(s) predict a different target (see more ...)\\n{}\")\n        missing_targets = Msg(\"Instances with missing targets \"\n                              \"are ignored while scoring.\")\n\n    class Error(OWWidget.Error):\n        predictor_failed = Msg(\"Some predictor(s) failed (see more ...)\\n{}\")\n        scorer_failed = Msg(\"Some scorer(s) failed (see more ...)\\n{}\")\n\n    settingsHandler = settings.ClassValuesContextHandler()\n    score_table = settings.SettingProvider(ScoreTable)\n\n    #: List of selected class value indices in the `class_values` list\n    selected_classes = settings.ContextSetting([])\n    selection = settings.Setting([], schema_only=True)\n\n    def __init__(self):\n        super().__init__()\n\n        self.data = None  # type: Optional[Orange.data.Table]\n        self.predictors = []  # type: List[PredictorSlot]\n        self.class_values = []  # type: List[str]\n        self._delegates = []\n        self.left_width = 10\n        self.selection_store = None\n        self.__pending_selection = self.selection\n\n        controlBox = gui.vBox(self.controlArea, \"Show probabilities for\")\n\n        gui.listBox(controlBox, self, \"selected_classes\", \"class_values\",\n                    callback=self._update_prediction_delegate,\n                    selectionMode=QListWidget.ExtendedSelection,\n                    sizePolicy=(QSizePolicy.Preferred, QSizePolicy.MinimumExpanding),\n                    sizeHint=QSize(1, 350),\n                    minimumHeight=100)\n        self.reset_button = gui.button(\n            controlBox, self, \"Restore Original Order\",\n            callback=self._reset_order,\n            tooltip=\"Show rows in the original order\")\n\n        table_opts = dict(horizontalScrollBarPolicy=Qt.ScrollBarAlwaysOn,\n                          horizontalScrollMode=QTableView.ScrollPerPixel,\n                          selectionMode=QTableView.ExtendedSelection,\n                          focusPolicy=Qt.StrongFocus)\n        self.dataview = TableView(\n            sortingEnabled=True,\n            verticalScrollBarPolicy=Qt.ScrollBarAlwaysOn,\n            **table_opts)\n        self.predictionsview = TableView(\n            sortingEnabled=True,\n            verticalScrollBarPolicy=Qt.ScrollBarAlwaysOff,\n            **table_opts)\n        self.dataview.verticalHeader().hide()\n        dsbar = self.dataview.verticalScrollBar()\n        psbar = self.predictionsview.verticalScrollBar()\n        psbar.valueChanged.connect(dsbar.setValue)\n        dsbar.valueChanged.connect(psbar.setValue)\n\n        self.dataview.verticalHeader().setDefaultSectionSize(22)\n        self.predictionsview.verticalHeader().setDefaultSectionSize(22)\n        self.dataview.verticalHeader().sectionResized.connect(\n            lambda index, _, size:\n            self.predictionsview.verticalHeader().resizeSection(index, size))\n\n        self.dataview.setItemDelegate(DataItemDelegate(self.dataview))\n\n        self.splitter = QSplitter(\n            orientation=Qt.Horizontal, childrenCollapsible=False, handleWidth=2)\n        self.splitter.splitterMoved.connect(self.splitter_resized)\n        self.splitter.addWidget(self.predictionsview)\n        self.splitter.addWidget(self.dataview)\n\n        self.score_table = ScoreTable(self)\n        self.vsplitter = gui.vBox(self.mainArea)\n        self.vsplitter.layout().addWidget(self.splitter)\n        self.vsplitter.layout().addWidget(self.score_table.view)\n\n    def get_selection_store(self, model):\n        # Both models map the same, so it doesn't matter which one is used\n        # to initialize SharedSelectionStore\n        if self.selection_store is None:\n            self.selection_store = SharedSelectionStore(model)\n        return self.selection_store\n\n    @Inputs.data\n    @check_sql_input\n    def set_data(self, data):\n        self.Warning.empty_data(shown=data is not None and not data)\n        self.data = data\n        self.selection_store = None\n        if not data:\n            self.dataview.setModel(None)\n            self.predictionsview.setModel(None)\n        else:\n            # force full reset of the view's HeaderView state\n            self.dataview.setModel(None)\n            model = DataModel(data, parent=None)\n            self.dataview.setModel(model)\n            sel_model = SharedSelectionModel(\n                self.get_selection_store(model), model, self.dataview)\n            self.dataview.setSelectionModel(sel_model)\n            if self.__pending_selection is not None:\n                self.selection = self.__pending_selection\n                self.__pending_selection = None\n                self.selection_store.select_rows(\n                    set(self.selection), QItemSelectionModel.ClearAndSelect)\n            sel_model.selectionChanged.connect(self.commit)\n            sel_model.selectionChanged.connect(self._store_selection)\n\n            self.dataview.model().list_sorted.connect(\n                partial(\n                    self._update_data_sort_order, self.dataview,\n                    self.predictionsview))\n\n        self._invalidate_predictions()\n\n    def _store_selection(self):\n        self.selection = list(self.selection_store.rows)\n\n    @property\n    def class_var(self):\n        return self.data and self.data.domain.class_var\n\n    @Inputs.predictors\n    def set_predictor(self, index, predictor: Model):\n        item = self.predictors[index]\n        self.predictors[index] = item._replace(\n            predictor=predictor, name=predictor.name, results=None\n        )\n\n    @Inputs.predictors.insert\n    def insert_predictor(self, index, predictor: Model):\n        item = PredictorSlot(predictor, predictor.name, None)\n        self.predictors.insert(index, item)\n\n    @Inputs.predictors.remove\n    def remove_predictor(self, index):\n        self.predictors.pop(index)\n\n    def _set_class_values(self):\n        class_values = []\n        for slot in self.predictors:\n            class_var = slot.predictor.domain.class_var\n            if class_var and class_var.is_discrete:\n                for value in class_var.values:\n                    if value not in class_values:\n                        class_values.append(value)\n\n        if self.class_var and self.class_var.is_discrete:\n            values = self.class_var.values\n            self.class_values = sorted(\n                class_values, key=lambda val: val not in values)\n            self.selected_classes = [\n                i for i, name in enumerate(class_values) if name in values]\n        else:\n            self.class_values = class_values  # This assignment updates listview\n            self.selected_classes = []\n\n    def handleNewSignals(self):\n        # Disconnect the model: the model and the delegate will be inconsistent\n        # between _set_class_values and update_predictions_model.\n        self.predictionsview.setModel(None)\n        self._set_class_values()\n        self._call_predictors()\n        self._update_scores()\n        self._update_predictions_model()\n        self._update_prediction_delegate()\n        self._set_errors()\n        self.commit()\n\n    def _call_predictors(self):\n        if not self.data:\n            return\n        if self.class_var:\n            domain = self.data.domain\n            classless_data = self.data.transform(\n                Domain(domain.attributes, None, domain.metas))\n        else:\n            classless_data = self.data\n\n        for index, slot in enumerate(self.predictors):\n            if isinstance(slot.results, Results):\n                continue\n\n            predictor = slot.predictor\n            try:\n                if predictor.domain.class_var.is_discrete:\n                    pred, prob = predictor(classless_data, Model.ValueProbs)\n                else:\n                    pred = predictor(classless_data, Model.Value)\n                    prob = numpy.zeros((len(pred), 0))\n            except (ValueError, DomainTransformationError) as err:\n                self.predictors[index] = \\\n                    slot._replace(results=f\"{predictor.name}: {err}\")\n                continue\n\n            results = Results()\n            results.data = self.data\n            results.domain = self.data.domain\n            results.row_indices = numpy.arange(len(self.data))\n            results.folds = (Ellipsis, )\n            results.actual = self.data.Y\n            results.unmapped_probabilities = prob\n            results.unmapped_predicted = pred\n            results.probabilities = results.predicted = None\n            self.predictors[index] = slot._replace(results=results)\n\n            target = predictor.domain.class_var\n            if target != self.class_var:\n                continue\n\n            if target is not self.class_var and target.is_discrete:\n                backmappers, n_values = predictor.get_backmappers(self.data)\n                prob = predictor.backmap_probs(prob, n_values, backmappers)\n                pred = predictor.backmap_value(pred, prob, n_values, backmappers)\n            results.predicted = pred.reshape((1, len(self.data)))\n            results.probabilities = prob.reshape((1,) + prob.shape)\n\n    def _update_scores(self):\n        model = self.score_table.model\n        model.clear()\n        scorers = usable_scorers(self.class_var) if self.class_var else []\n        self.score_table.update_header(scorers)\n        errors = []\n        for pred in self.predictors:\n            results = pred.results\n            if not isinstance(results, Results) or results.predicted is None:\n                continue\n            row = [QStandardItem(learner_name(pred.predictor)),\n                   QStandardItem(\"N/A\"), QStandardItem(\"N/A\")]\n\n            actual = results.actual\n            predicted = results.predicted\n            probabilities = results.probabilities\n\n            mask = numpy.isnan(results.actual)\n            no_targets = mask.sum() == len(results.actual)\n            results.actual = results.actual[~mask]\n            results.predicted = results.predicted[:, ~mask]\n            results.probabilities = results.probabilities[:, ~mask]\n\n            for scorer in scorers:\n                item = QStandardItem()\n                if no_targets:\n                    item.setText(\"NA\")\n                else:\n                    try:\n                        score = scorer_caller(scorer, results)()[0]\n                        item.setText(f\"{score:.3f}\")\n                    except Exception as exc:  # pylint: disable=broad-except\n                        item.setToolTip(str(exc))\n                        # false pos.; pylint: disable=unsupported-membership-test\n                        if scorer.name in self.score_table.shown_scores:\n                            errors.append(str(exc))\n                row.append(item)\n            self.score_table.model.appendRow(row)\n\n            results.actual = actual\n            results.predicted = predicted\n            results.probabilities = probabilities\n\n        view = self.score_table.view\n        if model.rowCount():\n            view.setVisible(True)\n            view.ensurePolished()\n            view.setFixedHeight(\n                5 + view.horizontalHeader().height() +\n                view.verticalHeader().sectionSize(0) * model.rowCount())\n        else:\n            view.setVisible(False)\n\n        self.Error.scorer_failed(\"\\n\".join(errors), shown=bool(errors))\n\n    def _set_errors(self):\n        # Not all predictors are run every time, so errors can't be collected\n        # in _call_predictors\n        errors = \"\\n\".join(\n            f\"- {p.predictor.name}: {p.results}\"\n            for p in self.predictors\n            if isinstance(p.results, str) and p.results)\n        self.Error.predictor_failed(errors, shown=bool(errors))\n\n        if self.class_var:\n            inv_targets = \"\\n\".join(\n                f\"- {pred.name} predicts '{pred.domain.class_var.name}'\"\n                for pred in (p.predictor for p in self.predictors\n                             if isinstance(p.results, Results)\n                             and p.results.probabilities is None))\n            self.Warning.wrong_targets(inv_targets, shown=bool(inv_targets))\n\n            show_warning = numpy.isnan(self.data.Y).any() and self.predictors\n            self.Warning.missing_targets(shown=show_warning)\n        else:\n            self.Warning.wrong_targets.clear()\n            self.Warning.missing_targets.clear()\n\n    def _get_details(self):\n        details = \"Data:<br>\"\n        details += format_summary_details(self.data, format=Qt.RichText)\n        details += \"<hr>\"\n        pred_names = [v.name for v in self.predictors]\n        n_predictors = len(self.predictors)\n        if n_predictors:\n            n_valid = len(self._non_errored_predictors())\n            details += plural(\"Model: {number} model{s}\", n_predictors)\n            if n_valid != n_predictors:\n                details += f\" ({n_predictors - n_valid} failed)\"\n            details += \"<ul>\"\n            for name in pred_names:\n                details += f\"<li>{name}</li>\"\n            details += \"</ul>\"\n        else:\n            details += \"Model:<br>No model on input.\"\n        return details\n\n    def _invalidate_predictions(self):\n        for i, pred in enumerate(self.predictors):\n            self.predictors[i] = pred._replace(results=None)\n\n    def _non_errored_predictors(self):\n        return [p for p in self.predictors\n                if isinstance(p.results, Results)]\n\n    def _reordered_probabilities(self, prediction):\n        cur_values = prediction.predictor.domain.class_var.values\n        new_ind = [self.class_values.index(x) for x in cur_values]\n        probs = prediction.results.unmapped_probabilities\n        new_probs = numpy.full(\n            (probs.shape[0], len(self.class_values)), numpy.nan)\n        new_probs[:, new_ind] = probs\n        return new_probs\n\n    def _update_predictions_model(self):\n        headers = []\n        all_values = []\n        all_probs = []\n        for p in self._non_errored_predictors():\n            values = p.results.unmapped_predicted\n            target = p.predictor.domain.class_var\n            if target.is_discrete:\n                # order probabilities in order from Show prob. for\n                prob = self._reordered_probabilities(p)\n                values = numpy.array(target.values)[values.astype(int)]\n            else:\n                prob = numpy.zeros((len(values), 0))\n            all_values.append(values)\n            all_probs.append(prob)\n            headers.append(p.predictor.name)\n\n        if all_values:\n            model = PredictionsModel(all_values, all_probs, headers)\n            model.list_sorted.connect(\n                partial(\n                    self._update_data_sort_order, self.predictionsview,\n                    self.dataview))\n        else:\n            model = None\n\n        if self.selection_store is not None:\n            self.selection_store.unregister(\n                self.predictionsview.selectionModel())\n\n        self.predictionsview.setModel(model)\n        if model is not None:\n            self.predictionsview.setSelectionModel(\n                SharedSelectionModel(self.get_selection_store(model),\n                                     model, self.predictionsview))\n\n        hheader = self.predictionsview.horizontalHeader()\n        hheader.setSortIndicatorShown(False)\n        hheader.setSectionsClickable(True)\n\n    def _update_data_sort_order(self, sort_source_view, sort_dest_view):\n        sort_source_view.horizontalHeader().setSortIndicatorShown(True)\n        sort_dest_view.horizontalHeader().setSortIndicatorShown(False)\n\n        sort_dest = sort_dest_view.model()\n        sort_source = sort_source_view.model()\n        if sort_dest is not None:\n            if sort_source is not None and sort_source.sortColumn() >= 0:\n                sort_dest.setSortIndices(sort_source.mapToSourceRows(...))\n            else:\n                sort_dest.setSortIndices(None)\n        self.commit()\n\n    def _reset_order(self):\n        datamodel = self.dataview.model()\n        predmodel = self.predictionsview.model()\n        if datamodel is not None:\n            datamodel.setSortIndices(None)\n            datamodel.sort(-1)\n        if predmodel is not None:\n            predmodel.setSortIndices(None)\n            predmodel.sort(-1)\n        self.predictionsview.horizontalHeader().setSortIndicatorShown(False)\n        self.dataview.horizontalHeader().setSortIndicatorShown(False)\n\n    def _all_color_values(self):\n        \"\"\"\n        Return list of colors together with their values from all predictors\n        classes. Colors and values are sorted according to the values order\n        for simpler comparison.\n        \"\"\"\n        predictors = self._non_errored_predictors()\n        color_values = [\n            list(zip(*sorted(zip(\n                p.predictor.domain.class_var.colors,\n                p.predictor.domain.class_var.values\n            ), key=itemgetter(1))))\n            for p in predictors if p.predictor.domain.class_var.is_discrete\n        ]\n        return color_values if color_values else [([], [])]\n\n    @staticmethod\n    def _colors_match(colors1, values1, color2, values2):\n        \"\"\"\n        Test whether colors for values match. Colors matches when all\n        values match for shorter list and colors match for shorter list.\n        It is assumed that values will be sorted together with their colors.\n        \"\"\"\n        shorter_length = min(len(colors1), len(color2))\n        return (values1[:shorter_length] == values2[:shorter_length]\n                and (numpy.array(colors1[:shorter_length]) ==\n                     numpy.array(color2[:shorter_length])).all())\n\n    def _get_colors(self):\n        \"\"\"\n        Defines colors for values. If colors match in all models use the union\n        otherwise use standard colors.\n        \"\"\"\n        all_colors_values = self._all_color_values()\n        base_color, base_values = all_colors_values[0]\n        for c, v in all_colors_values[1:]:\n            if not self._colors_match(base_color, base_values, c, v):\n                base_color = []\n                break\n            # replace base_color if longer\n            if len(v) > len(base_color):\n                base_color = c\n                base_values = v\n\n        if len(base_color) != len(self.class_values):\n            return LimitedDiscretePalette(len(self.class_values)).palette\n        # reorder colors to widgets order\n        colors = [None] * len(self.class_values)\n        for c, v in zip(base_color, base_values):\n            colors[self.class_values.index(v)] = c\n        return colors\n\n    def _update_prediction_delegate(self):\n        self._delegates.clear()\n        colors = self._get_colors()\n        for col, slot in enumerate(self.predictors):\n            target = slot.predictor.domain.class_var\n            shown_probs = (\n                () if target.is_continuous else\n                [val if self.class_values[val] in target.values else None\n                 for val in self.selected_classes]\n            )\n            delegate = PredictionsItemDelegate(\n                None if target.is_continuous else self.class_values,\n                colors,\n                shown_probs,\n                target.format_str if target.is_continuous else None,\n                parent=self.predictionsview\n            )\n            # QAbstractItemView does not take ownership of delegates, so we must\n            self._delegates.append(delegate)\n            self.predictionsview.setItemDelegateForColumn(col, delegate)\n            self.predictionsview.setColumnHidden(col, False)\n\n        self.predictionsview.resizeColumnsToContents()\n        self._recompute_splitter_sizes()\n        if self.predictionsview.model() is not None:\n            self.predictionsview.model().setProbInd(self.selected_classes)\n\n    def _recompute_splitter_sizes(self):\n        if not self.data:\n            return\n        view = self.predictionsview\n        self.left_width = \\\n            view.horizontalHeader().length() + view.verticalHeader().width()\n        self._update_splitter()\n\n    def _update_splitter(self):\n        w1, w2 = self.splitter.sizes()\n        self.splitter.setSizes([self.left_width, w1 + w2 - self.left_width])\n\n    def splitter_resized(self):\n        self.left_width = self.splitter.sizes()[0]\n\n    def commit(self):\n        self._commit_predictions()\n        self._commit_evaluation_results()\n\n    def _commit_evaluation_results(self):\n        slots = [p for p in self._non_errored_predictors()\n                 if p.results.predicted is not None]\n        if not slots:\n            self.Outputs.evaluation_results.send(None)\n            return\n\n        nanmask = numpy.isnan(self.data.get_column_view(self.class_var)[0])\n        data = self.data[~nanmask]\n        results = Results(data, store_data=True)\n        results.folds = None\n        results.row_indices = numpy.arange(len(data))\n        results.actual = data.Y.ravel()\n        results.predicted = numpy.vstack(\n            tuple(p.results.predicted[0][~nanmask] for p in slots))\n        if self.class_var and self.class_var.is_discrete:\n            results.probabilities = numpy.array(\n                [p.results.probabilities[0][~nanmask] for p in slots])\n        results.learner_names = [p.name for p in slots]\n        self.Outputs.evaluation_results.send(results)\n\n    def _commit_predictions(self):\n        if not self.data:\n            self.Outputs.predictions.send(None)\n            return\n\n        newmetas = []\n        newcolumns = []\n        for slot in self._non_errored_predictors():\n            if slot.predictor.domain.class_var.is_discrete:\n                self._add_classification_out_columns(slot, newmetas, newcolumns)\n            else:\n                self._add_regression_out_columns(slot, newmetas, newcolumns)\n\n        attrs = list(self.data.domain.attributes)\n        metas = list(self.data.domain.metas)\n        names = [var.name for var in chain(attrs, self.data.domain.class_vars, metas) if var]\n        uniq_newmetas = []\n        for new_ in newmetas:\n            uniq = get_unique_names(names, new_.name)\n            if uniq != new_.name:\n                new_ = new_.copy(name=uniq)\n            uniq_newmetas.append(new_)\n            names.append(uniq)\n\n        metas += uniq_newmetas\n        domain = Orange.data.Domain(attrs, self.class_var, metas=metas)\n        predictions = self.data.transform(domain)\n        if newcolumns:\n            newcolumns = numpy.hstack(\n                [numpy.atleast_2d(cols) for cols in newcolumns])\n            with predictions.unlocked(predictions.metas):\n                predictions.metas[:, -newcolumns.shape[1]:] = newcolumns\n\n        datamodel = self.dataview.model()\n        predmodel = self.predictionsview.model()\n        assert datamodel is not None  # because we have data\n        assert self.selection_store is not None\n        rows = numpy.array(list(self.selection_store.rows))\n        if rows.size:\n            # Reorder rows as they are ordered in view\n            shown_rows = datamodel.mapFromSourceRows(rows)\n            rows = rows[numpy.argsort(shown_rows)]\n            predictions = predictions[rows]\n        elif datamodel.sortColumn() >= 0 \\\n                or predmodel is not None and predmodel.sortColumn() > 0:\n            # No selection: output all, but in the shown order\n            predictions = predictions[datamodel.mapToSourceRows(...)]\n        self.Outputs.predictions.send(predictions)\n\n    @staticmethod\n    def _add_classification_out_columns(slot, newmetas, newcolumns):\n        # Mapped or unmapped predictions?!\n        # Or provide a checkbox so the user decides?\n        pred = slot.predictor\n        name = pred.name\n        values = pred.domain.class_var.values\n        newmetas.append(DiscreteVariable(name=name, values=values))\n        newcolumns.append(slot.results.unmapped_predicted.reshape(-1, 1))\n        newmetas += [ContinuousVariable(name=f\"{name} ({value})\")\n                     for value in values]\n        newcolumns.append(slot.results.unmapped_probabilities)\n\n    @staticmethod\n    def _add_regression_out_columns(slot, newmetas, newcolumns):\n        newmetas.append(ContinuousVariable(name=slot.predictor.name))\n        newcolumns.append(slot.results.unmapped_predicted.reshape((-1, 1)))\n\n    def send_report(self):\n        def merge_data_with_predictions():\n            data_model = self.dataview.model()\n            predictions_view = self.predictionsview\n            predictions_model = predictions_view.model()\n\n            # use ItemDelegate to style prediction values\n            delegates = [predictions_view.itemDelegateForColumn(i)\n                         for i in range(predictions_model.columnCount())]\n\n            # iterate only over visible columns of data's QTableView\n            iter_data_cols = list(filter(lambda x: not self.dataview.isColumnHidden(x),\n                                         range(data_model.columnCount())))\n\n            # print header\n            yield [''] + \\\n                  [predictions_model.headerData(col, Qt.Horizontal, Qt.DisplayRole)\n                   for col in range(predictions_model.columnCount())] + \\\n                  [data_model.headerData(col, Qt.Horizontal, Qt.DisplayRole)\n                   for col in iter_data_cols]\n\n            # print data & predictions\n            for i in range(data_model.rowCount()):\n                yield [data_model.headerData(i, Qt.Vertical, Qt.DisplayRole)] + \\\n                      [delegate.displayText(\n                          predictions_model.data(predictions_model.index(i, j)),\n                          QLocale())\n                       for j, delegate in enumerate(delegates)] + \\\n                      [data_model.data(data_model.index(i, j),\n                                       role=Qt.DisplayRole)\n                       for j in iter_data_cols]\n\n        if self.data:\n            text = self._get_details().replace('\\n', '<br>')\n            if self.selected_classes:\n                text += '<br>Showing probabilities for: '\n                text += ', '. join([self.class_values[i]\n                                    for i in self.selected_classes])\n            self.report_paragraph('Info', text)\n            self.report_table(\"Data & Predictions\", merge_data_with_predictions(),\n                              header_rows=1, header_columns=1)\n\n            self.report_table(\"Scores\", self.score_table.view)\n\n    def resizeEvent(self, event):\n        super().resizeEvent(event)\n        self._update_splitter()\n\n    def showEvent(self, event):\n        super().showEvent(event)\n        QTimer.singleShot(0, self._update_splitter)",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 47,
                            "end_lineno": 699,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "\"\"\"Tests for OWPredictions\"\"\"\n# pylint: disable=protected-access\nimport io\nimport unittest\nfrom unittest.mock import Mock\n\nimport numpy as np\n\nfrom AnyQt.QtCore import QItemSelectionModel, QItemSelection, Qt\n\nfrom Orange.base import Model\nfrom Orange.classification import LogisticRegressionLearner\nfrom Orange.data.io import TabReader\nfrom Orange.widgets.tests.base import WidgetTest\nfrom Orange.widgets.evaluate.owpredictions import (\n    OWPredictions, SharedSelectionModel, SharedSelectionStore, DataModel,\n    PredictionsModel)\nfrom Orange.widgets.evaluate.owcalibrationplot import OWCalibrationPlot\nfrom Orange.widgets.evaluate.owconfusionmatrix import OWConfusionMatrix\nfrom Orange.widgets.evaluate.owliftcurve import OWLiftCurve\nfrom Orange.widgets.evaluate.owrocanalysis import OWROCAnalysis\n\nfrom Orange.data import Table, Domain, DiscreteVariable\nfrom Orange.modelling import ConstantLearner, TreeLearner\nfrom Orange.evaluation import Results\nfrom Orange.widgets.tests.utils import excepthook_catch, \\\n    possible_duplicate_table\nfrom Orange.widgets.utils.colorpalettes import LimitedDiscretePalette\n\n\nclass TestOWPredictions(WidgetTest):\n\n    def setUp(self):\n        self.widget = self.create_widget(OWPredictions)  # type: OWPredictions\n        self.iris = Table(\"iris\")\n\n    def test_rowCount_from_model(self):\n        \"\"\"Don't crash if the bottom row is visible\"\"\"\n        self.send_signal(self.widget.Inputs.data, self.iris[:5])\n        self.widget.dataview.sizeHintForColumn(0)\n\n    def test_nan_target_input(self):\n        data = self.iris[::10].copy()\n        with data.unlocked():\n            data.Y[1] = np.nan\n        yvec, _ = data.get_column_view(data.domain.class_var)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.send_signal(self.widget.Inputs.predictors, ConstantLearner()(data), 1)\n        pred = self.get_output(self.widget.Outputs.predictions)\n        self.assertIsInstance(pred, Table)\n        np.testing.assert_array_equal(\n            yvec, pred.get_column_view(data.domain.class_var)[0])\n\n        evres = self.get_output(self.widget.Outputs.evaluation_results)\n        self.assertIsInstance(evres, Results)\n        self.assertIsInstance(evres.data, Table)\n        ev_yvec, _ = evres.data.get_column_view(data.domain.class_var)\n\n        self.assertTrue(np.all(~np.isnan(ev_yvec)))\n        self.assertTrue(np.all(~np.isnan(evres.actual)))\n\n        with data.unlocked():\n            data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, data)\n        evres = self.get_output(self.widget.Outputs.evaluation_results)\n        self.assertEqual(len(evres.data), 0)\n\n    def test_no_values_target(self):\n        train = Table(\"titanic\")\n        model = ConstantLearner()(train)\n        self.send_signal(self.widget.Inputs.predictors, model)\n        domain = Domain([DiscreteVariable(\"status\", values=(\"first\", \"third\")),\n                         DiscreteVariable(\"age\", values=(\"adult\", \"child\")),\n                         DiscreteVariable(\"sex\", values=(\"female\", \"male\"))],\n                        [DiscreteVariable(\"survived\", values=())])\n        test = Table(domain, np.array([[0, 0, 1], [0, 1, 0], [1, 0, 0]]),\n                     np.full((3, 1), np.nan))\n        self.send_signal(self.widget.Inputs.data, test)\n        pred = self.get_output(self.widget.Outputs.predictions)\n        self.assertEqual(len(pred), len(test))\n\n        results = self.get_output(self.widget.Outputs.evaluation_results)\n\n        cm_widget = self.create_widget(OWConfusionMatrix)\n        self.send_signal(cm_widget.Inputs.evaluation_results, results,\n                         widget=cm_widget)\n\n        ra_widget = self.create_widget(OWROCAnalysis)\n        self.send_signal(ra_widget.Inputs.evaluation_results, results,\n                         widget=ra_widget)\n\n        lc_widget = self.create_widget(OWLiftCurve)\n        self.send_signal(lc_widget.Inputs.evaluation_results, results,\n                         widget=lc_widget)\n\n        cp_widget = self.create_widget(OWCalibrationPlot)\n        self.send_signal(cp_widget.Inputs.evaluation_results, results,\n                         widget=cp_widget)\n\n    def test_mismatching_targets(self):\n        warning = self.widget.Warning\n\n        maj_iris = ConstantLearner()(self.iris)\n        dom = self.iris.domain\n        iris3 = self.iris.transform(Domain(dom[:3], dom[3]))\n        maj_iris3 = ConstantLearner()(iris3)\n\n        self.send_signal(self.widget.Inputs.predictors, maj_iris, 1)\n        self.send_signal(self.widget.Inputs.predictors, maj_iris3, 2)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None, 2)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, maj_iris3, 2)\n        self.assertTrue(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n    def test_no_class_on_test(self):\n        \"\"\"Allow test data with no class\"\"\"\n        titanic = Table(\"titanic\")\n        majority_titanic = ConstantLearner()(titanic)\n\n        no_class = titanic.transform(Domain(titanic.domain.attributes, None))\n        self.send_signal(self.widget.Inputs.predictors, majority_titanic, 1)\n        self.send_signal(self.widget.Inputs.data, no_class)\n        out = self.get_output(self.widget.Outputs.predictions)\n        np.testing.assert_allclose(out.get_column_view(\"constant\")[0], 0)\n\n    def test_bad_data(self):\n        \"\"\"\n        Firstly it creates predictions with TreeLearner. Then sends predictions and\n        different data with different domain to Predictions widget. Those different\n        data and domain are similar to original data and domain but they have three\n        different target values instead of two.\n        GH-2129\n        \"\"\"\n        filestr1 = \"\"\"\\\n        age\\tsex\\tsurvived\n        d\\td\\td\n        \\t\\tclass\n        adult\\tmale\\tyes\n        adult\\tfemale\\tno\n        child\\tmale\\tyes\n        child\\tfemale\\tyes\n        \"\"\"\n        file1 = io.StringIO(filestr1)\n        table = TabReader(file1).read()\n        learner = TreeLearner()\n        tree = learner(table)\n\n        filestr2 = \"\"\"\\\n        age\\tsex\\tsurvived\n        d\\td\\td\n        \\t\\tclass\n        adult\\tmale\\tyes\n        adult\\tfemale\\tno\n        child\\tmale\\tyes\n        child\\tfemale\\tunknown\n        \"\"\"\n        file2 = io.StringIO(filestr2)\n        bad_table = TabReader(file2).read()\n\n        self.send_signal(self.widget.Inputs.predictors, tree, 1)\n\n        with excepthook_catch():\n            self.send_signal(self.widget.Inputs.data, bad_table)\n\n    def test_continuous_class(self):\n        data = Table(\"housing\")\n        cl_data = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.predictors, cl_data, 1)\n        self.send_signal(self.widget.Inputs.data, data)\n\n    def test_changed_class_var(self):\n        def set_input(data, model):\n            self.send_signals([\n                (self.widget.Inputs.data, data),\n                (self.widget.Inputs.predictors, model)\n            ])\n        iris = self.iris\n        learner = ConstantLearner()\n        heart_disease = Table(\"heart_disease\")\n        # catch exceptions in item delegates etc. during switching inputs\n        with excepthook_catch():\n            set_input(iris[:5], learner(iris))\n            set_input(Table(\"housing\"), None)\n            set_input(heart_disease[:5], learner(heart_disease))\n\n    def test_predictor_fails(self):\n        titanic = Table(\"titanic\")\n        failing_model = ConstantLearner()(titanic)\n        failing_model.predict = Mock(side_effect=ValueError(\"foo\"))\n        self.send_signal(self.widget.Inputs.predictors, failing_model, 1)\n        self.send_signal(self.widget.Inputs.data, titanic)\n\n        model2 = ConstantLearner()(titanic)\n        self.send_signal(self.widget.Inputs.predictors, model2, 2)\n\n    def test_sort_matching(self):\n        def get_items_order(model):\n            return model.mapToSourceRows(np.arange(model.rowCount()))\n\n        w = self.widget\n\n        titanic = Table(\"titanic\")\n        majority_titanic = LogisticRegressionLearner()(titanic)\n        self.send_signal(self.widget.Inputs.predictors, majority_titanic)\n        self.send_signal(self.widget.Inputs.data, titanic)\n\n        pred_model = w.predictionsview.model()\n        data_model = w.dataview.model()\n        n = pred_model.rowCount()\n\n        # no sort\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, np.arange(n))\n        np.testing.assert_array_equal(data_order, np.arange(n))\n\n        # sort by first column in prediction table\n        pred_model.sort(0)\n        w.predictionsview.horizontalHeader().sectionClicked.emit(0)\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, data_order)\n\n        # sort by second column in data table\n        data_model.sort(1)\n        w.dataview.horizontalHeader().sectionClicked.emit(0)\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, data_order)\n\n        # restore order\n        w.reset_button.click()\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, np.arange(n))\n        np.testing.assert_array_equal(data_order, np.arange(n))\n\n    def test_sort_predictions(self):\n        \"\"\"\n        Test whether sorting of probabilities by FilterSortProxy is correct.\n        \"\"\"\n        def get_items_order(model):\n            return model.mapToSourceRows(np.arange(model.rowCount()))\n\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        _, log_reg_probs = log_reg_iris(self.iris, Model.ValueProbs)\n        # here I assume that classes are in order 0, 1, 2 in widget\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        widget_order = get_items_order(pred_model)\n\n        # correct order first sort by probs[:,0] then probs[:,1], ...\n        keys = tuple(\n            log_reg_probs[:, i] for i in range(\n                log_reg_probs.shape[1] - 1, -1, -1))\n        sort_order = np.lexsort(keys)\n        np.testing.assert_array_equal(widget_order, sort_order)\n\n    def test_reset_no_data(self):\n        \"\"\"\n        Check no error when resetting the view without model and data\n        \"\"\"\n        self.widget.reset_button.click()\n\n    def test_colors_same_domain(self):\n        \"\"\"\n        Test whether the color selection for values is correct.\n        \"\"\"\n        # pylint: disable=protected-access\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        # case 1: one same model\n        predictor_iris = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(\n            colors, self.iris.domain.class_var.colors)\n\n        # case 2: two same models\n        predictor_iris1 = ConstantLearner()(self.iris)\n        predictor_iris2 = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        # assume that colors have same order since values have same order\n        np.testing.assert_array_equal(\n            colors, self.iris.domain.class_var.colors)\n\n        # case 3: two same models - changed color order\n        idom = self.iris.domain\n        dom = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom.class_var.colors = dom.class_var.colors[::-1]\n        iris2 = self.iris.transform(dom)\n\n        predictor_iris1 = ConstantLearner()(iris2)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris2.domain.class_var.colors)\n\n    def test_colors_diff_domain(self):\n        \"\"\"\n        Test whether the color selection for values is correct.\n        \"\"\"\n        # pylint: disable=protected-access\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        # case 1: two domains one subset other\n        idom = self.iris.domain\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[:2])\n        )\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n        # case 2: two domains one subset other - different color order\n        idom = self.iris.domain\n        colors = idom.class_var.colors[::-1]\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[:2])\n        )\n        dom1.class_var.colors = colors\n        dom2.class_var.colors = colors[:2]\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n        # case 3: domain color, values miss-match - use default colors\n        idom = self.iris.domain\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom1.class_var.colors = dom1.class_var.colors[::-1]\n        iris1 = self.iris.transform(dom1)\n        iris2 = self.iris.transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, LimitedDiscretePalette(3).palette)\n\n        # case 4: two domains different values order, matching colors\n        idom = self.iris.domain\n        # this way we know that default colors are not used\n        colors = LimitedDiscretePalette(5).palette[2:]\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[::-1])\n        )\n        dom1.class_var.colors = colors\n        dom2.class_var.colors = colors[::-1]  # colors mixed same than values\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n    def test_colors_continuous(self):\n        \"\"\"\n        When only continuous variables in predictor no color should be selected\n        we do not work with classes.\n        When we add one classifier there should be colors.\n        \"\"\"\n        # pylint: disable=protected-access\n        data = Table(\"housing\")\n        cl_data = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.predictors, cl_data)\n        self.send_signal(self.widget.Inputs.data, data)\n        colors = self.widget._get_colors()\n        self.assertListEqual([], colors)\n\n        predictor_iris = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris, 1)\n        colors = self.widget._get_colors()\n        self.assertEqual(3, len(colors))\n\n        self.widget.send_report()  # just a quick check that it doesn't crash\n\n    def test_unique_output_domain(self):\n        data = possible_duplicate_table('constant')\n        predictor = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.send_signal(self.widget.Inputs.predictors, predictor)\n\n        output = self.get_output(self.widget.Outputs.predictions)\n        self.assertEqual(output.domain.metas[0].name, 'constant (1)')\n\n    def test_select(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        self.widget.predictionsview.selectRow(1)\n        sel = {(index.row(), index.column())\n               for index in self.widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(1, col) for col in range(5)})\n\n    def test_select_data_first(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        self.widget.predictionsview.selectRow(1)\n        sel = {(index.row(), index.column())\n               for index in self.widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(1, col) for col in range(5)})\n\n    def test_selection_in_setting(self):\n        widget = self.create_widget(OWPredictions,\n                                    stored_settings={\"selection\": [1, 3, 4]})\n        self.send_signal(widget.Inputs.data, self.iris)\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(widget.Inputs.predictors, log_reg_iris)\n        sel = {(index.row(), index.column())\n               for index in widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(row, col)\n                               for row in [1, 3, 4] for col in range(5)})\n        out = self.get_output(widget.Outputs.predictions)\n        exp = self.iris[np.array([1, 3, 4])]\n        np.testing.assert_equal(out.X, exp.X)\n\n    def test_unregister_prediction_model(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.widget.selection_store.unregister = Mock()\n        prev_model = self.widget.predictionsview.model()\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.widget.selection_store.unregister.called_with(prev_model)\n\n    def test_multi_inputs(self):\n        w = self.widget\n        data = self.iris[::5].copy()\n\n        p1 = ConstantLearner()(data)\n        p1.name = \"P1\"\n        p2 = ConstantLearner()(data)\n        p2.name = \"P2\"\n        p3 = ConstantLearner()(data)\n        p3.name = \"P3\"\n        for i, p in enumerate([p1, p2, p3], 1):\n            self.send_signal(w.Inputs.predictors, p, i)\n        self.send_signal(w.Inputs.data, data)\n\n        def check_evres(expected):\n            out = self.get_output(w.Outputs.evaluation_results)\n            self.assertSequenceEqual(out.learner_names, expected)\n\n        check_evres([\"P1\", \"P2\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, None, 2)\n        check_evres([\"P1\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, p2, 2)\n        check_evres([\"P1\", \"P2\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors,\n                         w.Inputs.predictors.closing_sentinel, 2)\n        check_evres([\"P1\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, p2, 2)\n        check_evres([\"P1\", \"P3\", \"P2\"])\n\n\nclass SelectionModelTest(unittest.TestCase):\n    def setUp(self):\n        iris = Table(\"iris\")\n\n        self.datamodel1 = DataModel(iris[:5, :2])\n        self.store = SharedSelectionStore(self.datamodel1)\n        self.model1 = SharedSelectionModel(self.store, self.datamodel1, None)\n\n        self.datamodel2 = DataModel(iris[-5:, :3])\n        self.model2 = SharedSelectionModel(self.store, self.datamodel2, None)\n\n    def itsel(self, rows):\n        sel = QItemSelection()\n        for row in rows:\n            index = self.store.model.index(row, 0)\n            sel.select(index, index)\n        return sel\n\n\nclass SharedSelectionStoreTest(SelectionModelTest):\n    def test_registration(self):\n        self.store.unregister(self.model1)\n        self.store.unregister(self.model1)  # should have no effect and no error\n        self.store.unregister(self.model2)\n\n    def test_select_rows(self):\n        emit1 = self.model1.emit_selection_rows_changed = Mock()\n        emit2 = self.model2.emit_selection_rows_changed = Mock()\n\n        store = self.store\n        store.select_rows({1, 2}, QItemSelectionModel.Select)\n        self.assertEqual(store.rows, {1, 2})\n        emit1.assert_called_with([1, 2], [])\n        emit2.assert_called_with([1, 2], [])\n\n        store.select_rows({1, 2, 4}, QItemSelectionModel.Select)\n        self.assertEqual(store.rows, {1, 2, 4})\n        emit1.assert_called_with([4], [])\n        emit2.assert_called_with([4], [])\n\n        store.select_rows({3, 4}, QItemSelectionModel.Toggle)\n        self.assertEqual(store.rows, {1, 2, 3})\n        emit1.assert_called_with([3], [4])\n        emit2.assert_called_with([3], [4])\n\n        store.select_rows({0, 2}, QItemSelectionModel.Deselect)\n        self.assertEqual(store.rows, {1, 3})\n        emit1.assert_called_with([], [2])\n        emit2.assert_called_with([], [2])\n\n        store.select_rows({2, 3, 4}, QItemSelectionModel.ClearAndSelect)\n        self.assertEqual(store.rows, {2, 3, 4})\n        emit1.assert_called_with([2, 4], [1])\n        emit2.assert_called_with([2, 4], [1])\n\n        store.select_rows({2, 3, 4}, QItemSelectionModel.Clear)\n        self.assertEqual(store.rows, set())\n        emit1.assert_called_with([], [2, 3, 4])\n        emit2.assert_called_with([], [2, 3, 4])\n\n        store.select_rows({2, 3, 4}, QItemSelectionModel.ClearAndSelect)\n        emit1.reset_mock()\n        emit2.reset_mock()\n        store.select_rows({2, 4}, QItemSelectionModel.Select)\n        emit1.assert_not_called()\n        emit2.assert_not_called()\n\n    def test_select_maps_from_proxy(self):\n        store = self.store\n        store.select_rows = Mock()\n\n        # Map QItemSelection\n        store.model.setSortIndices(np.array([4, 0, 1, 2, 3]))\n        store.select(self.itsel([1, 2]), QItemSelectionModel.Select)\n        store.select_rows.assert_called_with({0, 1}, QItemSelectionModel.Select)\n\n        # Map QModelIndex\n        store.model.setSortIndices(np.array([4, 0, 1, 2, 3]))\n        store.select(store.model.index(0, 0), QItemSelectionModel.Select)\n        store.select_rows.assert_called_with({4}, QItemSelectionModel.Select)\n\n        # Map empty selection\n        store.select(self.itsel([]), QItemSelectionModel.Select)\n        store.select_rows.assert_called_with(set(), QItemSelectionModel.Select)\n\n    def test_clear(self):\n        store = self.store\n\n        store.select_rows({1, 2, 4}, QItemSelectionModel.Select)\n        self.assertEqual(store.rows, {1, 2, 4})\n\n        emit1 = self.model1.emit_selection_rows_changed = Mock()\n        emit2 = self.model2.emit_selection_rows_changed = Mock()\n\n        store.clear_selection()\n        self.assertEqual(store.rows, set())\n        emit1.assert_called_with([], [1, 2, 4])\n        emit2.assert_called_with([], [1, 2, 4])\n\n    def test_reset(self):\n        store = self.store\n\n        store.select_rows({1, 2, 4}, QItemSelectionModel.Select)\n        self.assertEqual(store.rows, {1, 2, 4})\n\n        emit1 = self.model1.emit_selection_rows_changed = Mock()\n        emit2 = self.model2.emit_selection_rows_changed = Mock()\n\n        store.reset()\n        self.assertEqual(store.rows, set())\n        emit1.assert_not_called()\n        emit2.assert_not_called()\n\n    def test_emit_changed_maps_to_proxy(self):\n        store = self.store\n        emit1 = self.model1.emit_selection_rows_changed = Mock()\n        self.model2.emit_selection_rows_changed = Mock()\n\n        def assert_called(exp_selected, exp_deselected):\n            # pylint: disable=unsubscriptable-object\n            selected, deselected = emit1.call_args[0]\n            self.assertEqual(list(selected), exp_selected)\n            self.assertEqual(list(deselected), exp_deselected)\n\n\n        store.model.setSortIndices([4, 0, 1, 2, 3])\n        store.select_rows({3, 4}, QItemSelectionModel.Select)\n        assert_called([4, 0], [])\n\n        store.model.setSortIndices(None)\n        store.select_rows({4}, QItemSelectionModel.Deselect)\n        assert_called([], [4])\n\n        store.model.setSortIndices([1, 0, 3, 4, 2])\n        store.model.setSortIndices(None)\n        store.select_rows({2, 3}, QItemSelectionModel.Deselect)\n        assert_called([], [3])\n\n\nclass SharedSelectionModelTest(SelectionModelTest):\n    def test_select(self):\n        self.store.select = Mock()\n        sel = self.itsel({1, 2})\n        self.model1.select(sel, QItemSelectionModel.Deselect)\n        self.store.select.assert_called_with(sel, QItemSelectionModel.Deselect)\n\n    def test_selection_from_rows(self):\n        sel = self.model1.selection_from_rows({1, 2})\n        self.assertEqual(len(sel), 2)\n        ind1 = sel[0]\n        self.assertIs(ind1.model(), self.model1.model())\n        self.assertEqual(ind1.left(), 0)\n        self.assertEqual(ind1.right(), self.model1.model().columnCount() - 1)\n        self.assertEqual(ind1.top(), 1)\n        self.assertEqual(ind1.bottom(), 1)\n\n    def test_emit_selection_rows_changed(self):\n        m1 = Mock()\n        m2 = Mock()\n        self.model1.selectionChanged.connect(m1)\n        self.model2.selectionChanged.connect(m2)\n        self.model1.model().setSortIndices(np.array([1, 0, 2, 4, 3]))\n        self.model1.select(self.itsel({1, 3}), QItemSelectionModel.Select)\n        self.assertEqual(self.store.rows, {0, 4})\n\n        for model, m in zip((self.model1, self.model2), (m1, m2)):\n            sel = m.call_args[0][0]\n            self.assertEqual(len(sel), 2)\n            for ind, row in zip(sel, (1, 3)):\n                self.assertIs(ind.model(), model.model())\n                self.assertEqual(ind.left(), 0)\n                self.assertEqual(ind.right(), model.model().columnCount() - 1)\n                self.assertEqual(ind.top(), row)\n                self.assertEqual(ind.bottom(), row)\n\n    def test_methods(self):\n        def rowcol(sel):\n            return {(index.row(), index.column()) for index in sel}\n\n        self.model1.model().setSortIndices(np.array([1, 0, 2, 4, 3]))\n        self.model2.model().setSortIndices(np.array([1, 0, 2, 4, 3]))\n\n        self.assertFalse(self.model1.hasSelection())\n        self.assertFalse(self.model1.isColumnSelected(1))\n        self.assertFalse(self.model1.isRowSelected(1))\n        self.assertEqual(self.model1.selectedColumns(1), [])\n        self.assertEqual(self.model1.selectedRows(1), [])\n        self.assertEqual(self.model1.selectedIndexes(), [])\n\n        self.model1.select(self.itsel({1, 3}), QItemSelectionModel.Select)\n\n        self.assertEqual(rowcol(self.model1.selection().indexes()),\n                         {(1, 0), (1, 1), (3, 0), (3, 1)})\n        self.assertEqual(rowcol(self.model2.selection().indexes()),\n                         {(1, 0), (1, 1), (1, 2), (3, 0), (3, 1), (3, 2)})\n\n        self.assertTrue(self.model1.hasSelection())\n        self.assertFalse(self.model1.isColumnSelected(1))\n        self.assertTrue(self.model1.isRowSelected(1))\n        self.assertFalse(self.model1.isRowSelected(2))\n        self.assertEqual(self.model1.selectedColumns(1), [])\n        self.assertEqual(rowcol(self.model1.selectedRows(1)), {(1, 1), (3, 1)})\n        self.assertEqual(rowcol(self.model1.selectedIndexes()),\n                         {(1, 0), (1, 1), (3, 0), (3, 1)})\n\n        self.model1.select(self.itsel({0, 1, 2, 4}), QItemSelectionModel.Select)\n        self.assertTrue(self.model1.isColumnSelected(1))\n        self.assertEqual(rowcol(self.model1.selectedColumns(1)),\n                         {(1, 0), (1, 1)})\n\n        self.model1.clearSelection()\n        self.assertFalse(self.model1.hasSelection())\n        self.assertFalse(self.model1.isColumnSelected(1))\n        self.assertEqual(self.model1.selectedColumns(1), [])\n        self.assertEqual(self.model1.selectedRows(1), [])\n        self.assertEqual(self.model1.selectedIndexes(), [])\n\n        self.model1.select(self.itsel({0, 1, 2, 3, 4}),\n                           QItemSelectionModel.Select)\n        self.model1.reset()\n        self.assertFalse(self.model1.hasSelection())\n        self.assertFalse(self.model1.isColumnSelected(1))\n        self.assertEqual(self.model1.selectedColumns(1), [])\n        self.assertEqual(self.model1.selectedRows(1), [])\n        self.assertEqual(self.model1.selectedIndexes(), [])\n\n\nclass PredictionsModelTest(unittest.TestCase):\n    @classmethod\n    def setUpClass(cls) -> None:\n        cls.values = np.array([[0, 1, 1, 2, 0], [0, 0, 0, 1, 0]], dtype=float)\n        cls.probs = [np.array([[80, 10, 10],\n                               [30, 70, 0],\n                               [15, 80, 5],\n                               [0,  10, 90],\n                               [55, 40, 5]]) / 100,\n                     np.array([[80, 0, 20],\n                               [90, 5, 5],\n                               [70, 10, 20],\n                               [10, 60, 30],\n                               [50, 25, 25]]) / 100]\n        cls.no_probs = [np.zeros((5, 0)), np.zeros((5, 0))]\n\n    def test_model_classification(self):\n        model = PredictionsModel(self.values, self.probs)\n        self.assertEqual(model.rowCount(), 5)\n        self.assertEqual(model.columnCount(), 2)\n\n        val, prob = model.data(model.index(0, 1))\n        self.assertEqual(val, 0)\n        np.testing.assert_equal(prob, [0.8, 0, 0.2])\n\n        val, prob = model.data(model.index(3, 1))\n        self.assertEqual(val, 1)\n        np.testing.assert_equal(prob, [0.1, 0.6, 0.3])\n\n    def test_model_regression(self):\n        model = PredictionsModel(self.values, self.no_probs)\n        self.assertEqual(model.rowCount(), 5)\n        self.assertEqual(model.columnCount(), 2)\n\n        val, prob = model.data(model.index(0, 1))\n        self.assertEqual(val, 0)\n        np.testing.assert_equal(prob, [])\n\n        val, prob = model.data(model.index(3, 1))\n        self.assertEqual(val, 1)\n        np.testing.assert_equal(prob, [])\n\n    def test_model_header(self):\n        model = PredictionsModel(self.values, self.probs)\n        self.assertIsNone(model.headerData(0, Qt.Horizontal))\n        self.assertEqual(model.headerData(3, Qt.Vertical), \"4\")\n\n        model = PredictionsModel(self.values, self.probs, [\"a\", \"b\"])\n        self.assertEqual(model.headerData(0, Qt.Horizontal), \"a\")\n        self.assertEqual(model.headerData(1, Qt.Horizontal), \"b\")\n        self.assertEqual(model.headerData(3, Qt.Vertical), \"4\")\n\n        model = PredictionsModel(self.values, self.probs, [\"a\"])\n        self.assertEqual(model.headerData(0, Qt.Horizontal), \"a\")\n        self.assertIsNone(model.headerData(1, Qt.Horizontal))\n        self.assertEqual(model.headerData(3, Qt.Vertical), \"4\")\n\n    def test_model_empty(self):\n        model = PredictionsModel()\n        self.assertEqual(model.rowCount(), 0)\n        self.assertEqual(model.columnCount(), 0)\n        self.assertIsNone(model.headerData(1, Qt.Horizontal))\n\n    def test_sorting_classification(self):\n        model = PredictionsModel(self.values, self.probs)\n\n        val, prob = model.data(model.index(0, 1))\n        self.assertEqual(val, 0)\n        np.testing.assert_equal(prob, [0.8, 0, 0.2])\n\n        val, prob = model.data(model.index(3, 1))\n        self.assertEqual(val, 1)\n        np.testing.assert_equal(prob, [0.1, 0.6, 0.3])\n\n        model.setProbInd([2])\n        model.sort(0, Qt.DescendingOrder)\n        val, prob = model.data(model.index(0, 0))\n        self.assertEqual(val, 2)\n        np.testing.assert_equal(prob, [0, 0.1, 0.9])\n        val, prob = model.data(model.index(0, 1))\n        self.assertEqual(val, 1)\n        np.testing.assert_equal(prob, [0.1, 0.6, 0.3])\n\n        model.setProbInd([2])\n        model.sort(1, Qt.AscendingOrder)\n        val, prob = model.data(model.index(0, 1))\n        self.assertEqual(val, 0)\n        np.testing.assert_equal(prob, [0.9, 0.05, 0.05])\n        val, prob = model.data(model.index(0, 0))\n        self.assertEqual(val, 1)\n        np.testing.assert_equal(prob, [0.3, 0.7, 0])\n\n        model.setProbInd([1, 0])\n        model.sort(0, Qt.AscendingOrder)\n        np.testing.assert_equal(model.data(model.index(0, 0))[1], [0, .1, .9])\n        np.testing.assert_equal(model.data(model.index(1, 0))[1], [0.8, .1, .1])\n\n        model.setProbInd([1, 2])\n        model.sort(0, Qt.AscendingOrder)\n        np.testing.assert_equal(model.data(model.index(0, 0))[1], [0.8, .1, .1])\n        np.testing.assert_equal(model.data(model.index(1, 0))[1], [0, .1, .9])\n\n        model.setProbInd([])\n        model.sort(0, Qt.AscendingOrder)\n        self.assertEqual([model.data(model.index(i, 0))[0]\n                          for i in range(model.rowCount())], [0, 0, 1, 1, 2])\n\n        model.setProbInd([])\n        model.sort(0, Qt.DescendingOrder)\n        self.assertEqual([model.data(model.index(i, 0))[0]\n                          for i in range(model.rowCount())], [2, 1, 1, 0, 0])\n\n    def test_sorting_regression(self):\n        model = PredictionsModel(self.values, self.no_probs)\n\n        self.assertEqual(model.data(model.index(0, 1))[0], 0)\n        self.assertEqual(model.data(model.index(3, 1))[0], 1)\n\n        model.setProbInd([2])\n        model.sort(0, Qt.AscendingOrder)\n        self.assertEqual([model.data(model.index(i, 0))[0]\n                          for i in range(model.rowCount())], [0, 0, 1, 1, 2])\n\n        model.setProbInd([])\n        model.sort(0, Qt.DescendingOrder)\n        self.assertEqual([model.data(model.index(i, 0))[0]\n                          for i in range(model.rowCount())], [2, 1, 1, 0, 0])\n\n        model.setProbInd(None)\n        model.sort(0, Qt.AscendingOrder)\n        self.assertEqual([model.data(model.index(i, 0))[0]\n                          for i in range(model.rowCount())], [0, 0, 1, 1, 2])\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n",
            "new_file_content": "\"\"\"Tests for OWPredictions\"\"\"\n# pylint: disable=protected-access\nimport io\nimport unittest\nfrom unittest.mock import Mock\n\nimport numpy as np\n\nfrom AnyQt.QtCore import QItemSelectionModel, QItemSelection, Qt\n\nfrom Orange.base import Model\nfrom Orange.classification import LogisticRegressionLearner\nfrom Orange.data.io import TabReader\nfrom Orange.regression import LinearRegressionLearner\nfrom Orange.widgets.tests.base import WidgetTest\nfrom Orange.widgets.evaluate.owpredictions import (\n    OWPredictions, SharedSelectionModel, SharedSelectionStore, DataModel,\n    PredictionsModel)\nfrom Orange.widgets.evaluate.owcalibrationplot import OWCalibrationPlot\nfrom Orange.widgets.evaluate.owconfusionmatrix import OWConfusionMatrix\nfrom Orange.widgets.evaluate.owliftcurve import OWLiftCurve\nfrom Orange.widgets.evaluate.owrocanalysis import OWROCAnalysis\n\nfrom Orange.data import Table, Domain, DiscreteVariable\nfrom Orange.modelling import ConstantLearner, TreeLearner\nfrom Orange.evaluation import Results\nfrom Orange.widgets.tests.utils import excepthook_catch, \\\n    possible_duplicate_table\nfrom Orange.widgets.utils.colorpalettes import LimitedDiscretePalette\n\n\nclass TestOWPredictions(WidgetTest):\n\n    def setUp(self):\n        self.widget = self.create_widget(OWPredictions)  # type: OWPredictions\n        self.iris = Table(\"iris\")\n        self.housing = Table(\"housing\")\n\n    def test_rowCount_from_model(self):\n        \"\"\"Don't crash if the bottom row is visible\"\"\"\n        self.send_signal(self.widget.Inputs.data, self.iris[:5])\n        self.widget.dataview.sizeHintForColumn(0)\n\n    def test_nan_target_input(self):\n        data = self.iris[::10].copy()\n        with data.unlocked():\n            data.Y[1] = np.nan\n        yvec, _ = data.get_column_view(data.domain.class_var)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.send_signal(self.widget.Inputs.predictors, ConstantLearner()(data), 1)\n        pred = self.get_output(self.widget.Outputs.predictions)\n        self.assertIsInstance(pred, Table)\n        np.testing.assert_array_equal(\n            yvec, pred.get_column_view(data.domain.class_var)[0])\n\n        evres = self.get_output(self.widget.Outputs.evaluation_results)\n        self.assertIsInstance(evres, Results)\n        self.assertIsInstance(evres.data, Table)\n        ev_yvec, _ = evres.data.get_column_view(data.domain.class_var)\n\n        self.assertTrue(np.all(~np.isnan(ev_yvec)))\n        self.assertTrue(np.all(~np.isnan(evres.actual)))\n\n        with data.unlocked():\n            data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, data)\n        evres = self.get_output(self.widget.Outputs.evaluation_results)\n        self.assertEqual(len(evres.data), 0)\n\n    def test_no_values_target(self):\n        train = Table(\"titanic\")\n        model = ConstantLearner()(train)\n        self.send_signal(self.widget.Inputs.predictors, model)\n        domain = Domain([DiscreteVariable(\"status\", values=(\"first\", \"third\")),\n                         DiscreteVariable(\"age\", values=(\"adult\", \"child\")),\n                         DiscreteVariable(\"sex\", values=(\"female\", \"male\"))],\n                        [DiscreteVariable(\"survived\", values=())])\n        test = Table(domain, np.array([[0, 0, 1], [0, 1, 0], [1, 0, 0]]),\n                     np.full((3, 1), np.nan))\n        self.send_signal(self.widget.Inputs.data, test)\n        pred = self.get_output(self.widget.Outputs.predictions)\n        self.assertEqual(len(pred), len(test))\n\n        results = self.get_output(self.widget.Outputs.evaluation_results)\n\n        cm_widget = self.create_widget(OWConfusionMatrix)\n        self.send_signal(cm_widget.Inputs.evaluation_results, results,\n                         widget=cm_widget)\n\n        ra_widget = self.create_widget(OWROCAnalysis)\n        self.send_signal(ra_widget.Inputs.evaluation_results, results,\n                         widget=ra_widget)\n\n        lc_widget = self.create_widget(OWLiftCurve)\n        self.send_signal(lc_widget.Inputs.evaluation_results, results,\n                         widget=lc_widget)\n\n        cp_widget = self.create_widget(OWCalibrationPlot)\n        self.send_signal(cp_widget.Inputs.evaluation_results, results,\n                         widget=cp_widget)\n\n    def test_mismatching_targets(self):\n        warning = self.widget.Warning\n\n        maj_iris = ConstantLearner()(self.iris)\n        dom = self.iris.domain\n        iris3 = self.iris.transform(Domain(dom[:3], dom[3]))\n        maj_iris3 = ConstantLearner()(iris3)\n\n        self.send_signal(self.widget.Inputs.predictors, maj_iris, 1)\n        self.send_signal(self.widget.Inputs.predictors, maj_iris3, 2)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None, 2)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, maj_iris3, 2)\n        self.assertTrue(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n    def test_no_class_on_test(self):\n        \"\"\"Allow test data with no class\"\"\"\n        titanic = Table(\"titanic\")\n        majority_titanic = ConstantLearner()(titanic)\n\n        no_class = titanic.transform(Domain(titanic.domain.attributes, None))\n        self.send_signal(self.widget.Inputs.predictors, majority_titanic, 1)\n        self.send_signal(self.widget.Inputs.data, no_class)\n        out = self.get_output(self.widget.Outputs.predictions)\n        np.testing.assert_allclose(out.get_column_view(\"constant\")[0], 0)\n\n    def test_bad_data(self):\n        \"\"\"\n        Firstly it creates predictions with TreeLearner. Then sends predictions and\n        different data with different domain to Predictions widget. Those different\n        data and domain are similar to original data and domain but they have three\n        different target values instead of two.\n        GH-2129\n        \"\"\"\n        filestr1 = \"\"\"\\\n        age\\tsex\\tsurvived\n        d\\td\\td\n        \\t\\tclass\n        adult\\tmale\\tyes\n        adult\\tfemale\\tno\n        child\\tmale\\tyes\n        child\\tfemale\\tyes\n        \"\"\"\n        file1 = io.StringIO(filestr1)\n        table = TabReader(file1).read()\n        learner = TreeLearner()\n        tree = learner(table)\n\n        filestr2 = \"\"\"\\\n        age\\tsex\\tsurvived\n        d\\td\\td\n        \\t\\tclass\n        adult\\tmale\\tyes\n        adult\\tfemale\\tno\n        child\\tmale\\tyes\n        child\\tfemale\\tunknown\n        \"\"\"\n        file2 = io.StringIO(filestr2)\n        bad_table = TabReader(file2).read()\n\n        self.send_signal(self.widget.Inputs.predictors, tree, 1)\n\n        with excepthook_catch():\n            self.send_signal(self.widget.Inputs.data, bad_table)\n\n    def test_continuous_class(self):\n        data = Table(\"housing\")\n        cl_data = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.predictors, cl_data, 1)\n        self.send_signal(self.widget.Inputs.data, data)\n\n    def test_changed_class_var(self):\n        def set_input(data, model):\n            self.send_signals([\n                (self.widget.Inputs.data, data),\n                (self.widget.Inputs.predictors, model)\n            ])\n        iris = self.iris\n        learner = ConstantLearner()\n        heart_disease = Table(\"heart_disease\")\n        # catch exceptions in item delegates etc. during switching inputs\n        with excepthook_catch():\n            set_input(iris[:5], learner(iris))\n            set_input(Table(\"housing\"), None)\n            set_input(heart_disease[:5], learner(heart_disease))\n\n    def test_predictor_fails(self):\n        titanic = Table(\"titanic\")\n        failing_model = ConstantLearner()(titanic)\n        failing_model.predict = Mock(side_effect=ValueError(\"foo\"))\n        self.send_signal(self.widget.Inputs.predictors, failing_model, 1)\n        self.send_signal(self.widget.Inputs.data, titanic)\n\n        model2 = ConstantLearner()(titanic)\n        self.send_signal(self.widget.Inputs.predictors, model2, 2)\n\n    def test_sort_matching(self):\n        def get_items_order(model):\n            return model.mapToSourceRows(np.arange(model.rowCount()))\n\n        w = self.widget\n\n        titanic = Table(\"titanic\")\n        majority_titanic = LogisticRegressionLearner()(titanic)\n        self.send_signal(self.widget.Inputs.predictors, majority_titanic)\n        self.send_signal(self.widget.Inputs.data, titanic)\n\n        pred_model = w.predictionsview.model()\n        data_model = w.dataview.model()\n        n = pred_model.rowCount()\n\n        # no sort\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, np.arange(n))\n        np.testing.assert_array_equal(data_order, np.arange(n))\n\n        # sort by first column in prediction table\n        pred_model.sort(0)\n        w.predictionsview.horizontalHeader().sectionClicked.emit(0)\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, data_order)\n\n        # sort by second column in data table\n        data_model.sort(1)\n        w.dataview.horizontalHeader().sectionClicked.emit(0)\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, data_order)\n\n        # restore order\n        w.reset_button.click()\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, np.arange(n))\n        np.testing.assert_array_equal(data_order, np.arange(n))\n\n    def test_sort_predictions(self):\n        \"\"\"\n        Test whether sorting of probabilities by FilterSortProxy is correct.\n        \"\"\"\n        def get_items_order(model):\n            return model.mapToSourceRows(np.arange(model.rowCount()))\n\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        _, log_reg_probs = log_reg_iris(self.iris, Model.ValueProbs)\n        # here I assume that classes are in order 0, 1, 2 in widget\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        widget_order = get_items_order(pred_model)\n\n        # correct order first sort by probs[:,0] then probs[:,1], ...\n        keys = tuple(\n            log_reg_probs[:, i] for i in range(\n                log_reg_probs.shape[1] - 1, -1, -1))\n        sort_order = np.lexsort(keys)\n        np.testing.assert_array_equal(widget_order, sort_order)\n\n    def test_reset_no_data(self):\n        \"\"\"\n        Check no error when resetting the view without model and data\n        \"\"\"\n        self.widget.reset_button.click()\n\n    def test_colors_same_domain(self):\n        \"\"\"\n        Test whether the color selection for values is correct.\n        \"\"\"\n        # pylint: disable=protected-access\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        # case 1: one same model\n        predictor_iris = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(\n            colors, self.iris.domain.class_var.colors)\n\n        # case 2: two same models\n        predictor_iris1 = ConstantLearner()(self.iris)\n        predictor_iris2 = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        # assume that colors have same order since values have same order\n        np.testing.assert_array_equal(\n            colors, self.iris.domain.class_var.colors)\n\n        # case 3: two same models - changed color order\n        idom = self.iris.domain\n        dom = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom.class_var.colors = dom.class_var.colors[::-1]\n        iris2 = self.iris.transform(dom)\n\n        predictor_iris1 = ConstantLearner()(iris2)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris2.domain.class_var.colors)\n\n    def test_colors_diff_domain(self):\n        \"\"\"\n        Test whether the color selection for values is correct.\n        \"\"\"\n        # pylint: disable=protected-access\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        # case 1: two domains one subset other\n        idom = self.iris.domain\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[:2])\n        )\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n        # case 2: two domains one subset other - different color order\n        idom = self.iris.domain\n        colors = idom.class_var.colors[::-1]\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[:2])\n        )\n        dom1.class_var.colors = colors\n        dom2.class_var.colors = colors[:2]\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n        # case 3: domain color, values miss-match - use default colors\n        idom = self.iris.domain\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom1.class_var.colors = dom1.class_var.colors[::-1]\n        iris1 = self.iris.transform(dom1)\n        iris2 = self.iris.transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, LimitedDiscretePalette(3).palette)\n\n        # case 4: two domains different values order, matching colors\n        idom = self.iris.domain\n        # this way we know that default colors are not used\n        colors = LimitedDiscretePalette(5).palette[2:]\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[::-1])\n        )\n        dom1.class_var.colors = colors\n        dom2.class_var.colors = colors[::-1]  # colors mixed same than values\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n    def test_colors_continuous(self):\n        \"\"\"\n        When only continuous variables in predictor no color should be selected\n        we do not work with classes.\n        When we add one classifier there should be colors.\n        \"\"\"\n        # pylint: disable=protected-access\n        data = Table(\"housing\")\n        cl_data = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.predictors, cl_data)\n        self.send_signal(self.widget.Inputs.data, data)\n        colors = self.widget._get_colors()\n        self.assertListEqual([], colors)\n\n        predictor_iris = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris, 1)\n        colors = self.widget._get_colors()\n        self.assertEqual(3, len(colors))\n\n        self.widget.send_report()  # just a quick check that it doesn't crash\n\n    def test_unique_output_domain(self):\n        data = possible_duplicate_table('constant')\n        predictor = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.send_signal(self.widget.Inputs.predictors, predictor)\n\n        output = self.get_output(self.widget.Outputs.predictions)\n        self.assertEqual(output.domain.metas[0].name, 'constant (1)')\n\n    def test_select(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        self.widget.predictionsview.selectRow(1)\n        sel = {(index.row(), index.column())\n               for index in self.widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(1, col) for col in range(5)})\n\n    def test_select_data_first(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        self.widget.predictionsview.selectRow(1)\n        sel = {(index.row(), index.column())\n               for index in self.widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(1, col) for col in range(5)})\n\n    def test_selection_in_setting(self):\n        widget = self.create_widget(OWPredictions,\n                                    stored_settings={\"selection\": [1, 3, 4]})\n        self.send_signal(widget.Inputs.data, self.iris)\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(widget.Inputs.predictors, log_reg_iris)\n        sel = {(index.row(), index.column())\n               for index in widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(row, col)\n                               for row in [1, 3, 4] for col in range(5)})\n        out = self.get_output(widget.Outputs.predictions)\n        exp = self.iris[np.array([1, 3, 4])]\n        np.testing.assert_equal(out.X, exp.X)\n\n    def test_unregister_prediction_model(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.widget.selection_store.unregister = Mock()\n        prev_model = self.widget.predictionsview.model()\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.widget.selection_store.unregister.called_with(prev_model)\n\n    def test_multi_inputs(self):\n        w = self.widget\n        data = self.iris[::5].copy()\n\n        p1 = ConstantLearner()(data)\n        p1.name = \"P1\"\n        p2 = ConstantLearner()(data)\n        p2.name = \"P2\"\n        p3 = ConstantLearner()(data)\n        p3.name = \"P3\"\n        for i, p in enumerate([p1, p2, p3], 1):\n            self.send_signal(w.Inputs.predictors, p, i)\n        self.send_signal(w.Inputs.data, data)\n\n        def check_evres(expected):\n            out = self.get_output(w.Outputs.evaluation_results)\n            self.assertSequenceEqual(out.learner_names, expected)\n\n        check_evres([\"P1\", \"P2\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, None, 2)\n        check_evres([\"P1\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, p2, 2)\n        check_evres([\"P1\", \"P2\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors,\n                         w.Inputs.predictors.closing_sentinel, 2)\n        check_evres([\"P1\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, p2, 2)\n        check_evres([\"P1\", \"P3\", \"P2\"])\n\n    def test_missing_target_cls(self):\n        mask = np.zeros(len(self.iris), dtype=bool)\n        mask[::2] = True\n        train_data = self.iris[~mask]\n        predict_data = self.iris[mask]\n        model = LogisticRegressionLearner()(train_data)\n\n        self.send_signal(self.widget.Inputs.predictors, model)\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[0] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n    def test_missing_target_reg(self):\n        mask = np.zeros(len(self.housing), dtype=bool)\n        mask[::2] = True\n        train_data = self.housing[~mask]\n        predict_data = self.housing[mask]\n        model = LinearRegressionLearner()(train_data)\n\n        self.send_signal(self.widget.Inputs.predictors, model)\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[0] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n\nclass SelectionModelTest(unittest.TestCase):\n    def setUp(self):\n        iris = Table(\"iris\")\n\n        self.datamodel1 = DataModel(iris[:5, :2])\n        self.store = SharedSelectionStore(self.datamodel1)\n        self.model1 = SharedSelectionModel(self.store, self.datamodel1, None)\n\n        self.datamodel2 = DataModel(iris[-5:, :3])\n        self.model2 = SharedSelectionModel(self.store, self.datamodel2, None)\n\n    def itsel(self, rows):\n        sel = QItemSelection()\n        for row in rows:\n            index = self.store.model.index(row, 0)\n            sel.select(index, index)\n        return sel\n\n\nclass SharedSelectionStoreTest(SelectionModelTest):\n    def test_registration(self):\n        self.store.unregister(self.model1)\n        self.store.unregister(self.model1)  # should have no effect and no error\n        self.store.unregister(self.model2)\n\n    def test_select_rows(self):\n        emit1 = self.model1.emit_selection_rows_changed = Mock()\n        emit2 = self.model2.emit_selection_rows_changed = Mock()\n\n        store = self.store\n        store.select_rows({1, 2}, QItemSelectionModel.Select)\n        self.assertEqual(store.rows, {1, 2})\n        emit1.assert_called_with([1, 2], [])\n        emit2.assert_called_with([1, 2], [])\n\n        store.select_rows({1, 2, 4}, QItemSelectionModel.Select)\n        self.assertEqual(store.rows, {1, 2, 4})\n        emit1.assert_called_with([4], [])\n        emit2.assert_called_with([4], [])\n\n        store.select_rows({3, 4}, QItemSelectionModel.Toggle)\n        self.assertEqual(store.rows, {1, 2, 3})\n        emit1.assert_called_with([3], [4])\n        emit2.assert_called_with([3], [4])\n\n        store.select_rows({0, 2}, QItemSelectionModel.Deselect)\n        self.assertEqual(store.rows, {1, 3})\n        emit1.assert_called_with([], [2])\n        emit2.assert_called_with([], [2])\n\n        store.select_rows({2, 3, 4}, QItemSelectionModel.ClearAndSelect)\n        self.assertEqual(store.rows, {2, 3, 4})\n        emit1.assert_called_with([2, 4], [1])\n        emit2.assert_called_with([2, 4], [1])\n\n        store.select_rows({2, 3, 4}, QItemSelectionModel.Clear)\n        self.assertEqual(store.rows, set())\n        emit1.assert_called_with([], [2, 3, 4])\n        emit2.assert_called_with([], [2, 3, 4])\n\n        store.select_rows({2, 3, 4}, QItemSelectionModel.ClearAndSelect)\n        emit1.reset_mock()\n        emit2.reset_mock()\n        store.select_rows({2, 4}, QItemSelectionModel.Select)\n        emit1.assert_not_called()\n        emit2.assert_not_called()\n\n    def test_select_maps_from_proxy(self):\n        store = self.store\n        store.select_rows = Mock()\n\n        # Map QItemSelection\n        store.model.setSortIndices(np.array([4, 0, 1, 2, 3]))\n        store.select(self.itsel([1, 2]), QItemSelectionModel.Select)\n        store.select_rows.assert_called_with({0, 1}, QItemSelectionModel.Select)\n\n        # Map QModelIndex\n        store.model.setSortIndices(np.array([4, 0, 1, 2, 3]))\n        store.select(store.model.index(0, 0), QItemSelectionModel.Select)\n        store.select_rows.assert_called_with({4}, QItemSelectionModel.Select)\n\n        # Map empty selection\n        store.select(self.itsel([]), QItemSelectionModel.Select)\n        store.select_rows.assert_called_with(set(), QItemSelectionModel.Select)\n\n    def test_clear(self):\n        store = self.store\n\n        store.select_rows({1, 2, 4}, QItemSelectionModel.Select)\n        self.assertEqual(store.rows, {1, 2, 4})\n\n        emit1 = self.model1.emit_selection_rows_changed = Mock()\n        emit2 = self.model2.emit_selection_rows_changed = Mock()\n\n        store.clear_selection()\n        self.assertEqual(store.rows, set())\n        emit1.assert_called_with([], [1, 2, 4])\n        emit2.assert_called_with([], [1, 2, 4])\n\n    def test_reset(self):\n        store = self.store\n\n        store.select_rows({1, 2, 4}, QItemSelectionModel.Select)\n        self.assertEqual(store.rows, {1, 2, 4})\n\n        emit1 = self.model1.emit_selection_rows_changed = Mock()\n        emit2 = self.model2.emit_selection_rows_changed = Mock()\n\n        store.reset()\n        self.assertEqual(store.rows, set())\n        emit1.assert_not_called()\n        emit2.assert_not_called()\n\n    def test_emit_changed_maps_to_proxy(self):\n        store = self.store\n        emit1 = self.model1.emit_selection_rows_changed = Mock()\n        self.model2.emit_selection_rows_changed = Mock()\n\n        def assert_called(exp_selected, exp_deselected):\n            # pylint: disable=unsubscriptable-object\n            selected, deselected = emit1.call_args[0]\n            self.assertEqual(list(selected), exp_selected)\n            self.assertEqual(list(deselected), exp_deselected)\n\n\n        store.model.setSortIndices([4, 0, 1, 2, 3])\n        store.select_rows({3, 4}, QItemSelectionModel.Select)\n        assert_called([4, 0], [])\n\n        store.model.setSortIndices(None)\n        store.select_rows({4}, QItemSelectionModel.Deselect)\n        assert_called([], [4])\n\n        store.model.setSortIndices([1, 0, 3, 4, 2])\n        store.model.setSortIndices(None)\n        store.select_rows({2, 3}, QItemSelectionModel.Deselect)\n        assert_called([], [3])\n\n\nclass SharedSelectionModelTest(SelectionModelTest):\n    def test_select(self):\n        self.store.select = Mock()\n        sel = self.itsel({1, 2})\n        self.model1.select(sel, QItemSelectionModel.Deselect)\n        self.store.select.assert_called_with(sel, QItemSelectionModel.Deselect)\n\n    def test_selection_from_rows(self):\n        sel = self.model1.selection_from_rows({1, 2})\n        self.assertEqual(len(sel), 2)\n        ind1 = sel[0]\n        self.assertIs(ind1.model(), self.model1.model())\n        self.assertEqual(ind1.left(), 0)\n        self.assertEqual(ind1.right(), self.model1.model().columnCount() - 1)\n        self.assertEqual(ind1.top(), 1)\n        self.assertEqual(ind1.bottom(), 1)\n\n    def test_emit_selection_rows_changed(self):\n        m1 = Mock()\n        m2 = Mock()\n        self.model1.selectionChanged.connect(m1)\n        self.model2.selectionChanged.connect(m2)\n        self.model1.model().setSortIndices(np.array([1, 0, 2, 4, 3]))\n        self.model1.select(self.itsel({1, 3}), QItemSelectionModel.Select)\n        self.assertEqual(self.store.rows, {0, 4})\n\n        for model, m in zip((self.model1, self.model2), (m1, m2)):\n            sel = m.call_args[0][0]\n            self.assertEqual(len(sel), 2)\n            for ind, row in zip(sel, (1, 3)):\n                self.assertIs(ind.model(), model.model())\n                self.assertEqual(ind.left(), 0)\n                self.assertEqual(ind.right(), model.model().columnCount() - 1)\n                self.assertEqual(ind.top(), row)\n                self.assertEqual(ind.bottom(), row)\n\n    def test_methods(self):\n        def rowcol(sel):\n            return {(index.row(), index.column()) for index in sel}\n\n        self.model1.model().setSortIndices(np.array([1, 0, 2, 4, 3]))\n        self.model2.model().setSortIndices(np.array([1, 0, 2, 4, 3]))\n\n        self.assertFalse(self.model1.hasSelection())\n        self.assertFalse(self.model1.isColumnSelected(1))\n        self.assertFalse(self.model1.isRowSelected(1))\n        self.assertEqual(self.model1.selectedColumns(1), [])\n        self.assertEqual(self.model1.selectedRows(1), [])\n        self.assertEqual(self.model1.selectedIndexes(), [])\n\n        self.model1.select(self.itsel({1, 3}), QItemSelectionModel.Select)\n\n        self.assertEqual(rowcol(self.model1.selection().indexes()),\n                         {(1, 0), (1, 1), (3, 0), (3, 1)})\n        self.assertEqual(rowcol(self.model2.selection().indexes()),\n                         {(1, 0), (1, 1), (1, 2), (3, 0), (3, 1), (3, 2)})\n\n        self.assertTrue(self.model1.hasSelection())\n        self.assertFalse(self.model1.isColumnSelected(1))\n        self.assertTrue(self.model1.isRowSelected(1))\n        self.assertFalse(self.model1.isRowSelected(2))\n        self.assertEqual(self.model1.selectedColumns(1), [])\n        self.assertEqual(rowcol(self.model1.selectedRows(1)), {(1, 1), (3, 1)})\n        self.assertEqual(rowcol(self.model1.selectedIndexes()),\n                         {(1, 0), (1, 1), (3, 0), (3, 1)})\n\n        self.model1.select(self.itsel({0, 1, 2, 4}), QItemSelectionModel.Select)\n        self.assertTrue(self.model1.isColumnSelected(1))\n        self.assertEqual(rowcol(self.model1.selectedColumns(1)),\n                         {(1, 0), (1, 1)})\n\n        self.model1.clearSelection()\n        self.assertFalse(self.model1.hasSelection())\n        self.assertFalse(self.model1.isColumnSelected(1))\n        self.assertEqual(self.model1.selectedColumns(1), [])\n        self.assertEqual(self.model1.selectedRows(1), [])\n        self.assertEqual(self.model1.selectedIndexes(), [])\n\n        self.model1.select(self.itsel({0, 1, 2, 3, 4}),\n                           QItemSelectionModel.Select)\n        self.model1.reset()\n        self.assertFalse(self.model1.hasSelection())\n        self.assertFalse(self.model1.isColumnSelected(1))\n        self.assertEqual(self.model1.selectedColumns(1), [])\n        self.assertEqual(self.model1.selectedRows(1), [])\n        self.assertEqual(self.model1.selectedIndexes(), [])\n\n\nclass PredictionsModelTest(unittest.TestCase):\n    @classmethod\n    def setUpClass(cls) -> None:\n        cls.values = np.array([[0, 1, 1, 2, 0], [0, 0, 0, 1, 0]], dtype=float)\n        cls.probs = [np.array([[80, 10, 10],\n                               [30, 70, 0],\n                               [15, 80, 5],\n                               [0,  10, 90],\n                               [55, 40, 5]]) / 100,\n                     np.array([[80, 0, 20],\n                               [90, 5, 5],\n                               [70, 10, 20],\n                               [10, 60, 30],\n                               [50, 25, 25]]) / 100]\n        cls.no_probs = [np.zeros((5, 0)), np.zeros((5, 0))]\n\n    def test_model_classification(self):\n        model = PredictionsModel(self.values, self.probs)\n        self.assertEqual(model.rowCount(), 5)\n        self.assertEqual(model.columnCount(), 2)\n\n        val, prob = model.data(model.index(0, 1))\n        self.assertEqual(val, 0)\n        np.testing.assert_equal(prob, [0.8, 0, 0.2])\n\n        val, prob = model.data(model.index(3, 1))\n        self.assertEqual(val, 1)\n        np.testing.assert_equal(prob, [0.1, 0.6, 0.3])\n\n    def test_model_regression(self):\n        model = PredictionsModel(self.values, self.no_probs)\n        self.assertEqual(model.rowCount(), 5)\n        self.assertEqual(model.columnCount(), 2)\n\n        val, prob = model.data(model.index(0, 1))\n        self.assertEqual(val, 0)\n        np.testing.assert_equal(prob, [])\n\n        val, prob = model.data(model.index(3, 1))\n        self.assertEqual(val, 1)\n        np.testing.assert_equal(prob, [])\n\n    def test_model_header(self):\n        model = PredictionsModel(self.values, self.probs)\n        self.assertIsNone(model.headerData(0, Qt.Horizontal))\n        self.assertEqual(model.headerData(3, Qt.Vertical), \"4\")\n\n        model = PredictionsModel(self.values, self.probs, [\"a\", \"b\"])\n        self.assertEqual(model.headerData(0, Qt.Horizontal), \"a\")\n        self.assertEqual(model.headerData(1, Qt.Horizontal), \"b\")\n        self.assertEqual(model.headerData(3, Qt.Vertical), \"4\")\n\n        model = PredictionsModel(self.values, self.probs, [\"a\"])\n        self.assertEqual(model.headerData(0, Qt.Horizontal), \"a\")\n        self.assertIsNone(model.headerData(1, Qt.Horizontal))\n        self.assertEqual(model.headerData(3, Qt.Vertical), \"4\")\n\n    def test_model_empty(self):\n        model = PredictionsModel()\n        self.assertEqual(model.rowCount(), 0)\n        self.assertEqual(model.columnCount(), 0)\n        self.assertIsNone(model.headerData(1, Qt.Horizontal))\n\n    def test_sorting_classification(self):\n        model = PredictionsModel(self.values, self.probs)\n\n        val, prob = model.data(model.index(0, 1))\n        self.assertEqual(val, 0)\n        np.testing.assert_equal(prob, [0.8, 0, 0.2])\n\n        val, prob = model.data(model.index(3, 1))\n        self.assertEqual(val, 1)\n        np.testing.assert_equal(prob, [0.1, 0.6, 0.3])\n\n        model.setProbInd([2])\n        model.sort(0, Qt.DescendingOrder)\n        val, prob = model.data(model.index(0, 0))\n        self.assertEqual(val, 2)\n        np.testing.assert_equal(prob, [0, 0.1, 0.9])\n        val, prob = model.data(model.index(0, 1))\n        self.assertEqual(val, 1)\n        np.testing.assert_equal(prob, [0.1, 0.6, 0.3])\n\n        model.setProbInd([2])\n        model.sort(1, Qt.AscendingOrder)\n        val, prob = model.data(model.index(0, 1))\n        self.assertEqual(val, 0)\n        np.testing.assert_equal(prob, [0.9, 0.05, 0.05])\n        val, prob = model.data(model.index(0, 0))\n        self.assertEqual(val, 1)\n        np.testing.assert_equal(prob, [0.3, 0.7, 0])\n\n        model.setProbInd([1, 0])\n        model.sort(0, Qt.AscendingOrder)\n        np.testing.assert_equal(model.data(model.index(0, 0))[1], [0, .1, .9])\n        np.testing.assert_equal(model.data(model.index(1, 0))[1], [0.8, .1, .1])\n\n        model.setProbInd([1, 2])\n        model.sort(0, Qt.AscendingOrder)\n        np.testing.assert_equal(model.data(model.index(0, 0))[1], [0.8, .1, .1])\n        np.testing.assert_equal(model.data(model.index(1, 0))[1], [0, .1, .9])\n\n        model.setProbInd([])\n        model.sort(0, Qt.AscendingOrder)\n        self.assertEqual([model.data(model.index(i, 0))[0]\n                          for i in range(model.rowCount())], [0, 0, 1, 1, 2])\n\n        model.setProbInd([])\n        model.sort(0, Qt.DescendingOrder)\n        self.assertEqual([model.data(model.index(i, 0))[0]\n                          for i in range(model.rowCount())], [2, 1, 1, 0, 0])\n\n    def test_sorting_regression(self):\n        model = PredictionsModel(self.values, self.no_probs)\n\n        self.assertEqual(model.data(model.index(0, 1))[0], 0)\n        self.assertEqual(model.data(model.index(3, 1))[0], 1)\n\n        model.setProbInd([2])\n        model.sort(0, Qt.AscendingOrder)\n        self.assertEqual([model.data(model.index(i, 0))[0]\n                          for i in range(model.rowCount())], [0, 0, 1, 1, 2])\n\n        model.setProbInd([])\n        model.sort(0, Qt.DescendingOrder)\n        self.assertEqual([model.data(model.index(i, 0))[0]\n                          for i in range(model.rowCount())], [2, 1, 1, 0, 0])\n\n        model.setProbInd(None)\n        model.sort(0, Qt.AscendingOrder)\n        self.assertEqual([model.data(model.index(i, 0))[0]\n                          for i in range(model.rowCount())], [0, 0, 1, 1, 2])\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n",
            "header": {
                "file": {
                    "path": "Orange/widgets/evaluate/tests/test_owpredictions.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "71dc1e967",
                "new_commit_hash": "26fbfbfdd",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/Orange/widgets/evaluate/tests/test_owpredictions.py"
            },
            "plus_file": {
                "path": "b/Orange/widgets/evaluate/tests/test_owpredictions.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 11,
                            "length": 6
                        },
                        "new_range": {
                            "start": 11,
                            "length": 7
                        },
                        "section": "from AnyQt.QtCore import QItemSelectionModel, QItemSelection, Qt"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "from Orange.base import Model",
                                "type": "context"
                            },
                            {
                                "content": "from Orange.classification import LogisticRegressionLearner",
                                "type": "context"
                            },
                            {
                                "content": "from Orange.data.io import TabReader",
                                "type": "context"
                            },
                            {
                                "content": "from Orange.regression import LinearRegressionLearner",
                                "type": "added"
                            },
                            {
                                "content": "from Orange.widgets.tests.base import WidgetTest",
                                "type": "context"
                            },
                            {
                                "content": "from Orange.widgets.evaluate.owpredictions import (",
                                "type": "context"
                            },
                            {
                                "content": "    OWPredictions, SharedSelectionModel, SharedSelectionStore, DataModel,",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [
                        {
                            "file_name": "Orange/widgets/evaluate/tests/test_owpredictions.py",
                            "type": "statement",
                            "name": "",
                            "content": "from Orange.regression import LinearRegressionLearner",
                            "ast_type_str": "ImportFrom",
                            "start_lineno": 14,
                            "end_lineno": 14,
                            "parent": null
                        }
                    ],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 33,
                            "length": 6
                        },
                        "new_range": {
                            "start": 34,
                            "length": 7
                        },
                        "section": "class TestOWPredictions(WidgetTest):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    def setUp(self):",
                                "type": "context"
                            },
                            {
                                "content": "        self.widget = self.create_widget(OWPredictions)  # type: OWPredictions",
                                "type": "context"
                            },
                            {
                                "content": "        self.iris = Table(\"iris\")",
                                "type": "context"
                            },
                            {
                                "content": "        self.housing = Table(\"housing\")",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    def test_rowCount_from_model(self):",
                                "type": "context"
                            },
                            {
                                "content": "        \"\"\"Don't crash if the bottom row is visible\"\"\"",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "Orange/widgets/evaluate/tests/test_owpredictions.py",
                            "type": "method",
                            "name": "TestOWPredictions.setUp",
                            "content": "class TestOWPredictions(WidgetTest):\n\n    def setUp(self):\n        self.widget = self.create_widget(OWPredictions)  # type: OWPredictions\n        self.iris = Table(\"iris\")\n        self.housing = Table(\"housing\")\n\n    def test_rowCount_from_model(self):\n        \"\"\"Don't crash if the bottom row is visible\"\"\"\n        self.send_signal(self.widget.Inputs.data, self.iris[:5])\n        self.widget.dataview.sizeHintForColumn(0)\n\n    def test_nan_target_input(self):\n        data = self.iris[::10].copy()\n        with data.unlocked():\n            data.Y[1] = np.nan\n        yvec, _ = data.get_column_view(data.domain.class_var)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.send_signal(self.widget.Inputs.predictors, ConstantLearner()(data), 1)\n        pred = self.get_output(self.widget.Outputs.predictions)\n        self.assertIsInstance(pred, Table)\n        np.testing.assert_array_equal(\n            yvec, pred.get_column_view(data.domain.class_var)[0])\n\n        evres = self.get_output(self.widget.Outputs.evaluation_results)\n        self.assertIsInstance(evres, Results)\n        self.assertIsInstance(evres.data, Table)\n        ev_yvec, _ = evres.data.get_column_view(data.domain.class_var)\n\n        self.assertTrue(np.all(~np.isnan(ev_yvec)))\n        self.assertTrue(np.all(~np.isnan(evres.actual)))\n\n        with data.unlocked():\n            data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, data)\n        evres = self.get_output(self.widget.Outputs.evaluation_results)\n        self.assertEqual(len(evres.data), 0)\n\n    def test_no_values_target(self):\n        train = Table(\"titanic\")\n        model = ConstantLearner()(train)\n        self.send_signal(self.widget.Inputs.predictors, model)\n        domain = Domain([DiscreteVariable(\"status\", values=(\"first\", \"third\")),\n                         DiscreteVariable(\"age\", values=(\"adult\", \"child\")),\n                         DiscreteVariable(\"sex\", values=(\"female\", \"male\"))],\n                        [DiscreteVariable(\"survived\", values=())])\n        test = Table(domain, np.array([[0, 0, 1], [0, 1, 0], [1, 0, 0]]),\n                     np.full((3, 1), np.nan))\n        self.send_signal(self.widget.Inputs.data, test)\n        pred = self.get_output(self.widget.Outputs.predictions)\n        self.assertEqual(len(pred), len(test))\n\n        results = self.get_output(self.widget.Outputs.evaluation_results)\n\n        cm_widget = self.create_widget(OWConfusionMatrix)\n        self.send_signal(cm_widget.Inputs.evaluation_results, results,\n                         widget=cm_widget)\n\n        ra_widget = self.create_widget(OWROCAnalysis)\n        self.send_signal(ra_widget.Inputs.evaluation_results, results,\n                         widget=ra_widget)\n\n        lc_widget = self.create_widget(OWLiftCurve)\n        self.send_signal(lc_widget.Inputs.evaluation_results, results,\n                         widget=lc_widget)\n\n        cp_widget = self.create_widget(OWCalibrationPlot)\n        self.send_signal(cp_widget.Inputs.evaluation_results, results,\n                         widget=cp_widget)\n\n    def test_mismatching_targets(self):\n        warning = self.widget.Warning\n\n        maj_iris = ConstantLearner()(self.iris)\n        dom = self.iris.domain\n        iris3 = self.iris.transform(Domain(dom[:3], dom[3]))\n        maj_iris3 = ConstantLearner()(iris3)\n\n        self.send_signal(self.widget.Inputs.predictors, maj_iris, 1)\n        self.send_signal(self.widget.Inputs.predictors, maj_iris3, 2)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None, 2)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, maj_iris3, 2)\n        self.assertTrue(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n    def test_no_class_on_test(self):\n        \"\"\"Allow test data with no class\"\"\"\n        titanic = Table(\"titanic\")\n        majority_titanic = ConstantLearner()(titanic)\n\n        no_class = titanic.transform(Domain(titanic.domain.attributes, None))\n        self.send_signal(self.widget.Inputs.predictors, majority_titanic, 1)\n        self.send_signal(self.widget.Inputs.data, no_class)\n        out = self.get_output(self.widget.Outputs.predictions)\n        np.testing.assert_allclose(out.get_column_view(\"constant\")[0], 0)\n\n    def test_bad_data(self):\n        \"\"\"\n        Firstly it creates predictions with TreeLearner. Then sends predictions and\n        different data with different domain to Predictions widget. Those different\n        data and domain are similar to original data and domain but they have three\n        different target values instead of two.\n        GH-2129\n        \"\"\"\n        filestr1 = \"\"\"\\\n        age\\tsex\\tsurvived\n        d\\td\\td\n        \\t\\tclass\n        adult\\tmale\\tyes\n        adult\\tfemale\\tno\n        child\\tmale\\tyes\n        child\\tfemale\\tyes\n        \"\"\"\n        file1 = io.StringIO(filestr1)\n        table = TabReader(file1).read()\n        learner = TreeLearner()\n        tree = learner(table)\n\n        filestr2 = \"\"\"\\\n        age\\tsex\\tsurvived\n        d\\td\\td\n        \\t\\tclass\n        adult\\tmale\\tyes\n        adult\\tfemale\\tno\n        child\\tmale\\tyes\n        child\\tfemale\\tunknown\n        \"\"\"\n        file2 = io.StringIO(filestr2)\n        bad_table = TabReader(file2).read()\n\n        self.send_signal(self.widget.Inputs.predictors, tree, 1)\n\n        with excepthook_catch():\n            self.send_signal(self.widget.Inputs.data, bad_table)\n\n    def test_continuous_class(self):\n        data = Table(\"housing\")\n        cl_data = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.predictors, cl_data, 1)\n        self.send_signal(self.widget.Inputs.data, data)\n\n    def test_changed_class_var(self):\n        def set_input(data, model):\n            self.send_signals([\n                (self.widget.Inputs.data, data),\n                (self.widget.Inputs.predictors, model)\n            ])\n        iris = self.iris\n        learner = ConstantLearner()\n        heart_disease = Table(\"heart_disease\")\n        # catch exceptions in item delegates etc. during switching inputs\n        with excepthook_catch():\n            set_input(iris[:5], learner(iris))\n            set_input(Table(\"housing\"), None)\n            set_input(heart_disease[:5], learner(heart_disease))\n\n    def test_predictor_fails(self):\n        titanic = Table(\"titanic\")\n        failing_model = ConstantLearner()(titanic)\n        failing_model.predict = Mock(side_effect=ValueError(\"foo\"))\n        self.send_signal(self.widget.Inputs.predictors, failing_model, 1)\n        self.send_signal(self.widget.Inputs.data, titanic)\n\n        model2 = ConstantLearner()(titanic)\n        self.send_signal(self.widget.Inputs.predictors, model2, 2)\n\n    def test_sort_matching(self):\n        def get_items_order(model):\n            return model.mapToSourceRows(np.arange(model.rowCount()))\n\n        w = self.widget\n\n        titanic = Table(\"titanic\")\n        majority_titanic = LogisticRegressionLearner()(titanic)\n        self.send_signal(self.widget.Inputs.predictors, majority_titanic)\n        self.send_signal(self.widget.Inputs.data, titanic)\n\n        pred_model = w.predictionsview.model()\n        data_model = w.dataview.model()\n        n = pred_model.rowCount()\n\n        # no sort\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, np.arange(n))\n        np.testing.assert_array_equal(data_order, np.arange(n))\n\n        # sort by first column in prediction table\n        pred_model.sort(0)\n        w.predictionsview.horizontalHeader().sectionClicked.emit(0)\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, data_order)\n\n        # sort by second column in data table\n        data_model.sort(1)\n        w.dataview.horizontalHeader().sectionClicked.emit(0)\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, data_order)\n\n        # restore order\n        w.reset_button.click()\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, np.arange(n))\n        np.testing.assert_array_equal(data_order, np.arange(n))\n\n    def test_sort_predictions(self):\n        \"\"\"\n        Test whether sorting of probabilities by FilterSortProxy is correct.\n        \"\"\"\n        def get_items_order(model):\n            return model.mapToSourceRows(np.arange(model.rowCount()))\n\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        _, log_reg_probs = log_reg_iris(self.iris, Model.ValueProbs)\n        # here I assume that classes are in order 0, 1, 2 in widget\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        widget_order = get_items_order(pred_model)\n\n        # correct order first sort by probs[:,0] then probs[:,1], ...\n        keys = tuple(\n            log_reg_probs[:, i] for i in range(\n                log_reg_probs.shape[1] - 1, -1, -1))\n        sort_order = np.lexsort(keys)\n        np.testing.assert_array_equal(widget_order, sort_order)\n\n    def test_reset_no_data(self):\n        \"\"\"\n        Check no error when resetting the view without model and data\n        \"\"\"\n        self.widget.reset_button.click()\n\n    def test_colors_same_domain(self):\n        \"\"\"\n        Test whether the color selection for values is correct.\n        \"\"\"\n        # pylint: disable=protected-access\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        # case 1: one same model\n        predictor_iris = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(\n            colors, self.iris.domain.class_var.colors)\n\n        # case 2: two same models\n        predictor_iris1 = ConstantLearner()(self.iris)\n        predictor_iris2 = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        # assume that colors have same order since values have same order\n        np.testing.assert_array_equal(\n            colors, self.iris.domain.class_var.colors)\n\n        # case 3: two same models - changed color order\n        idom = self.iris.domain\n        dom = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom.class_var.colors = dom.class_var.colors[::-1]\n        iris2 = self.iris.transform(dom)\n\n        predictor_iris1 = ConstantLearner()(iris2)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris2.domain.class_var.colors)\n\n    def test_colors_diff_domain(self):\n        \"\"\"\n        Test whether the color selection for values is correct.\n        \"\"\"\n        # pylint: disable=protected-access\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        # case 1: two domains one subset other\n        idom = self.iris.domain\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[:2])\n        )\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n        # case 2: two domains one subset other - different color order\n        idom = self.iris.domain\n        colors = idom.class_var.colors[::-1]\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[:2])\n        )\n        dom1.class_var.colors = colors\n        dom2.class_var.colors = colors[:2]\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n        # case 3: domain color, values miss-match - use default colors\n        idom = self.iris.domain\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom1.class_var.colors = dom1.class_var.colors[::-1]\n        iris1 = self.iris.transform(dom1)\n        iris2 = self.iris.transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, LimitedDiscretePalette(3).palette)\n\n        # case 4: two domains different values order, matching colors\n        idom = self.iris.domain\n        # this way we know that default colors are not used\n        colors = LimitedDiscretePalette(5).palette[2:]\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[::-1])\n        )\n        dom1.class_var.colors = colors\n        dom2.class_var.colors = colors[::-1]  # colors mixed same than values\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n    def test_colors_continuous(self):\n        \"\"\"\n        When only continuous variables in predictor no color should be selected\n        we do not work with classes.\n        When we add one classifier there should be colors.\n        \"\"\"\n        # pylint: disable=protected-access\n        data = Table(\"housing\")\n        cl_data = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.predictors, cl_data)\n        self.send_signal(self.widget.Inputs.data, data)\n        colors = self.widget._get_colors()\n        self.assertListEqual([], colors)\n\n        predictor_iris = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris, 1)\n        colors = self.widget._get_colors()\n        self.assertEqual(3, len(colors))\n\n        self.widget.send_report()  # just a quick check that it doesn't crash\n\n    def test_unique_output_domain(self):\n        data = possible_duplicate_table('constant')\n        predictor = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.send_signal(self.widget.Inputs.predictors, predictor)\n\n        output = self.get_output(self.widget.Outputs.predictions)\n        self.assertEqual(output.domain.metas[0].name, 'constant (1)')\n\n    def test_select(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        self.widget.predictionsview.selectRow(1)\n        sel = {(index.row(), index.column())\n               for index in self.widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(1, col) for col in range(5)})\n\n    def test_select_data_first(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        self.widget.predictionsview.selectRow(1)\n        sel = {(index.row(), index.column())\n               for index in self.widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(1, col) for col in range(5)})\n\n    def test_selection_in_setting(self):\n        widget = self.create_widget(OWPredictions,\n                                    stored_settings={\"selection\": [1, 3, 4]})\n        self.send_signal(widget.Inputs.data, self.iris)\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(widget.Inputs.predictors, log_reg_iris)\n        sel = {(index.row(), index.column())\n               for index in widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(row, col)\n                               for row in [1, 3, 4] for col in range(5)})\n        out = self.get_output(widget.Outputs.predictions)\n        exp = self.iris[np.array([1, 3, 4])]\n        np.testing.assert_equal(out.X, exp.X)\n\n    def test_unregister_prediction_model(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.widget.selection_store.unregister = Mock()\n        prev_model = self.widget.predictionsview.model()\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.widget.selection_store.unregister.called_with(prev_model)\n\n    def test_multi_inputs(self):\n        w = self.widget\n        data = self.iris[::5].copy()\n\n        p1 = ConstantLearner()(data)\n        p1.name = \"P1\"\n        p2 = ConstantLearner()(data)\n        p2.name = \"P2\"\n        p3 = ConstantLearner()(data)\n        p3.name = \"P3\"\n        for i, p in enumerate([p1, p2, p3], 1):\n            self.send_signal(w.Inputs.predictors, p, i)\n        self.send_signal(w.Inputs.data, data)\n\n        def check_evres(expected):\n            out = self.get_output(w.Outputs.evaluation_results)\n            self.assertSequenceEqual(out.learner_names, expected)\n\n        check_evres([\"P1\", \"P2\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, None, 2)\n        check_evres([\"P1\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, p2, 2)\n        check_evres([\"P1\", \"P2\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors,\n                         w.Inputs.predictors.closing_sentinel, 2)\n        check_evres([\"P1\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, p2, 2)\n        check_evres([\"P1\", \"P3\", \"P2\"])\n\n    def test_missing_target_cls(self):\n        mask = np.zeros(len(self.iris), dtype=bool)\n        mask[::2] = True\n        train_data = self.iris[~mask]\n        predict_data = self.iris[mask]\n        model = LogisticRegressionLearner()(train_data)\n\n        self.send_signal(self.widget.Inputs.predictors, model)\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[0] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n    def test_missing_target_reg(self):\n        mask = np.zeros(len(self.housing), dtype=bool)\n        mask[::2] = True\n        train_data = self.housing[~mask]\n        predict_data = self.housing[mask]\n        model = LinearRegressionLearner()(train_data)\n\n        self.send_signal(self.widget.Inputs.predictors, model)\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[0] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 34,
                            "end_lineno": 37,
                            "parent": {
                                "file_name": "Orange/widgets/evaluate/tests/test_owpredictions.py",
                                "type": "class",
                                "name": "TestOWPredictions",
                                "content": "class TestOWPredictions(WidgetTest):\n\n    def setUp(self):\n        self.widget = self.create_widget(OWPredictions)  # type: OWPredictions\n        self.iris = Table(\"iris\")\n        self.housing = Table(\"housing\")\n\n    def test_rowCount_from_model(self):\n        \"\"\"Don't crash if the bottom row is visible\"\"\"\n        self.send_signal(self.widget.Inputs.data, self.iris[:5])\n        self.widget.dataview.sizeHintForColumn(0)\n\n    def test_nan_target_input(self):\n        data = self.iris[::10].copy()\n        with data.unlocked():\n            data.Y[1] = np.nan\n        yvec, _ = data.get_column_view(data.domain.class_var)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.send_signal(self.widget.Inputs.predictors, ConstantLearner()(data), 1)\n        pred = self.get_output(self.widget.Outputs.predictions)\n        self.assertIsInstance(pred, Table)\n        np.testing.assert_array_equal(\n            yvec, pred.get_column_view(data.domain.class_var)[0])\n\n        evres = self.get_output(self.widget.Outputs.evaluation_results)\n        self.assertIsInstance(evres, Results)\n        self.assertIsInstance(evres.data, Table)\n        ev_yvec, _ = evres.data.get_column_view(data.domain.class_var)\n\n        self.assertTrue(np.all(~np.isnan(ev_yvec)))\n        self.assertTrue(np.all(~np.isnan(evres.actual)))\n\n        with data.unlocked():\n            data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, data)\n        evres = self.get_output(self.widget.Outputs.evaluation_results)\n        self.assertEqual(len(evres.data), 0)\n\n    def test_no_values_target(self):\n        train = Table(\"titanic\")\n        model = ConstantLearner()(train)\n        self.send_signal(self.widget.Inputs.predictors, model)\n        domain = Domain([DiscreteVariable(\"status\", values=(\"first\", \"third\")),\n                         DiscreteVariable(\"age\", values=(\"adult\", \"child\")),\n                         DiscreteVariable(\"sex\", values=(\"female\", \"male\"))],\n                        [DiscreteVariable(\"survived\", values=())])\n        test = Table(domain, np.array([[0, 0, 1], [0, 1, 0], [1, 0, 0]]),\n                     np.full((3, 1), np.nan))\n        self.send_signal(self.widget.Inputs.data, test)\n        pred = self.get_output(self.widget.Outputs.predictions)\n        self.assertEqual(len(pred), len(test))\n\n        results = self.get_output(self.widget.Outputs.evaluation_results)\n\n        cm_widget = self.create_widget(OWConfusionMatrix)\n        self.send_signal(cm_widget.Inputs.evaluation_results, results,\n                         widget=cm_widget)\n\n        ra_widget = self.create_widget(OWROCAnalysis)\n        self.send_signal(ra_widget.Inputs.evaluation_results, results,\n                         widget=ra_widget)\n\n        lc_widget = self.create_widget(OWLiftCurve)\n        self.send_signal(lc_widget.Inputs.evaluation_results, results,\n                         widget=lc_widget)\n\n        cp_widget = self.create_widget(OWCalibrationPlot)\n        self.send_signal(cp_widget.Inputs.evaluation_results, results,\n                         widget=cp_widget)\n\n    def test_mismatching_targets(self):\n        warning = self.widget.Warning\n\n        maj_iris = ConstantLearner()(self.iris)\n        dom = self.iris.domain\n        iris3 = self.iris.transform(Domain(dom[:3], dom[3]))\n        maj_iris3 = ConstantLearner()(iris3)\n\n        self.send_signal(self.widget.Inputs.predictors, maj_iris, 1)\n        self.send_signal(self.widget.Inputs.predictors, maj_iris3, 2)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None, 2)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, maj_iris3, 2)\n        self.assertTrue(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n    def test_no_class_on_test(self):\n        \"\"\"Allow test data with no class\"\"\"\n        titanic = Table(\"titanic\")\n        majority_titanic = ConstantLearner()(titanic)\n\n        no_class = titanic.transform(Domain(titanic.domain.attributes, None))\n        self.send_signal(self.widget.Inputs.predictors, majority_titanic, 1)\n        self.send_signal(self.widget.Inputs.data, no_class)\n        out = self.get_output(self.widget.Outputs.predictions)\n        np.testing.assert_allclose(out.get_column_view(\"constant\")[0], 0)\n\n    def test_bad_data(self):\n        \"\"\"\n        Firstly it creates predictions with TreeLearner. Then sends predictions and\n        different data with different domain to Predictions widget. Those different\n        data and domain are similar to original data and domain but they have three\n        different target values instead of two.\n        GH-2129\n        \"\"\"\n        filestr1 = \"\"\"\\\n        age\\tsex\\tsurvived\n        d\\td\\td\n        \\t\\tclass\n        adult\\tmale\\tyes\n        adult\\tfemale\\tno\n        child\\tmale\\tyes\n        child\\tfemale\\tyes\n        \"\"\"\n        file1 = io.StringIO(filestr1)\n        table = TabReader(file1).read()\n        learner = TreeLearner()\n        tree = learner(table)\n\n        filestr2 = \"\"\"\\\n        age\\tsex\\tsurvived\n        d\\td\\td\n        \\t\\tclass\n        adult\\tmale\\tyes\n        adult\\tfemale\\tno\n        child\\tmale\\tyes\n        child\\tfemale\\tunknown\n        \"\"\"\n        file2 = io.StringIO(filestr2)\n        bad_table = TabReader(file2).read()\n\n        self.send_signal(self.widget.Inputs.predictors, tree, 1)\n\n        with excepthook_catch():\n            self.send_signal(self.widget.Inputs.data, bad_table)\n\n    def test_continuous_class(self):\n        data = Table(\"housing\")\n        cl_data = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.predictors, cl_data, 1)\n        self.send_signal(self.widget.Inputs.data, data)\n\n    def test_changed_class_var(self):\n        def set_input(data, model):\n            self.send_signals([\n                (self.widget.Inputs.data, data),\n                (self.widget.Inputs.predictors, model)\n            ])\n        iris = self.iris\n        learner = ConstantLearner()\n        heart_disease = Table(\"heart_disease\")\n        # catch exceptions in item delegates etc. during switching inputs\n        with excepthook_catch():\n            set_input(iris[:5], learner(iris))\n            set_input(Table(\"housing\"), None)\n            set_input(heart_disease[:5], learner(heart_disease))\n\n    def test_predictor_fails(self):\n        titanic = Table(\"titanic\")\n        failing_model = ConstantLearner()(titanic)\n        failing_model.predict = Mock(side_effect=ValueError(\"foo\"))\n        self.send_signal(self.widget.Inputs.predictors, failing_model, 1)\n        self.send_signal(self.widget.Inputs.data, titanic)\n\n        model2 = ConstantLearner()(titanic)\n        self.send_signal(self.widget.Inputs.predictors, model2, 2)\n\n    def test_sort_matching(self):\n        def get_items_order(model):\n            return model.mapToSourceRows(np.arange(model.rowCount()))\n\n        w = self.widget\n\n        titanic = Table(\"titanic\")\n        majority_titanic = LogisticRegressionLearner()(titanic)\n        self.send_signal(self.widget.Inputs.predictors, majority_titanic)\n        self.send_signal(self.widget.Inputs.data, titanic)\n\n        pred_model = w.predictionsview.model()\n        data_model = w.dataview.model()\n        n = pred_model.rowCount()\n\n        # no sort\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, np.arange(n))\n        np.testing.assert_array_equal(data_order, np.arange(n))\n\n        # sort by first column in prediction table\n        pred_model.sort(0)\n        w.predictionsview.horizontalHeader().sectionClicked.emit(0)\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, data_order)\n\n        # sort by second column in data table\n        data_model.sort(1)\n        w.dataview.horizontalHeader().sectionClicked.emit(0)\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, data_order)\n\n        # restore order\n        w.reset_button.click()\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, np.arange(n))\n        np.testing.assert_array_equal(data_order, np.arange(n))\n\n    def test_sort_predictions(self):\n        \"\"\"\n        Test whether sorting of probabilities by FilterSortProxy is correct.\n        \"\"\"\n        def get_items_order(model):\n            return model.mapToSourceRows(np.arange(model.rowCount()))\n\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        _, log_reg_probs = log_reg_iris(self.iris, Model.ValueProbs)\n        # here I assume that classes are in order 0, 1, 2 in widget\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        widget_order = get_items_order(pred_model)\n\n        # correct order first sort by probs[:,0] then probs[:,1], ...\n        keys = tuple(\n            log_reg_probs[:, i] for i in range(\n                log_reg_probs.shape[1] - 1, -1, -1))\n        sort_order = np.lexsort(keys)\n        np.testing.assert_array_equal(widget_order, sort_order)\n\n    def test_reset_no_data(self):\n        \"\"\"\n        Check no error when resetting the view without model and data\n        \"\"\"\n        self.widget.reset_button.click()\n\n    def test_colors_same_domain(self):\n        \"\"\"\n        Test whether the color selection for values is correct.\n        \"\"\"\n        # pylint: disable=protected-access\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        # case 1: one same model\n        predictor_iris = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(\n            colors, self.iris.domain.class_var.colors)\n\n        # case 2: two same models\n        predictor_iris1 = ConstantLearner()(self.iris)\n        predictor_iris2 = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        # assume that colors have same order since values have same order\n        np.testing.assert_array_equal(\n            colors, self.iris.domain.class_var.colors)\n\n        # case 3: two same models - changed color order\n        idom = self.iris.domain\n        dom = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom.class_var.colors = dom.class_var.colors[::-1]\n        iris2 = self.iris.transform(dom)\n\n        predictor_iris1 = ConstantLearner()(iris2)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris2.domain.class_var.colors)\n\n    def test_colors_diff_domain(self):\n        \"\"\"\n        Test whether the color selection for values is correct.\n        \"\"\"\n        # pylint: disable=protected-access\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        # case 1: two domains one subset other\n        idom = self.iris.domain\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[:2])\n        )\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n        # case 2: two domains one subset other - different color order\n        idom = self.iris.domain\n        colors = idom.class_var.colors[::-1]\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[:2])\n        )\n        dom1.class_var.colors = colors\n        dom2.class_var.colors = colors[:2]\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n        # case 3: domain color, values miss-match - use default colors\n        idom = self.iris.domain\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom1.class_var.colors = dom1.class_var.colors[::-1]\n        iris1 = self.iris.transform(dom1)\n        iris2 = self.iris.transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, LimitedDiscretePalette(3).palette)\n\n        # case 4: two domains different values order, matching colors\n        idom = self.iris.domain\n        # this way we know that default colors are not used\n        colors = LimitedDiscretePalette(5).palette[2:]\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[::-1])\n        )\n        dom1.class_var.colors = colors\n        dom2.class_var.colors = colors[::-1]  # colors mixed same than values\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n    def test_colors_continuous(self):\n        \"\"\"\n        When only continuous variables in predictor no color should be selected\n        we do not work with classes.\n        When we add one classifier there should be colors.\n        \"\"\"\n        # pylint: disable=protected-access\n        data = Table(\"housing\")\n        cl_data = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.predictors, cl_data)\n        self.send_signal(self.widget.Inputs.data, data)\n        colors = self.widget._get_colors()\n        self.assertListEqual([], colors)\n\n        predictor_iris = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris, 1)\n        colors = self.widget._get_colors()\n        self.assertEqual(3, len(colors))\n\n        self.widget.send_report()  # just a quick check that it doesn't crash\n\n    def test_unique_output_domain(self):\n        data = possible_duplicate_table('constant')\n        predictor = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.send_signal(self.widget.Inputs.predictors, predictor)\n\n        output = self.get_output(self.widget.Outputs.predictions)\n        self.assertEqual(output.domain.metas[0].name, 'constant (1)')\n\n    def test_select(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        self.widget.predictionsview.selectRow(1)\n        sel = {(index.row(), index.column())\n               for index in self.widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(1, col) for col in range(5)})\n\n    def test_select_data_first(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        self.widget.predictionsview.selectRow(1)\n        sel = {(index.row(), index.column())\n               for index in self.widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(1, col) for col in range(5)})\n\n    def test_selection_in_setting(self):\n        widget = self.create_widget(OWPredictions,\n                                    stored_settings={\"selection\": [1, 3, 4]})\n        self.send_signal(widget.Inputs.data, self.iris)\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(widget.Inputs.predictors, log_reg_iris)\n        sel = {(index.row(), index.column())\n               for index in widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(row, col)\n                               for row in [1, 3, 4] for col in range(5)})\n        out = self.get_output(widget.Outputs.predictions)\n        exp = self.iris[np.array([1, 3, 4])]\n        np.testing.assert_equal(out.X, exp.X)\n\n    def test_unregister_prediction_model(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.widget.selection_store.unregister = Mock()\n        prev_model = self.widget.predictionsview.model()\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.widget.selection_store.unregister.called_with(prev_model)\n\n    def test_multi_inputs(self):\n        w = self.widget\n        data = self.iris[::5].copy()\n\n        p1 = ConstantLearner()(data)\n        p1.name = \"P1\"\n        p2 = ConstantLearner()(data)\n        p2.name = \"P2\"\n        p3 = ConstantLearner()(data)\n        p3.name = \"P3\"\n        for i, p in enumerate([p1, p2, p3], 1):\n            self.send_signal(w.Inputs.predictors, p, i)\n        self.send_signal(w.Inputs.data, data)\n\n        def check_evres(expected):\n            out = self.get_output(w.Outputs.evaluation_results)\n            self.assertSequenceEqual(out.learner_names, expected)\n\n        check_evres([\"P1\", \"P2\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, None, 2)\n        check_evres([\"P1\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, p2, 2)\n        check_evres([\"P1\", \"P2\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors,\n                         w.Inputs.predictors.closing_sentinel, 2)\n        check_evres([\"P1\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, p2, 2)\n        check_evres([\"P1\", \"P3\", \"P2\"])\n\n    def test_missing_target_cls(self):\n        mask = np.zeros(len(self.iris), dtype=bool)\n        mask[::2] = True\n        train_data = self.iris[~mask]\n        predict_data = self.iris[mask]\n        model = LogisticRegressionLearner()(train_data)\n\n        self.send_signal(self.widget.Inputs.predictors, model)\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[0] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n    def test_missing_target_reg(self):\n        mask = np.zeros(len(self.housing), dtype=bool)\n        mask[::2] = True\n        train_data = self.housing[~mask]\n        predict_data = self.housing[mask]\n        model = LinearRegressionLearner()(train_data)\n\n        self.send_signal(self.widget.Inputs.predictors, model)\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[0] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 32,
                                "end_lineno": 578,
                                "parent": null
                            }
                        },
                        {
                            "file_name": "Orange/widgets/evaluate/tests/test_owpredictions.py",
                            "type": "class",
                            "name": "TestOWPredictions",
                            "content": "class TestOWPredictions(WidgetTest):\n\n    def setUp(self):\n        self.widget = self.create_widget(OWPredictions)  # type: OWPredictions\n        self.iris = Table(\"iris\")\n        self.housing = Table(\"housing\")\n\n    def test_rowCount_from_model(self):\n        \"\"\"Don't crash if the bottom row is visible\"\"\"\n        self.send_signal(self.widget.Inputs.data, self.iris[:5])\n        self.widget.dataview.sizeHintForColumn(0)\n\n    def test_nan_target_input(self):\n        data = self.iris[::10].copy()\n        with data.unlocked():\n            data.Y[1] = np.nan\n        yvec, _ = data.get_column_view(data.domain.class_var)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.send_signal(self.widget.Inputs.predictors, ConstantLearner()(data), 1)\n        pred = self.get_output(self.widget.Outputs.predictions)\n        self.assertIsInstance(pred, Table)\n        np.testing.assert_array_equal(\n            yvec, pred.get_column_view(data.domain.class_var)[0])\n\n        evres = self.get_output(self.widget.Outputs.evaluation_results)\n        self.assertIsInstance(evres, Results)\n        self.assertIsInstance(evres.data, Table)\n        ev_yvec, _ = evres.data.get_column_view(data.domain.class_var)\n\n        self.assertTrue(np.all(~np.isnan(ev_yvec)))\n        self.assertTrue(np.all(~np.isnan(evres.actual)))\n\n        with data.unlocked():\n            data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, data)\n        evres = self.get_output(self.widget.Outputs.evaluation_results)\n        self.assertEqual(len(evres.data), 0)\n\n    def test_no_values_target(self):\n        train = Table(\"titanic\")\n        model = ConstantLearner()(train)\n        self.send_signal(self.widget.Inputs.predictors, model)\n        domain = Domain([DiscreteVariable(\"status\", values=(\"first\", \"third\")),\n                         DiscreteVariable(\"age\", values=(\"adult\", \"child\")),\n                         DiscreteVariable(\"sex\", values=(\"female\", \"male\"))],\n                        [DiscreteVariable(\"survived\", values=())])\n        test = Table(domain, np.array([[0, 0, 1], [0, 1, 0], [1, 0, 0]]),\n                     np.full((3, 1), np.nan))\n        self.send_signal(self.widget.Inputs.data, test)\n        pred = self.get_output(self.widget.Outputs.predictions)\n        self.assertEqual(len(pred), len(test))\n\n        results = self.get_output(self.widget.Outputs.evaluation_results)\n\n        cm_widget = self.create_widget(OWConfusionMatrix)\n        self.send_signal(cm_widget.Inputs.evaluation_results, results,\n                         widget=cm_widget)\n\n        ra_widget = self.create_widget(OWROCAnalysis)\n        self.send_signal(ra_widget.Inputs.evaluation_results, results,\n                         widget=ra_widget)\n\n        lc_widget = self.create_widget(OWLiftCurve)\n        self.send_signal(lc_widget.Inputs.evaluation_results, results,\n                         widget=lc_widget)\n\n        cp_widget = self.create_widget(OWCalibrationPlot)\n        self.send_signal(cp_widget.Inputs.evaluation_results, results,\n                         widget=cp_widget)\n\n    def test_mismatching_targets(self):\n        warning = self.widget.Warning\n\n        maj_iris = ConstantLearner()(self.iris)\n        dom = self.iris.domain\n        iris3 = self.iris.transform(Domain(dom[:3], dom[3]))\n        maj_iris3 = ConstantLearner()(iris3)\n\n        self.send_signal(self.widget.Inputs.predictors, maj_iris, 1)\n        self.send_signal(self.widget.Inputs.predictors, maj_iris3, 2)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None, 2)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, maj_iris3, 2)\n        self.assertTrue(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n    def test_no_class_on_test(self):\n        \"\"\"Allow test data with no class\"\"\"\n        titanic = Table(\"titanic\")\n        majority_titanic = ConstantLearner()(titanic)\n\n        no_class = titanic.transform(Domain(titanic.domain.attributes, None))\n        self.send_signal(self.widget.Inputs.predictors, majority_titanic, 1)\n        self.send_signal(self.widget.Inputs.data, no_class)\n        out = self.get_output(self.widget.Outputs.predictions)\n        np.testing.assert_allclose(out.get_column_view(\"constant\")[0], 0)\n\n    def test_bad_data(self):\n        \"\"\"\n        Firstly it creates predictions with TreeLearner. Then sends predictions and\n        different data with different domain to Predictions widget. Those different\n        data and domain are similar to original data and domain but they have three\n        different target values instead of two.\n        GH-2129\n        \"\"\"\n        filestr1 = \"\"\"\\\n        age\\tsex\\tsurvived\n        d\\td\\td\n        \\t\\tclass\n        adult\\tmale\\tyes\n        adult\\tfemale\\tno\n        child\\tmale\\tyes\n        child\\tfemale\\tyes\n        \"\"\"\n        file1 = io.StringIO(filestr1)\n        table = TabReader(file1).read()\n        learner = TreeLearner()\n        tree = learner(table)\n\n        filestr2 = \"\"\"\\\n        age\\tsex\\tsurvived\n        d\\td\\td\n        \\t\\tclass\n        adult\\tmale\\tyes\n        adult\\tfemale\\tno\n        child\\tmale\\tyes\n        child\\tfemale\\tunknown\n        \"\"\"\n        file2 = io.StringIO(filestr2)\n        bad_table = TabReader(file2).read()\n\n        self.send_signal(self.widget.Inputs.predictors, tree, 1)\n\n        with excepthook_catch():\n            self.send_signal(self.widget.Inputs.data, bad_table)\n\n    def test_continuous_class(self):\n        data = Table(\"housing\")\n        cl_data = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.predictors, cl_data, 1)\n        self.send_signal(self.widget.Inputs.data, data)\n\n    def test_changed_class_var(self):\n        def set_input(data, model):\n            self.send_signals([\n                (self.widget.Inputs.data, data),\n                (self.widget.Inputs.predictors, model)\n            ])\n        iris = self.iris\n        learner = ConstantLearner()\n        heart_disease = Table(\"heart_disease\")\n        # catch exceptions in item delegates etc. during switching inputs\n        with excepthook_catch():\n            set_input(iris[:5], learner(iris))\n            set_input(Table(\"housing\"), None)\n            set_input(heart_disease[:5], learner(heart_disease))\n\n    def test_predictor_fails(self):\n        titanic = Table(\"titanic\")\n        failing_model = ConstantLearner()(titanic)\n        failing_model.predict = Mock(side_effect=ValueError(\"foo\"))\n        self.send_signal(self.widget.Inputs.predictors, failing_model, 1)\n        self.send_signal(self.widget.Inputs.data, titanic)\n\n        model2 = ConstantLearner()(titanic)\n        self.send_signal(self.widget.Inputs.predictors, model2, 2)\n\n    def test_sort_matching(self):\n        def get_items_order(model):\n            return model.mapToSourceRows(np.arange(model.rowCount()))\n\n        w = self.widget\n\n        titanic = Table(\"titanic\")\n        majority_titanic = LogisticRegressionLearner()(titanic)\n        self.send_signal(self.widget.Inputs.predictors, majority_titanic)\n        self.send_signal(self.widget.Inputs.data, titanic)\n\n        pred_model = w.predictionsview.model()\n        data_model = w.dataview.model()\n        n = pred_model.rowCount()\n\n        # no sort\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, np.arange(n))\n        np.testing.assert_array_equal(data_order, np.arange(n))\n\n        # sort by first column in prediction table\n        pred_model.sort(0)\n        w.predictionsview.horizontalHeader().sectionClicked.emit(0)\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, data_order)\n\n        # sort by second column in data table\n        data_model.sort(1)\n        w.dataview.horizontalHeader().sectionClicked.emit(0)\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, data_order)\n\n        # restore order\n        w.reset_button.click()\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, np.arange(n))\n        np.testing.assert_array_equal(data_order, np.arange(n))\n\n    def test_sort_predictions(self):\n        \"\"\"\n        Test whether sorting of probabilities by FilterSortProxy is correct.\n        \"\"\"\n        def get_items_order(model):\n            return model.mapToSourceRows(np.arange(model.rowCount()))\n\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        _, log_reg_probs = log_reg_iris(self.iris, Model.ValueProbs)\n        # here I assume that classes are in order 0, 1, 2 in widget\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        widget_order = get_items_order(pred_model)\n\n        # correct order first sort by probs[:,0] then probs[:,1], ...\n        keys = tuple(\n            log_reg_probs[:, i] for i in range(\n                log_reg_probs.shape[1] - 1, -1, -1))\n        sort_order = np.lexsort(keys)\n        np.testing.assert_array_equal(widget_order, sort_order)\n\n    def test_reset_no_data(self):\n        \"\"\"\n        Check no error when resetting the view without model and data\n        \"\"\"\n        self.widget.reset_button.click()\n\n    def test_colors_same_domain(self):\n        \"\"\"\n        Test whether the color selection for values is correct.\n        \"\"\"\n        # pylint: disable=protected-access\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        # case 1: one same model\n        predictor_iris = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(\n            colors, self.iris.domain.class_var.colors)\n\n        # case 2: two same models\n        predictor_iris1 = ConstantLearner()(self.iris)\n        predictor_iris2 = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        # assume that colors have same order since values have same order\n        np.testing.assert_array_equal(\n            colors, self.iris.domain.class_var.colors)\n\n        # case 3: two same models - changed color order\n        idom = self.iris.domain\n        dom = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom.class_var.colors = dom.class_var.colors[::-1]\n        iris2 = self.iris.transform(dom)\n\n        predictor_iris1 = ConstantLearner()(iris2)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris2.domain.class_var.colors)\n\n    def test_colors_diff_domain(self):\n        \"\"\"\n        Test whether the color selection for values is correct.\n        \"\"\"\n        # pylint: disable=protected-access\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        # case 1: two domains one subset other\n        idom = self.iris.domain\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[:2])\n        )\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n        # case 2: two domains one subset other - different color order\n        idom = self.iris.domain\n        colors = idom.class_var.colors[::-1]\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[:2])\n        )\n        dom1.class_var.colors = colors\n        dom2.class_var.colors = colors[:2]\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n        # case 3: domain color, values miss-match - use default colors\n        idom = self.iris.domain\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom1.class_var.colors = dom1.class_var.colors[::-1]\n        iris1 = self.iris.transform(dom1)\n        iris2 = self.iris.transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, LimitedDiscretePalette(3).palette)\n\n        # case 4: two domains different values order, matching colors\n        idom = self.iris.domain\n        # this way we know that default colors are not used\n        colors = LimitedDiscretePalette(5).palette[2:]\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[::-1])\n        )\n        dom1.class_var.colors = colors\n        dom2.class_var.colors = colors[::-1]  # colors mixed same than values\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n    def test_colors_continuous(self):\n        \"\"\"\n        When only continuous variables in predictor no color should be selected\n        we do not work with classes.\n        When we add one classifier there should be colors.\n        \"\"\"\n        # pylint: disable=protected-access\n        data = Table(\"housing\")\n        cl_data = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.predictors, cl_data)\n        self.send_signal(self.widget.Inputs.data, data)\n        colors = self.widget._get_colors()\n        self.assertListEqual([], colors)\n\n        predictor_iris = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris, 1)\n        colors = self.widget._get_colors()\n        self.assertEqual(3, len(colors))\n\n        self.widget.send_report()  # just a quick check that it doesn't crash\n\n    def test_unique_output_domain(self):\n        data = possible_duplicate_table('constant')\n        predictor = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.send_signal(self.widget.Inputs.predictors, predictor)\n\n        output = self.get_output(self.widget.Outputs.predictions)\n        self.assertEqual(output.domain.metas[0].name, 'constant (1)')\n\n    def test_select(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        self.widget.predictionsview.selectRow(1)\n        sel = {(index.row(), index.column())\n               for index in self.widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(1, col) for col in range(5)})\n\n    def test_select_data_first(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        self.widget.predictionsview.selectRow(1)\n        sel = {(index.row(), index.column())\n               for index in self.widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(1, col) for col in range(5)})\n\n    def test_selection_in_setting(self):\n        widget = self.create_widget(OWPredictions,\n                                    stored_settings={\"selection\": [1, 3, 4]})\n        self.send_signal(widget.Inputs.data, self.iris)\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(widget.Inputs.predictors, log_reg_iris)\n        sel = {(index.row(), index.column())\n               for index in widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(row, col)\n                               for row in [1, 3, 4] for col in range(5)})\n        out = self.get_output(widget.Outputs.predictions)\n        exp = self.iris[np.array([1, 3, 4])]\n        np.testing.assert_equal(out.X, exp.X)\n\n    def test_unregister_prediction_model(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.widget.selection_store.unregister = Mock()\n        prev_model = self.widget.predictionsview.model()\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.widget.selection_store.unregister.called_with(prev_model)\n\n    def test_multi_inputs(self):\n        w = self.widget\n        data = self.iris[::5].copy()\n\n        p1 = ConstantLearner()(data)\n        p1.name = \"P1\"\n        p2 = ConstantLearner()(data)\n        p2.name = \"P2\"\n        p3 = ConstantLearner()(data)\n        p3.name = \"P3\"\n        for i, p in enumerate([p1, p2, p3], 1):\n            self.send_signal(w.Inputs.predictors, p, i)\n        self.send_signal(w.Inputs.data, data)\n\n        def check_evres(expected):\n            out = self.get_output(w.Outputs.evaluation_results)\n            self.assertSequenceEqual(out.learner_names, expected)\n\n        check_evres([\"P1\", \"P2\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, None, 2)\n        check_evres([\"P1\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, p2, 2)\n        check_evres([\"P1\", \"P2\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors,\n                         w.Inputs.predictors.closing_sentinel, 2)\n        check_evres([\"P1\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, p2, 2)\n        check_evres([\"P1\", \"P3\", \"P2\"])\n\n    def test_missing_target_cls(self):\n        mask = np.zeros(len(self.iris), dtype=bool)\n        mask[::2] = True\n        train_data = self.iris[~mask]\n        predict_data = self.iris[mask]\n        model = LogisticRegressionLearner()(train_data)\n\n        self.send_signal(self.widget.Inputs.predictors, model)\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[0] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n    def test_missing_target_reg(self):\n        mask = np.zeros(len(self.housing), dtype=bool)\n        mask[::2] = True\n        train_data = self.housing[~mask]\n        predict_data = self.housing[mask]\n        model = LinearRegressionLearner()(train_data)\n\n        self.send_signal(self.widget.Inputs.predictors, model)\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[0] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 32,
                            "end_lineno": 578,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 519,
                            "length": 6
                        },
                        "new_range": {
                            "start": 521,
                            "length": 62
                        },
                        "section": "class TestOWPredictions(WidgetTest):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        self.send_signal(w.Inputs.predictors, p2, 2)",
                                "type": "context"
                            },
                            {
                                "content": "        check_evres([\"P1\", \"P3\", \"P2\"])",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    def test_missing_target_cls(self):",
                                "type": "added"
                            },
                            {
                                "content": "        mask = np.zeros(len(self.iris), dtype=bool)",
                                "type": "added"
                            },
                            {
                                "content": "        mask[::2] = True",
                                "type": "added"
                            },
                            {
                                "content": "        train_data = self.iris[~mask]",
                                "type": "added"
                            },
                            {
                                "content": "        predict_data = self.iris[mask]",
                                "type": "added"
                            },
                            {
                                "content": "        model = LogisticRegressionLearner()(train_data)",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        self.send_signal(self.widget.Inputs.predictors, model)",
                                "type": "added"
                            },
                            {
                                "content": "        self.send_signal(self.widget.Inputs.data, predict_data)",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertFalse(self.widget.Warning.missing_targets.is_shown())",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertFalse(self.widget.Error.scorer_failed.is_shown())",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        with predict_data.unlocked():",
                                "type": "added"
                            },
                            {
                                "content": "            predict_data.Y[0] = np.nan",
                                "type": "added"
                            },
                            {
                                "content": "        self.send_signal(self.widget.Inputs.data, predict_data)",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertTrue(self.widget.Warning.missing_targets.is_shown())",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertFalse(self.widget.Error.scorer_failed.is_shown())",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        with predict_data.unlocked():",
                                "type": "added"
                            },
                            {
                                "content": "            predict_data.Y[:] = np.nan",
                                "type": "added"
                            },
                            {
                                "content": "        self.send_signal(self.widget.Inputs.data, predict_data)",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertTrue(self.widget.Warning.missing_targets.is_shown())",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertFalse(self.widget.Error.scorer_failed.is_shown())",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        self.send_signal(self.widget.Inputs.predictors, None)",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertFalse(self.widget.Warning.missing_targets.is_shown())",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertFalse(self.widget.Error.scorer_failed.is_shown())",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    def test_missing_target_reg(self):",
                                "type": "added"
                            },
                            {
                                "content": "        mask = np.zeros(len(self.housing), dtype=bool)",
                                "type": "added"
                            },
                            {
                                "content": "        mask[::2] = True",
                                "type": "added"
                            },
                            {
                                "content": "        train_data = self.housing[~mask]",
                                "type": "added"
                            },
                            {
                                "content": "        predict_data = self.housing[mask]",
                                "type": "added"
                            },
                            {
                                "content": "        model = LinearRegressionLearner()(train_data)",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        self.send_signal(self.widget.Inputs.predictors, model)",
                                "type": "added"
                            },
                            {
                                "content": "        self.send_signal(self.widget.Inputs.data, predict_data)",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertFalse(self.widget.Warning.missing_targets.is_shown())",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertFalse(self.widget.Error.scorer_failed.is_shown())",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        with predict_data.unlocked():",
                                "type": "added"
                            },
                            {
                                "content": "            predict_data.Y[0] = np.nan",
                                "type": "added"
                            },
                            {
                                "content": "        self.send_signal(self.widget.Inputs.data, predict_data)",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertTrue(self.widget.Warning.missing_targets.is_shown())",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertFalse(self.widget.Error.scorer_failed.is_shown())",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        with predict_data.unlocked():",
                                "type": "added"
                            },
                            {
                                "content": "            predict_data.Y[:] = np.nan",
                                "type": "added"
                            },
                            {
                                "content": "        self.send_signal(self.widget.Inputs.data, predict_data)",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertTrue(self.widget.Warning.missing_targets.is_shown())",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertFalse(self.widget.Error.scorer_failed.is_shown())",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        self.send_signal(self.widget.Inputs.predictors, None)",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertFalse(self.widget.Warning.missing_targets.is_shown())",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertFalse(self.widget.Error.scorer_failed.is_shown())",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "class SelectionModelTest(unittest.TestCase):",
                                "type": "context"
                            },
                            {
                                "content": "    def setUp(self):",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "Orange/widgets/evaluate/tests/test_owpredictions.py",
                            "type": "class",
                            "name": "TestOWPredictions",
                            "content": "class TestOWPredictions(WidgetTest):\n\n    def setUp(self):\n        self.widget = self.create_widget(OWPredictions)  # type: OWPredictions\n        self.iris = Table(\"iris\")\n        self.housing = Table(\"housing\")\n\n    def test_rowCount_from_model(self):\n        \"\"\"Don't crash if the bottom row is visible\"\"\"\n        self.send_signal(self.widget.Inputs.data, self.iris[:5])\n        self.widget.dataview.sizeHintForColumn(0)\n\n    def test_nan_target_input(self):\n        data = self.iris[::10].copy()\n        with data.unlocked():\n            data.Y[1] = np.nan\n        yvec, _ = data.get_column_view(data.domain.class_var)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.send_signal(self.widget.Inputs.predictors, ConstantLearner()(data), 1)\n        pred = self.get_output(self.widget.Outputs.predictions)\n        self.assertIsInstance(pred, Table)\n        np.testing.assert_array_equal(\n            yvec, pred.get_column_view(data.domain.class_var)[0])\n\n        evres = self.get_output(self.widget.Outputs.evaluation_results)\n        self.assertIsInstance(evres, Results)\n        self.assertIsInstance(evres.data, Table)\n        ev_yvec, _ = evres.data.get_column_view(data.domain.class_var)\n\n        self.assertTrue(np.all(~np.isnan(ev_yvec)))\n        self.assertTrue(np.all(~np.isnan(evres.actual)))\n\n        with data.unlocked():\n            data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, data)\n        evres = self.get_output(self.widget.Outputs.evaluation_results)\n        self.assertEqual(len(evres.data), 0)\n\n    def test_no_values_target(self):\n        train = Table(\"titanic\")\n        model = ConstantLearner()(train)\n        self.send_signal(self.widget.Inputs.predictors, model)\n        domain = Domain([DiscreteVariable(\"status\", values=(\"first\", \"third\")),\n                         DiscreteVariable(\"age\", values=(\"adult\", \"child\")),\n                         DiscreteVariable(\"sex\", values=(\"female\", \"male\"))],\n                        [DiscreteVariable(\"survived\", values=())])\n        test = Table(domain, np.array([[0, 0, 1], [0, 1, 0], [1, 0, 0]]),\n                     np.full((3, 1), np.nan))\n        self.send_signal(self.widget.Inputs.data, test)\n        pred = self.get_output(self.widget.Outputs.predictions)\n        self.assertEqual(len(pred), len(test))\n\n        results = self.get_output(self.widget.Outputs.evaluation_results)\n\n        cm_widget = self.create_widget(OWConfusionMatrix)\n        self.send_signal(cm_widget.Inputs.evaluation_results, results,\n                         widget=cm_widget)\n\n        ra_widget = self.create_widget(OWROCAnalysis)\n        self.send_signal(ra_widget.Inputs.evaluation_results, results,\n                         widget=ra_widget)\n\n        lc_widget = self.create_widget(OWLiftCurve)\n        self.send_signal(lc_widget.Inputs.evaluation_results, results,\n                         widget=lc_widget)\n\n        cp_widget = self.create_widget(OWCalibrationPlot)\n        self.send_signal(cp_widget.Inputs.evaluation_results, results,\n                         widget=cp_widget)\n\n    def test_mismatching_targets(self):\n        warning = self.widget.Warning\n\n        maj_iris = ConstantLearner()(self.iris)\n        dom = self.iris.domain\n        iris3 = self.iris.transform(Domain(dom[:3], dom[3]))\n        maj_iris3 = ConstantLearner()(iris3)\n\n        self.send_signal(self.widget.Inputs.predictors, maj_iris, 1)\n        self.send_signal(self.widget.Inputs.predictors, maj_iris3, 2)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None, 2)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, maj_iris3, 2)\n        self.assertTrue(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n    def test_no_class_on_test(self):\n        \"\"\"Allow test data with no class\"\"\"\n        titanic = Table(\"titanic\")\n        majority_titanic = ConstantLearner()(titanic)\n\n        no_class = titanic.transform(Domain(titanic.domain.attributes, None))\n        self.send_signal(self.widget.Inputs.predictors, majority_titanic, 1)\n        self.send_signal(self.widget.Inputs.data, no_class)\n        out = self.get_output(self.widget.Outputs.predictions)\n        np.testing.assert_allclose(out.get_column_view(\"constant\")[0], 0)\n\n    def test_bad_data(self):\n        \"\"\"\n        Firstly it creates predictions with TreeLearner. Then sends predictions and\n        different data with different domain to Predictions widget. Those different\n        data and domain are similar to original data and domain but they have three\n        different target values instead of two.\n        GH-2129\n        \"\"\"\n        filestr1 = \"\"\"\\\n        age\\tsex\\tsurvived\n        d\\td\\td\n        \\t\\tclass\n        adult\\tmale\\tyes\n        adult\\tfemale\\tno\n        child\\tmale\\tyes\n        child\\tfemale\\tyes\n        \"\"\"\n        file1 = io.StringIO(filestr1)\n        table = TabReader(file1).read()\n        learner = TreeLearner()\n        tree = learner(table)\n\n        filestr2 = \"\"\"\\\n        age\\tsex\\tsurvived\n        d\\td\\td\n        \\t\\tclass\n        adult\\tmale\\tyes\n        adult\\tfemale\\tno\n        child\\tmale\\tyes\n        child\\tfemale\\tunknown\n        \"\"\"\n        file2 = io.StringIO(filestr2)\n        bad_table = TabReader(file2).read()\n\n        self.send_signal(self.widget.Inputs.predictors, tree, 1)\n\n        with excepthook_catch():\n            self.send_signal(self.widget.Inputs.data, bad_table)\n\n    def test_continuous_class(self):\n        data = Table(\"housing\")\n        cl_data = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.predictors, cl_data, 1)\n        self.send_signal(self.widget.Inputs.data, data)\n\n    def test_changed_class_var(self):\n        def set_input(data, model):\n            self.send_signals([\n                (self.widget.Inputs.data, data),\n                (self.widget.Inputs.predictors, model)\n            ])\n        iris = self.iris\n        learner = ConstantLearner()\n        heart_disease = Table(\"heart_disease\")\n        # catch exceptions in item delegates etc. during switching inputs\n        with excepthook_catch():\n            set_input(iris[:5], learner(iris))\n            set_input(Table(\"housing\"), None)\n            set_input(heart_disease[:5], learner(heart_disease))\n\n    def test_predictor_fails(self):\n        titanic = Table(\"titanic\")\n        failing_model = ConstantLearner()(titanic)\n        failing_model.predict = Mock(side_effect=ValueError(\"foo\"))\n        self.send_signal(self.widget.Inputs.predictors, failing_model, 1)\n        self.send_signal(self.widget.Inputs.data, titanic)\n\n        model2 = ConstantLearner()(titanic)\n        self.send_signal(self.widget.Inputs.predictors, model2, 2)\n\n    def test_sort_matching(self):\n        def get_items_order(model):\n            return model.mapToSourceRows(np.arange(model.rowCount()))\n\n        w = self.widget\n\n        titanic = Table(\"titanic\")\n        majority_titanic = LogisticRegressionLearner()(titanic)\n        self.send_signal(self.widget.Inputs.predictors, majority_titanic)\n        self.send_signal(self.widget.Inputs.data, titanic)\n\n        pred_model = w.predictionsview.model()\n        data_model = w.dataview.model()\n        n = pred_model.rowCount()\n\n        # no sort\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, np.arange(n))\n        np.testing.assert_array_equal(data_order, np.arange(n))\n\n        # sort by first column in prediction table\n        pred_model.sort(0)\n        w.predictionsview.horizontalHeader().sectionClicked.emit(0)\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, data_order)\n\n        # sort by second column in data table\n        data_model.sort(1)\n        w.dataview.horizontalHeader().sectionClicked.emit(0)\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, data_order)\n\n        # restore order\n        w.reset_button.click()\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, np.arange(n))\n        np.testing.assert_array_equal(data_order, np.arange(n))\n\n    def test_sort_predictions(self):\n        \"\"\"\n        Test whether sorting of probabilities by FilterSortProxy is correct.\n        \"\"\"\n        def get_items_order(model):\n            return model.mapToSourceRows(np.arange(model.rowCount()))\n\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        _, log_reg_probs = log_reg_iris(self.iris, Model.ValueProbs)\n        # here I assume that classes are in order 0, 1, 2 in widget\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        widget_order = get_items_order(pred_model)\n\n        # correct order first sort by probs[:,0] then probs[:,1], ...\n        keys = tuple(\n            log_reg_probs[:, i] for i in range(\n                log_reg_probs.shape[1] - 1, -1, -1))\n        sort_order = np.lexsort(keys)\n        np.testing.assert_array_equal(widget_order, sort_order)\n\n    def test_reset_no_data(self):\n        \"\"\"\n        Check no error when resetting the view without model and data\n        \"\"\"\n        self.widget.reset_button.click()\n\n    def test_colors_same_domain(self):\n        \"\"\"\n        Test whether the color selection for values is correct.\n        \"\"\"\n        # pylint: disable=protected-access\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        # case 1: one same model\n        predictor_iris = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(\n            colors, self.iris.domain.class_var.colors)\n\n        # case 2: two same models\n        predictor_iris1 = ConstantLearner()(self.iris)\n        predictor_iris2 = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        # assume that colors have same order since values have same order\n        np.testing.assert_array_equal(\n            colors, self.iris.domain.class_var.colors)\n\n        # case 3: two same models - changed color order\n        idom = self.iris.domain\n        dom = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom.class_var.colors = dom.class_var.colors[::-1]\n        iris2 = self.iris.transform(dom)\n\n        predictor_iris1 = ConstantLearner()(iris2)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris2.domain.class_var.colors)\n\n    def test_colors_diff_domain(self):\n        \"\"\"\n        Test whether the color selection for values is correct.\n        \"\"\"\n        # pylint: disable=protected-access\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        # case 1: two domains one subset other\n        idom = self.iris.domain\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[:2])\n        )\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n        # case 2: two domains one subset other - different color order\n        idom = self.iris.domain\n        colors = idom.class_var.colors[::-1]\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[:2])\n        )\n        dom1.class_var.colors = colors\n        dom2.class_var.colors = colors[:2]\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n        # case 3: domain color, values miss-match - use default colors\n        idom = self.iris.domain\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom1.class_var.colors = dom1.class_var.colors[::-1]\n        iris1 = self.iris.transform(dom1)\n        iris2 = self.iris.transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, LimitedDiscretePalette(3).palette)\n\n        # case 4: two domains different values order, matching colors\n        idom = self.iris.domain\n        # this way we know that default colors are not used\n        colors = LimitedDiscretePalette(5).palette[2:]\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[::-1])\n        )\n        dom1.class_var.colors = colors\n        dom2.class_var.colors = colors[::-1]  # colors mixed same than values\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n    def test_colors_continuous(self):\n        \"\"\"\n        When only continuous variables in predictor no color should be selected\n        we do not work with classes.\n        When we add one classifier there should be colors.\n        \"\"\"\n        # pylint: disable=protected-access\n        data = Table(\"housing\")\n        cl_data = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.predictors, cl_data)\n        self.send_signal(self.widget.Inputs.data, data)\n        colors = self.widget._get_colors()\n        self.assertListEqual([], colors)\n\n        predictor_iris = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris, 1)\n        colors = self.widget._get_colors()\n        self.assertEqual(3, len(colors))\n\n        self.widget.send_report()  # just a quick check that it doesn't crash\n\n    def test_unique_output_domain(self):\n        data = possible_duplicate_table('constant')\n        predictor = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.send_signal(self.widget.Inputs.predictors, predictor)\n\n        output = self.get_output(self.widget.Outputs.predictions)\n        self.assertEqual(output.domain.metas[0].name, 'constant (1)')\n\n    def test_select(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        self.widget.predictionsview.selectRow(1)\n        sel = {(index.row(), index.column())\n               for index in self.widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(1, col) for col in range(5)})\n\n    def test_select_data_first(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        self.widget.predictionsview.selectRow(1)\n        sel = {(index.row(), index.column())\n               for index in self.widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(1, col) for col in range(5)})\n\n    def test_selection_in_setting(self):\n        widget = self.create_widget(OWPredictions,\n                                    stored_settings={\"selection\": [1, 3, 4]})\n        self.send_signal(widget.Inputs.data, self.iris)\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(widget.Inputs.predictors, log_reg_iris)\n        sel = {(index.row(), index.column())\n               for index in widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(row, col)\n                               for row in [1, 3, 4] for col in range(5)})\n        out = self.get_output(widget.Outputs.predictions)\n        exp = self.iris[np.array([1, 3, 4])]\n        np.testing.assert_equal(out.X, exp.X)\n\n    def test_unregister_prediction_model(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.widget.selection_store.unregister = Mock()\n        prev_model = self.widget.predictionsview.model()\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.widget.selection_store.unregister.called_with(prev_model)\n\n    def test_multi_inputs(self):\n        w = self.widget\n        data = self.iris[::5].copy()\n\n        p1 = ConstantLearner()(data)\n        p1.name = \"P1\"\n        p2 = ConstantLearner()(data)\n        p2.name = \"P2\"\n        p3 = ConstantLearner()(data)\n        p3.name = \"P3\"\n        for i, p in enumerate([p1, p2, p3], 1):\n            self.send_signal(w.Inputs.predictors, p, i)\n        self.send_signal(w.Inputs.data, data)\n\n        def check_evres(expected):\n            out = self.get_output(w.Outputs.evaluation_results)\n            self.assertSequenceEqual(out.learner_names, expected)\n\n        check_evres([\"P1\", \"P2\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, None, 2)\n        check_evres([\"P1\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, p2, 2)\n        check_evres([\"P1\", \"P2\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors,\n                         w.Inputs.predictors.closing_sentinel, 2)\n        check_evres([\"P1\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, p2, 2)\n        check_evres([\"P1\", \"P3\", \"P2\"])\n\n    def test_missing_target_cls(self):\n        mask = np.zeros(len(self.iris), dtype=bool)\n        mask[::2] = True\n        train_data = self.iris[~mask]\n        predict_data = self.iris[mask]\n        model = LogisticRegressionLearner()(train_data)\n\n        self.send_signal(self.widget.Inputs.predictors, model)\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[0] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n    def test_missing_target_reg(self):\n        mask = np.zeros(len(self.housing), dtype=bool)\n        mask[::2] = True\n        train_data = self.housing[~mask]\n        predict_data = self.housing[mask]\n        model = LinearRegressionLearner()(train_data)\n\n        self.send_signal(self.widget.Inputs.predictors, model)\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[0] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 32,
                            "end_lineno": 578,
                            "parent": null
                        }
                    ],
                    "added_entities": [
                        {
                            "file_name": "Orange/widgets/evaluate/tests/test_owpredictions.py",
                            "type": "method",
                            "name": "TestOWPredictions.test_missing_target_cls",
                            "content": "class TestOWPredictions(WidgetTest):\n\n    def setUp(self):\n        self.widget = self.create_widget(OWPredictions)  # type: OWPredictions\n        self.iris = Table(\"iris\")\n        self.housing = Table(\"housing\")\n\n    def test_rowCount_from_model(self):\n        \"\"\"Don't crash if the bottom row is visible\"\"\"\n        self.send_signal(self.widget.Inputs.data, self.iris[:5])\n        self.widget.dataview.sizeHintForColumn(0)\n\n    def test_nan_target_input(self):\n        data = self.iris[::10].copy()\n        with data.unlocked():\n            data.Y[1] = np.nan\n        yvec, _ = data.get_column_view(data.domain.class_var)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.send_signal(self.widget.Inputs.predictors, ConstantLearner()(data), 1)\n        pred = self.get_output(self.widget.Outputs.predictions)\n        self.assertIsInstance(pred, Table)\n        np.testing.assert_array_equal(\n            yvec, pred.get_column_view(data.domain.class_var)[0])\n\n        evres = self.get_output(self.widget.Outputs.evaluation_results)\n        self.assertIsInstance(evres, Results)\n        self.assertIsInstance(evres.data, Table)\n        ev_yvec, _ = evres.data.get_column_view(data.domain.class_var)\n\n        self.assertTrue(np.all(~np.isnan(ev_yvec)))\n        self.assertTrue(np.all(~np.isnan(evres.actual)))\n\n        with data.unlocked():\n            data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, data)\n        evres = self.get_output(self.widget.Outputs.evaluation_results)\n        self.assertEqual(len(evres.data), 0)\n\n    def test_no_values_target(self):\n        train = Table(\"titanic\")\n        model = ConstantLearner()(train)\n        self.send_signal(self.widget.Inputs.predictors, model)\n        domain = Domain([DiscreteVariable(\"status\", values=(\"first\", \"third\")),\n                         DiscreteVariable(\"age\", values=(\"adult\", \"child\")),\n                         DiscreteVariable(\"sex\", values=(\"female\", \"male\"))],\n                        [DiscreteVariable(\"survived\", values=())])\n        test = Table(domain, np.array([[0, 0, 1], [0, 1, 0], [1, 0, 0]]),\n                     np.full((3, 1), np.nan))\n        self.send_signal(self.widget.Inputs.data, test)\n        pred = self.get_output(self.widget.Outputs.predictions)\n        self.assertEqual(len(pred), len(test))\n\n        results = self.get_output(self.widget.Outputs.evaluation_results)\n\n        cm_widget = self.create_widget(OWConfusionMatrix)\n        self.send_signal(cm_widget.Inputs.evaluation_results, results,\n                         widget=cm_widget)\n\n        ra_widget = self.create_widget(OWROCAnalysis)\n        self.send_signal(ra_widget.Inputs.evaluation_results, results,\n                         widget=ra_widget)\n\n        lc_widget = self.create_widget(OWLiftCurve)\n        self.send_signal(lc_widget.Inputs.evaluation_results, results,\n                         widget=lc_widget)\n\n        cp_widget = self.create_widget(OWCalibrationPlot)\n        self.send_signal(cp_widget.Inputs.evaluation_results, results,\n                         widget=cp_widget)\n\n    def test_mismatching_targets(self):\n        warning = self.widget.Warning\n\n        maj_iris = ConstantLearner()(self.iris)\n        dom = self.iris.domain\n        iris3 = self.iris.transform(Domain(dom[:3], dom[3]))\n        maj_iris3 = ConstantLearner()(iris3)\n\n        self.send_signal(self.widget.Inputs.predictors, maj_iris, 1)\n        self.send_signal(self.widget.Inputs.predictors, maj_iris3, 2)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None, 2)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, maj_iris3, 2)\n        self.assertTrue(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n    def test_no_class_on_test(self):\n        \"\"\"Allow test data with no class\"\"\"\n        titanic = Table(\"titanic\")\n        majority_titanic = ConstantLearner()(titanic)\n\n        no_class = titanic.transform(Domain(titanic.domain.attributes, None))\n        self.send_signal(self.widget.Inputs.predictors, majority_titanic, 1)\n        self.send_signal(self.widget.Inputs.data, no_class)\n        out = self.get_output(self.widget.Outputs.predictions)\n        np.testing.assert_allclose(out.get_column_view(\"constant\")[0], 0)\n\n    def test_bad_data(self):\n        \"\"\"\n        Firstly it creates predictions with TreeLearner. Then sends predictions and\n        different data with different domain to Predictions widget. Those different\n        data and domain are similar to original data and domain but they have three\n        different target values instead of two.\n        GH-2129\n        \"\"\"\n        filestr1 = \"\"\"\\\n        age\\tsex\\tsurvived\n        d\\td\\td\n        \\t\\tclass\n        adult\\tmale\\tyes\n        adult\\tfemale\\tno\n        child\\tmale\\tyes\n        child\\tfemale\\tyes\n        \"\"\"\n        file1 = io.StringIO(filestr1)\n        table = TabReader(file1).read()\n        learner = TreeLearner()\n        tree = learner(table)\n\n        filestr2 = \"\"\"\\\n        age\\tsex\\tsurvived\n        d\\td\\td\n        \\t\\tclass\n        adult\\tmale\\tyes\n        adult\\tfemale\\tno\n        child\\tmale\\tyes\n        child\\tfemale\\tunknown\n        \"\"\"\n        file2 = io.StringIO(filestr2)\n        bad_table = TabReader(file2).read()\n\n        self.send_signal(self.widget.Inputs.predictors, tree, 1)\n\n        with excepthook_catch():\n            self.send_signal(self.widget.Inputs.data, bad_table)\n\n    def test_continuous_class(self):\n        data = Table(\"housing\")\n        cl_data = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.predictors, cl_data, 1)\n        self.send_signal(self.widget.Inputs.data, data)\n\n    def test_changed_class_var(self):\n        def set_input(data, model):\n            self.send_signals([\n                (self.widget.Inputs.data, data),\n                (self.widget.Inputs.predictors, model)\n            ])\n        iris = self.iris\n        learner = ConstantLearner()\n        heart_disease = Table(\"heart_disease\")\n        # catch exceptions in item delegates etc. during switching inputs\n        with excepthook_catch():\n            set_input(iris[:5], learner(iris))\n            set_input(Table(\"housing\"), None)\n            set_input(heart_disease[:5], learner(heart_disease))\n\n    def test_predictor_fails(self):\n        titanic = Table(\"titanic\")\n        failing_model = ConstantLearner()(titanic)\n        failing_model.predict = Mock(side_effect=ValueError(\"foo\"))\n        self.send_signal(self.widget.Inputs.predictors, failing_model, 1)\n        self.send_signal(self.widget.Inputs.data, titanic)\n\n        model2 = ConstantLearner()(titanic)\n        self.send_signal(self.widget.Inputs.predictors, model2, 2)\n\n    def test_sort_matching(self):\n        def get_items_order(model):\n            return model.mapToSourceRows(np.arange(model.rowCount()))\n\n        w = self.widget\n\n        titanic = Table(\"titanic\")\n        majority_titanic = LogisticRegressionLearner()(titanic)\n        self.send_signal(self.widget.Inputs.predictors, majority_titanic)\n        self.send_signal(self.widget.Inputs.data, titanic)\n\n        pred_model = w.predictionsview.model()\n        data_model = w.dataview.model()\n        n = pred_model.rowCount()\n\n        # no sort\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, np.arange(n))\n        np.testing.assert_array_equal(data_order, np.arange(n))\n\n        # sort by first column in prediction table\n        pred_model.sort(0)\n        w.predictionsview.horizontalHeader().sectionClicked.emit(0)\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, data_order)\n\n        # sort by second column in data table\n        data_model.sort(1)\n        w.dataview.horizontalHeader().sectionClicked.emit(0)\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, data_order)\n\n        # restore order\n        w.reset_button.click()\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, np.arange(n))\n        np.testing.assert_array_equal(data_order, np.arange(n))\n\n    def test_sort_predictions(self):\n        \"\"\"\n        Test whether sorting of probabilities by FilterSortProxy is correct.\n        \"\"\"\n        def get_items_order(model):\n            return model.mapToSourceRows(np.arange(model.rowCount()))\n\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        _, log_reg_probs = log_reg_iris(self.iris, Model.ValueProbs)\n        # here I assume that classes are in order 0, 1, 2 in widget\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        widget_order = get_items_order(pred_model)\n\n        # correct order first sort by probs[:,0] then probs[:,1], ...\n        keys = tuple(\n            log_reg_probs[:, i] for i in range(\n                log_reg_probs.shape[1] - 1, -1, -1))\n        sort_order = np.lexsort(keys)\n        np.testing.assert_array_equal(widget_order, sort_order)\n\n    def test_reset_no_data(self):\n        \"\"\"\n        Check no error when resetting the view without model and data\n        \"\"\"\n        self.widget.reset_button.click()\n\n    def test_colors_same_domain(self):\n        \"\"\"\n        Test whether the color selection for values is correct.\n        \"\"\"\n        # pylint: disable=protected-access\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        # case 1: one same model\n        predictor_iris = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(\n            colors, self.iris.domain.class_var.colors)\n\n        # case 2: two same models\n        predictor_iris1 = ConstantLearner()(self.iris)\n        predictor_iris2 = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        # assume that colors have same order since values have same order\n        np.testing.assert_array_equal(\n            colors, self.iris.domain.class_var.colors)\n\n        # case 3: two same models - changed color order\n        idom = self.iris.domain\n        dom = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom.class_var.colors = dom.class_var.colors[::-1]\n        iris2 = self.iris.transform(dom)\n\n        predictor_iris1 = ConstantLearner()(iris2)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris2.domain.class_var.colors)\n\n    def test_colors_diff_domain(self):\n        \"\"\"\n        Test whether the color selection for values is correct.\n        \"\"\"\n        # pylint: disable=protected-access\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        # case 1: two domains one subset other\n        idom = self.iris.domain\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[:2])\n        )\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n        # case 2: two domains one subset other - different color order\n        idom = self.iris.domain\n        colors = idom.class_var.colors[::-1]\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[:2])\n        )\n        dom1.class_var.colors = colors\n        dom2.class_var.colors = colors[:2]\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n        # case 3: domain color, values miss-match - use default colors\n        idom = self.iris.domain\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom1.class_var.colors = dom1.class_var.colors[::-1]\n        iris1 = self.iris.transform(dom1)\n        iris2 = self.iris.transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, LimitedDiscretePalette(3).palette)\n\n        # case 4: two domains different values order, matching colors\n        idom = self.iris.domain\n        # this way we know that default colors are not used\n        colors = LimitedDiscretePalette(5).palette[2:]\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[::-1])\n        )\n        dom1.class_var.colors = colors\n        dom2.class_var.colors = colors[::-1]  # colors mixed same than values\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n    def test_colors_continuous(self):\n        \"\"\"\n        When only continuous variables in predictor no color should be selected\n        we do not work with classes.\n        When we add one classifier there should be colors.\n        \"\"\"\n        # pylint: disable=protected-access\n        data = Table(\"housing\")\n        cl_data = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.predictors, cl_data)\n        self.send_signal(self.widget.Inputs.data, data)\n        colors = self.widget._get_colors()\n        self.assertListEqual([], colors)\n\n        predictor_iris = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris, 1)\n        colors = self.widget._get_colors()\n        self.assertEqual(3, len(colors))\n\n        self.widget.send_report()  # just a quick check that it doesn't crash\n\n    def test_unique_output_domain(self):\n        data = possible_duplicate_table('constant')\n        predictor = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.send_signal(self.widget.Inputs.predictors, predictor)\n\n        output = self.get_output(self.widget.Outputs.predictions)\n        self.assertEqual(output.domain.metas[0].name, 'constant (1)')\n\n    def test_select(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        self.widget.predictionsview.selectRow(1)\n        sel = {(index.row(), index.column())\n               for index in self.widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(1, col) for col in range(5)})\n\n    def test_select_data_first(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        self.widget.predictionsview.selectRow(1)\n        sel = {(index.row(), index.column())\n               for index in self.widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(1, col) for col in range(5)})\n\n    def test_selection_in_setting(self):\n        widget = self.create_widget(OWPredictions,\n                                    stored_settings={\"selection\": [1, 3, 4]})\n        self.send_signal(widget.Inputs.data, self.iris)\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(widget.Inputs.predictors, log_reg_iris)\n        sel = {(index.row(), index.column())\n               for index in widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(row, col)\n                               for row in [1, 3, 4] for col in range(5)})\n        out = self.get_output(widget.Outputs.predictions)\n        exp = self.iris[np.array([1, 3, 4])]\n        np.testing.assert_equal(out.X, exp.X)\n\n    def test_unregister_prediction_model(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.widget.selection_store.unregister = Mock()\n        prev_model = self.widget.predictionsview.model()\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.widget.selection_store.unregister.called_with(prev_model)\n\n    def test_multi_inputs(self):\n        w = self.widget\n        data = self.iris[::5].copy()\n\n        p1 = ConstantLearner()(data)\n        p1.name = \"P1\"\n        p2 = ConstantLearner()(data)\n        p2.name = \"P2\"\n        p3 = ConstantLearner()(data)\n        p3.name = \"P3\"\n        for i, p in enumerate([p1, p2, p3], 1):\n            self.send_signal(w.Inputs.predictors, p, i)\n        self.send_signal(w.Inputs.data, data)\n\n        def check_evres(expected):\n            out = self.get_output(w.Outputs.evaluation_results)\n            self.assertSequenceEqual(out.learner_names, expected)\n\n        check_evres([\"P1\", \"P2\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, None, 2)\n        check_evres([\"P1\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, p2, 2)\n        check_evres([\"P1\", \"P2\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors,\n                         w.Inputs.predictors.closing_sentinel, 2)\n        check_evres([\"P1\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, p2, 2)\n        check_evres([\"P1\", \"P3\", \"P2\"])\n\n    def test_missing_target_cls(self):\n        mask = np.zeros(len(self.iris), dtype=bool)\n        mask[::2] = True\n        train_data = self.iris[~mask]\n        predict_data = self.iris[mask]\n        model = LogisticRegressionLearner()(train_data)\n\n        self.send_signal(self.widget.Inputs.predictors, model)\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[0] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n    def test_missing_target_reg(self):\n        mask = np.zeros(len(self.housing), dtype=bool)\n        mask[::2] = True\n        train_data = self.housing[~mask]\n        predict_data = self.housing[mask]\n        model = LinearRegressionLearner()(train_data)\n\n        self.send_signal(self.widget.Inputs.predictors, model)\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[0] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 524,
                            "end_lineno": 550,
                            "parent": {
                                "file_name": "Orange/widgets/evaluate/tests/test_owpredictions.py",
                                "type": "class",
                                "name": "TestOWPredictions",
                                "content": "class TestOWPredictions(WidgetTest):\n\n    def setUp(self):\n        self.widget = self.create_widget(OWPredictions)  # type: OWPredictions\n        self.iris = Table(\"iris\")\n        self.housing = Table(\"housing\")\n\n    def test_rowCount_from_model(self):\n        \"\"\"Don't crash if the bottom row is visible\"\"\"\n        self.send_signal(self.widget.Inputs.data, self.iris[:5])\n        self.widget.dataview.sizeHintForColumn(0)\n\n    def test_nan_target_input(self):\n        data = self.iris[::10].copy()\n        with data.unlocked():\n            data.Y[1] = np.nan\n        yvec, _ = data.get_column_view(data.domain.class_var)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.send_signal(self.widget.Inputs.predictors, ConstantLearner()(data), 1)\n        pred = self.get_output(self.widget.Outputs.predictions)\n        self.assertIsInstance(pred, Table)\n        np.testing.assert_array_equal(\n            yvec, pred.get_column_view(data.domain.class_var)[0])\n\n        evres = self.get_output(self.widget.Outputs.evaluation_results)\n        self.assertIsInstance(evres, Results)\n        self.assertIsInstance(evres.data, Table)\n        ev_yvec, _ = evres.data.get_column_view(data.domain.class_var)\n\n        self.assertTrue(np.all(~np.isnan(ev_yvec)))\n        self.assertTrue(np.all(~np.isnan(evres.actual)))\n\n        with data.unlocked():\n            data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, data)\n        evres = self.get_output(self.widget.Outputs.evaluation_results)\n        self.assertEqual(len(evres.data), 0)\n\n    def test_no_values_target(self):\n        train = Table(\"titanic\")\n        model = ConstantLearner()(train)\n        self.send_signal(self.widget.Inputs.predictors, model)\n        domain = Domain([DiscreteVariable(\"status\", values=(\"first\", \"third\")),\n                         DiscreteVariable(\"age\", values=(\"adult\", \"child\")),\n                         DiscreteVariable(\"sex\", values=(\"female\", \"male\"))],\n                        [DiscreteVariable(\"survived\", values=())])\n        test = Table(domain, np.array([[0, 0, 1], [0, 1, 0], [1, 0, 0]]),\n                     np.full((3, 1), np.nan))\n        self.send_signal(self.widget.Inputs.data, test)\n        pred = self.get_output(self.widget.Outputs.predictions)\n        self.assertEqual(len(pred), len(test))\n\n        results = self.get_output(self.widget.Outputs.evaluation_results)\n\n        cm_widget = self.create_widget(OWConfusionMatrix)\n        self.send_signal(cm_widget.Inputs.evaluation_results, results,\n                         widget=cm_widget)\n\n        ra_widget = self.create_widget(OWROCAnalysis)\n        self.send_signal(ra_widget.Inputs.evaluation_results, results,\n                         widget=ra_widget)\n\n        lc_widget = self.create_widget(OWLiftCurve)\n        self.send_signal(lc_widget.Inputs.evaluation_results, results,\n                         widget=lc_widget)\n\n        cp_widget = self.create_widget(OWCalibrationPlot)\n        self.send_signal(cp_widget.Inputs.evaluation_results, results,\n                         widget=cp_widget)\n\n    def test_mismatching_targets(self):\n        warning = self.widget.Warning\n\n        maj_iris = ConstantLearner()(self.iris)\n        dom = self.iris.domain\n        iris3 = self.iris.transform(Domain(dom[:3], dom[3]))\n        maj_iris3 = ConstantLearner()(iris3)\n\n        self.send_signal(self.widget.Inputs.predictors, maj_iris, 1)\n        self.send_signal(self.widget.Inputs.predictors, maj_iris3, 2)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None, 2)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, maj_iris3, 2)\n        self.assertTrue(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n    def test_no_class_on_test(self):\n        \"\"\"Allow test data with no class\"\"\"\n        titanic = Table(\"titanic\")\n        majority_titanic = ConstantLearner()(titanic)\n\n        no_class = titanic.transform(Domain(titanic.domain.attributes, None))\n        self.send_signal(self.widget.Inputs.predictors, majority_titanic, 1)\n        self.send_signal(self.widget.Inputs.data, no_class)\n        out = self.get_output(self.widget.Outputs.predictions)\n        np.testing.assert_allclose(out.get_column_view(\"constant\")[0], 0)\n\n    def test_bad_data(self):\n        \"\"\"\n        Firstly it creates predictions with TreeLearner. Then sends predictions and\n        different data with different domain to Predictions widget. Those different\n        data and domain are similar to original data and domain but they have three\n        different target values instead of two.\n        GH-2129\n        \"\"\"\n        filestr1 = \"\"\"\\\n        age\\tsex\\tsurvived\n        d\\td\\td\n        \\t\\tclass\n        adult\\tmale\\tyes\n        adult\\tfemale\\tno\n        child\\tmale\\tyes\n        child\\tfemale\\tyes\n        \"\"\"\n        file1 = io.StringIO(filestr1)\n        table = TabReader(file1).read()\n        learner = TreeLearner()\n        tree = learner(table)\n\n        filestr2 = \"\"\"\\\n        age\\tsex\\tsurvived\n        d\\td\\td\n        \\t\\tclass\n        adult\\tmale\\tyes\n        adult\\tfemale\\tno\n        child\\tmale\\tyes\n        child\\tfemale\\tunknown\n        \"\"\"\n        file2 = io.StringIO(filestr2)\n        bad_table = TabReader(file2).read()\n\n        self.send_signal(self.widget.Inputs.predictors, tree, 1)\n\n        with excepthook_catch():\n            self.send_signal(self.widget.Inputs.data, bad_table)\n\n    def test_continuous_class(self):\n        data = Table(\"housing\")\n        cl_data = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.predictors, cl_data, 1)\n        self.send_signal(self.widget.Inputs.data, data)\n\n    def test_changed_class_var(self):\n        def set_input(data, model):\n            self.send_signals([\n                (self.widget.Inputs.data, data),\n                (self.widget.Inputs.predictors, model)\n            ])\n        iris = self.iris\n        learner = ConstantLearner()\n        heart_disease = Table(\"heart_disease\")\n        # catch exceptions in item delegates etc. during switching inputs\n        with excepthook_catch():\n            set_input(iris[:5], learner(iris))\n            set_input(Table(\"housing\"), None)\n            set_input(heart_disease[:5], learner(heart_disease))\n\n    def test_predictor_fails(self):\n        titanic = Table(\"titanic\")\n        failing_model = ConstantLearner()(titanic)\n        failing_model.predict = Mock(side_effect=ValueError(\"foo\"))\n        self.send_signal(self.widget.Inputs.predictors, failing_model, 1)\n        self.send_signal(self.widget.Inputs.data, titanic)\n\n        model2 = ConstantLearner()(titanic)\n        self.send_signal(self.widget.Inputs.predictors, model2, 2)\n\n    def test_sort_matching(self):\n        def get_items_order(model):\n            return model.mapToSourceRows(np.arange(model.rowCount()))\n\n        w = self.widget\n\n        titanic = Table(\"titanic\")\n        majority_titanic = LogisticRegressionLearner()(titanic)\n        self.send_signal(self.widget.Inputs.predictors, majority_titanic)\n        self.send_signal(self.widget.Inputs.data, titanic)\n\n        pred_model = w.predictionsview.model()\n        data_model = w.dataview.model()\n        n = pred_model.rowCount()\n\n        # no sort\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, np.arange(n))\n        np.testing.assert_array_equal(data_order, np.arange(n))\n\n        # sort by first column in prediction table\n        pred_model.sort(0)\n        w.predictionsview.horizontalHeader().sectionClicked.emit(0)\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, data_order)\n\n        # sort by second column in data table\n        data_model.sort(1)\n        w.dataview.horizontalHeader().sectionClicked.emit(0)\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, data_order)\n\n        # restore order\n        w.reset_button.click()\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, np.arange(n))\n        np.testing.assert_array_equal(data_order, np.arange(n))\n\n    def test_sort_predictions(self):\n        \"\"\"\n        Test whether sorting of probabilities by FilterSortProxy is correct.\n        \"\"\"\n        def get_items_order(model):\n            return model.mapToSourceRows(np.arange(model.rowCount()))\n\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        _, log_reg_probs = log_reg_iris(self.iris, Model.ValueProbs)\n        # here I assume that classes are in order 0, 1, 2 in widget\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        widget_order = get_items_order(pred_model)\n\n        # correct order first sort by probs[:,0] then probs[:,1], ...\n        keys = tuple(\n            log_reg_probs[:, i] for i in range(\n                log_reg_probs.shape[1] - 1, -1, -1))\n        sort_order = np.lexsort(keys)\n        np.testing.assert_array_equal(widget_order, sort_order)\n\n    def test_reset_no_data(self):\n        \"\"\"\n        Check no error when resetting the view without model and data\n        \"\"\"\n        self.widget.reset_button.click()\n\n    def test_colors_same_domain(self):\n        \"\"\"\n        Test whether the color selection for values is correct.\n        \"\"\"\n        # pylint: disable=protected-access\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        # case 1: one same model\n        predictor_iris = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(\n            colors, self.iris.domain.class_var.colors)\n\n        # case 2: two same models\n        predictor_iris1 = ConstantLearner()(self.iris)\n        predictor_iris2 = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        # assume that colors have same order since values have same order\n        np.testing.assert_array_equal(\n            colors, self.iris.domain.class_var.colors)\n\n        # case 3: two same models - changed color order\n        idom = self.iris.domain\n        dom = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom.class_var.colors = dom.class_var.colors[::-1]\n        iris2 = self.iris.transform(dom)\n\n        predictor_iris1 = ConstantLearner()(iris2)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris2.domain.class_var.colors)\n\n    def test_colors_diff_domain(self):\n        \"\"\"\n        Test whether the color selection for values is correct.\n        \"\"\"\n        # pylint: disable=protected-access\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        # case 1: two domains one subset other\n        idom = self.iris.domain\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[:2])\n        )\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n        # case 2: two domains one subset other - different color order\n        idom = self.iris.domain\n        colors = idom.class_var.colors[::-1]\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[:2])\n        )\n        dom1.class_var.colors = colors\n        dom2.class_var.colors = colors[:2]\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n        # case 3: domain color, values miss-match - use default colors\n        idom = self.iris.domain\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom1.class_var.colors = dom1.class_var.colors[::-1]\n        iris1 = self.iris.transform(dom1)\n        iris2 = self.iris.transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, LimitedDiscretePalette(3).palette)\n\n        # case 4: two domains different values order, matching colors\n        idom = self.iris.domain\n        # this way we know that default colors are not used\n        colors = LimitedDiscretePalette(5).palette[2:]\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[::-1])\n        )\n        dom1.class_var.colors = colors\n        dom2.class_var.colors = colors[::-1]  # colors mixed same than values\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n    def test_colors_continuous(self):\n        \"\"\"\n        When only continuous variables in predictor no color should be selected\n        we do not work with classes.\n        When we add one classifier there should be colors.\n        \"\"\"\n        # pylint: disable=protected-access\n        data = Table(\"housing\")\n        cl_data = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.predictors, cl_data)\n        self.send_signal(self.widget.Inputs.data, data)\n        colors = self.widget._get_colors()\n        self.assertListEqual([], colors)\n\n        predictor_iris = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris, 1)\n        colors = self.widget._get_colors()\n        self.assertEqual(3, len(colors))\n\n        self.widget.send_report()  # just a quick check that it doesn't crash\n\n    def test_unique_output_domain(self):\n        data = possible_duplicate_table('constant')\n        predictor = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.send_signal(self.widget.Inputs.predictors, predictor)\n\n        output = self.get_output(self.widget.Outputs.predictions)\n        self.assertEqual(output.domain.metas[0].name, 'constant (1)')\n\n    def test_select(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        self.widget.predictionsview.selectRow(1)\n        sel = {(index.row(), index.column())\n               for index in self.widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(1, col) for col in range(5)})\n\n    def test_select_data_first(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        self.widget.predictionsview.selectRow(1)\n        sel = {(index.row(), index.column())\n               for index in self.widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(1, col) for col in range(5)})\n\n    def test_selection_in_setting(self):\n        widget = self.create_widget(OWPredictions,\n                                    stored_settings={\"selection\": [1, 3, 4]})\n        self.send_signal(widget.Inputs.data, self.iris)\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(widget.Inputs.predictors, log_reg_iris)\n        sel = {(index.row(), index.column())\n               for index in widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(row, col)\n                               for row in [1, 3, 4] for col in range(5)})\n        out = self.get_output(widget.Outputs.predictions)\n        exp = self.iris[np.array([1, 3, 4])]\n        np.testing.assert_equal(out.X, exp.X)\n\n    def test_unregister_prediction_model(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.widget.selection_store.unregister = Mock()\n        prev_model = self.widget.predictionsview.model()\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.widget.selection_store.unregister.called_with(prev_model)\n\n    def test_multi_inputs(self):\n        w = self.widget\n        data = self.iris[::5].copy()\n\n        p1 = ConstantLearner()(data)\n        p1.name = \"P1\"\n        p2 = ConstantLearner()(data)\n        p2.name = \"P2\"\n        p3 = ConstantLearner()(data)\n        p3.name = \"P3\"\n        for i, p in enumerate([p1, p2, p3], 1):\n            self.send_signal(w.Inputs.predictors, p, i)\n        self.send_signal(w.Inputs.data, data)\n\n        def check_evres(expected):\n            out = self.get_output(w.Outputs.evaluation_results)\n            self.assertSequenceEqual(out.learner_names, expected)\n\n        check_evres([\"P1\", \"P2\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, None, 2)\n        check_evres([\"P1\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, p2, 2)\n        check_evres([\"P1\", \"P2\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors,\n                         w.Inputs.predictors.closing_sentinel, 2)\n        check_evres([\"P1\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, p2, 2)\n        check_evres([\"P1\", \"P3\", \"P2\"])\n\n    def test_missing_target_cls(self):\n        mask = np.zeros(len(self.iris), dtype=bool)\n        mask[::2] = True\n        train_data = self.iris[~mask]\n        predict_data = self.iris[mask]\n        model = LogisticRegressionLearner()(train_data)\n\n        self.send_signal(self.widget.Inputs.predictors, model)\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[0] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n    def test_missing_target_reg(self):\n        mask = np.zeros(len(self.housing), dtype=bool)\n        mask[::2] = True\n        train_data = self.housing[~mask]\n        predict_data = self.housing[mask]\n        model = LinearRegressionLearner()(train_data)\n\n        self.send_signal(self.widget.Inputs.predictors, model)\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[0] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 32,
                                "end_lineno": 578,
                                "parent": null
                            }
                        },
                        {
                            "file_name": "Orange/widgets/evaluate/tests/test_owpredictions.py",
                            "type": "method",
                            "name": "TestOWPredictions.test_missing_target_reg",
                            "content": "class TestOWPredictions(WidgetTest):\n\n    def setUp(self):\n        self.widget = self.create_widget(OWPredictions)  # type: OWPredictions\n        self.iris = Table(\"iris\")\n        self.housing = Table(\"housing\")\n\n    def test_rowCount_from_model(self):\n        \"\"\"Don't crash if the bottom row is visible\"\"\"\n        self.send_signal(self.widget.Inputs.data, self.iris[:5])\n        self.widget.dataview.sizeHintForColumn(0)\n\n    def test_nan_target_input(self):\n        data = self.iris[::10].copy()\n        with data.unlocked():\n            data.Y[1] = np.nan\n        yvec, _ = data.get_column_view(data.domain.class_var)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.send_signal(self.widget.Inputs.predictors, ConstantLearner()(data), 1)\n        pred = self.get_output(self.widget.Outputs.predictions)\n        self.assertIsInstance(pred, Table)\n        np.testing.assert_array_equal(\n            yvec, pred.get_column_view(data.domain.class_var)[0])\n\n        evres = self.get_output(self.widget.Outputs.evaluation_results)\n        self.assertIsInstance(evres, Results)\n        self.assertIsInstance(evres.data, Table)\n        ev_yvec, _ = evres.data.get_column_view(data.domain.class_var)\n\n        self.assertTrue(np.all(~np.isnan(ev_yvec)))\n        self.assertTrue(np.all(~np.isnan(evres.actual)))\n\n        with data.unlocked():\n            data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, data)\n        evres = self.get_output(self.widget.Outputs.evaluation_results)\n        self.assertEqual(len(evres.data), 0)\n\n    def test_no_values_target(self):\n        train = Table(\"titanic\")\n        model = ConstantLearner()(train)\n        self.send_signal(self.widget.Inputs.predictors, model)\n        domain = Domain([DiscreteVariable(\"status\", values=(\"first\", \"third\")),\n                         DiscreteVariable(\"age\", values=(\"adult\", \"child\")),\n                         DiscreteVariable(\"sex\", values=(\"female\", \"male\"))],\n                        [DiscreteVariable(\"survived\", values=())])\n        test = Table(domain, np.array([[0, 0, 1], [0, 1, 0], [1, 0, 0]]),\n                     np.full((3, 1), np.nan))\n        self.send_signal(self.widget.Inputs.data, test)\n        pred = self.get_output(self.widget.Outputs.predictions)\n        self.assertEqual(len(pred), len(test))\n\n        results = self.get_output(self.widget.Outputs.evaluation_results)\n\n        cm_widget = self.create_widget(OWConfusionMatrix)\n        self.send_signal(cm_widget.Inputs.evaluation_results, results,\n                         widget=cm_widget)\n\n        ra_widget = self.create_widget(OWROCAnalysis)\n        self.send_signal(ra_widget.Inputs.evaluation_results, results,\n                         widget=ra_widget)\n\n        lc_widget = self.create_widget(OWLiftCurve)\n        self.send_signal(lc_widget.Inputs.evaluation_results, results,\n                         widget=lc_widget)\n\n        cp_widget = self.create_widget(OWCalibrationPlot)\n        self.send_signal(cp_widget.Inputs.evaluation_results, results,\n                         widget=cp_widget)\n\n    def test_mismatching_targets(self):\n        warning = self.widget.Warning\n\n        maj_iris = ConstantLearner()(self.iris)\n        dom = self.iris.domain\n        iris3 = self.iris.transform(Domain(dom[:3], dom[3]))\n        maj_iris3 = ConstantLearner()(iris3)\n\n        self.send_signal(self.widget.Inputs.predictors, maj_iris, 1)\n        self.send_signal(self.widget.Inputs.predictors, maj_iris3, 2)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None, 2)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, maj_iris3, 2)\n        self.assertTrue(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n    def test_no_class_on_test(self):\n        \"\"\"Allow test data with no class\"\"\"\n        titanic = Table(\"titanic\")\n        majority_titanic = ConstantLearner()(titanic)\n\n        no_class = titanic.transform(Domain(titanic.domain.attributes, None))\n        self.send_signal(self.widget.Inputs.predictors, majority_titanic, 1)\n        self.send_signal(self.widget.Inputs.data, no_class)\n        out = self.get_output(self.widget.Outputs.predictions)\n        np.testing.assert_allclose(out.get_column_view(\"constant\")[0], 0)\n\n    def test_bad_data(self):\n        \"\"\"\n        Firstly it creates predictions with TreeLearner. Then sends predictions and\n        different data with different domain to Predictions widget. Those different\n        data and domain are similar to original data and domain but they have three\n        different target values instead of two.\n        GH-2129\n        \"\"\"\n        filestr1 = \"\"\"\\\n        age\\tsex\\tsurvived\n        d\\td\\td\n        \\t\\tclass\n        adult\\tmale\\tyes\n        adult\\tfemale\\tno\n        child\\tmale\\tyes\n        child\\tfemale\\tyes\n        \"\"\"\n        file1 = io.StringIO(filestr1)\n        table = TabReader(file1).read()\n        learner = TreeLearner()\n        tree = learner(table)\n\n        filestr2 = \"\"\"\\\n        age\\tsex\\tsurvived\n        d\\td\\td\n        \\t\\tclass\n        adult\\tmale\\tyes\n        adult\\tfemale\\tno\n        child\\tmale\\tyes\n        child\\tfemale\\tunknown\n        \"\"\"\n        file2 = io.StringIO(filestr2)\n        bad_table = TabReader(file2).read()\n\n        self.send_signal(self.widget.Inputs.predictors, tree, 1)\n\n        with excepthook_catch():\n            self.send_signal(self.widget.Inputs.data, bad_table)\n\n    def test_continuous_class(self):\n        data = Table(\"housing\")\n        cl_data = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.predictors, cl_data, 1)\n        self.send_signal(self.widget.Inputs.data, data)\n\n    def test_changed_class_var(self):\n        def set_input(data, model):\n            self.send_signals([\n                (self.widget.Inputs.data, data),\n                (self.widget.Inputs.predictors, model)\n            ])\n        iris = self.iris\n        learner = ConstantLearner()\n        heart_disease = Table(\"heart_disease\")\n        # catch exceptions in item delegates etc. during switching inputs\n        with excepthook_catch():\n            set_input(iris[:5], learner(iris))\n            set_input(Table(\"housing\"), None)\n            set_input(heart_disease[:5], learner(heart_disease))\n\n    def test_predictor_fails(self):\n        titanic = Table(\"titanic\")\n        failing_model = ConstantLearner()(titanic)\n        failing_model.predict = Mock(side_effect=ValueError(\"foo\"))\n        self.send_signal(self.widget.Inputs.predictors, failing_model, 1)\n        self.send_signal(self.widget.Inputs.data, titanic)\n\n        model2 = ConstantLearner()(titanic)\n        self.send_signal(self.widget.Inputs.predictors, model2, 2)\n\n    def test_sort_matching(self):\n        def get_items_order(model):\n            return model.mapToSourceRows(np.arange(model.rowCount()))\n\n        w = self.widget\n\n        titanic = Table(\"titanic\")\n        majority_titanic = LogisticRegressionLearner()(titanic)\n        self.send_signal(self.widget.Inputs.predictors, majority_titanic)\n        self.send_signal(self.widget.Inputs.data, titanic)\n\n        pred_model = w.predictionsview.model()\n        data_model = w.dataview.model()\n        n = pred_model.rowCount()\n\n        # no sort\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, np.arange(n))\n        np.testing.assert_array_equal(data_order, np.arange(n))\n\n        # sort by first column in prediction table\n        pred_model.sort(0)\n        w.predictionsview.horizontalHeader().sectionClicked.emit(0)\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, data_order)\n\n        # sort by second column in data table\n        data_model.sort(1)\n        w.dataview.horizontalHeader().sectionClicked.emit(0)\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, data_order)\n\n        # restore order\n        w.reset_button.click()\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, np.arange(n))\n        np.testing.assert_array_equal(data_order, np.arange(n))\n\n    def test_sort_predictions(self):\n        \"\"\"\n        Test whether sorting of probabilities by FilterSortProxy is correct.\n        \"\"\"\n        def get_items_order(model):\n            return model.mapToSourceRows(np.arange(model.rowCount()))\n\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        _, log_reg_probs = log_reg_iris(self.iris, Model.ValueProbs)\n        # here I assume that classes are in order 0, 1, 2 in widget\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        widget_order = get_items_order(pred_model)\n\n        # correct order first sort by probs[:,0] then probs[:,1], ...\n        keys = tuple(\n            log_reg_probs[:, i] for i in range(\n                log_reg_probs.shape[1] - 1, -1, -1))\n        sort_order = np.lexsort(keys)\n        np.testing.assert_array_equal(widget_order, sort_order)\n\n    def test_reset_no_data(self):\n        \"\"\"\n        Check no error when resetting the view without model and data\n        \"\"\"\n        self.widget.reset_button.click()\n\n    def test_colors_same_domain(self):\n        \"\"\"\n        Test whether the color selection for values is correct.\n        \"\"\"\n        # pylint: disable=protected-access\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        # case 1: one same model\n        predictor_iris = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(\n            colors, self.iris.domain.class_var.colors)\n\n        # case 2: two same models\n        predictor_iris1 = ConstantLearner()(self.iris)\n        predictor_iris2 = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        # assume that colors have same order since values have same order\n        np.testing.assert_array_equal(\n            colors, self.iris.domain.class_var.colors)\n\n        # case 3: two same models - changed color order\n        idom = self.iris.domain\n        dom = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom.class_var.colors = dom.class_var.colors[::-1]\n        iris2 = self.iris.transform(dom)\n\n        predictor_iris1 = ConstantLearner()(iris2)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris2.domain.class_var.colors)\n\n    def test_colors_diff_domain(self):\n        \"\"\"\n        Test whether the color selection for values is correct.\n        \"\"\"\n        # pylint: disable=protected-access\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        # case 1: two domains one subset other\n        idom = self.iris.domain\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[:2])\n        )\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n        # case 2: two domains one subset other - different color order\n        idom = self.iris.domain\n        colors = idom.class_var.colors[::-1]\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[:2])\n        )\n        dom1.class_var.colors = colors\n        dom2.class_var.colors = colors[:2]\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n        # case 3: domain color, values miss-match - use default colors\n        idom = self.iris.domain\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom1.class_var.colors = dom1.class_var.colors[::-1]\n        iris1 = self.iris.transform(dom1)\n        iris2 = self.iris.transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, LimitedDiscretePalette(3).palette)\n\n        # case 4: two domains different values order, matching colors\n        idom = self.iris.domain\n        # this way we know that default colors are not used\n        colors = LimitedDiscretePalette(5).palette[2:]\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[::-1])\n        )\n        dom1.class_var.colors = colors\n        dom2.class_var.colors = colors[::-1]  # colors mixed same than values\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n    def test_colors_continuous(self):\n        \"\"\"\n        When only continuous variables in predictor no color should be selected\n        we do not work with classes.\n        When we add one classifier there should be colors.\n        \"\"\"\n        # pylint: disable=protected-access\n        data = Table(\"housing\")\n        cl_data = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.predictors, cl_data)\n        self.send_signal(self.widget.Inputs.data, data)\n        colors = self.widget._get_colors()\n        self.assertListEqual([], colors)\n\n        predictor_iris = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris, 1)\n        colors = self.widget._get_colors()\n        self.assertEqual(3, len(colors))\n\n        self.widget.send_report()  # just a quick check that it doesn't crash\n\n    def test_unique_output_domain(self):\n        data = possible_duplicate_table('constant')\n        predictor = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.send_signal(self.widget.Inputs.predictors, predictor)\n\n        output = self.get_output(self.widget.Outputs.predictions)\n        self.assertEqual(output.domain.metas[0].name, 'constant (1)')\n\n    def test_select(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        self.widget.predictionsview.selectRow(1)\n        sel = {(index.row(), index.column())\n               for index in self.widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(1, col) for col in range(5)})\n\n    def test_select_data_first(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        self.widget.predictionsview.selectRow(1)\n        sel = {(index.row(), index.column())\n               for index in self.widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(1, col) for col in range(5)})\n\n    def test_selection_in_setting(self):\n        widget = self.create_widget(OWPredictions,\n                                    stored_settings={\"selection\": [1, 3, 4]})\n        self.send_signal(widget.Inputs.data, self.iris)\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(widget.Inputs.predictors, log_reg_iris)\n        sel = {(index.row(), index.column())\n               for index in widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(row, col)\n                               for row in [1, 3, 4] for col in range(5)})\n        out = self.get_output(widget.Outputs.predictions)\n        exp = self.iris[np.array([1, 3, 4])]\n        np.testing.assert_equal(out.X, exp.X)\n\n    def test_unregister_prediction_model(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.widget.selection_store.unregister = Mock()\n        prev_model = self.widget.predictionsview.model()\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.widget.selection_store.unregister.called_with(prev_model)\n\n    def test_multi_inputs(self):\n        w = self.widget\n        data = self.iris[::5].copy()\n\n        p1 = ConstantLearner()(data)\n        p1.name = \"P1\"\n        p2 = ConstantLearner()(data)\n        p2.name = \"P2\"\n        p3 = ConstantLearner()(data)\n        p3.name = \"P3\"\n        for i, p in enumerate([p1, p2, p3], 1):\n            self.send_signal(w.Inputs.predictors, p, i)\n        self.send_signal(w.Inputs.data, data)\n\n        def check_evres(expected):\n            out = self.get_output(w.Outputs.evaluation_results)\n            self.assertSequenceEqual(out.learner_names, expected)\n\n        check_evres([\"P1\", \"P2\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, None, 2)\n        check_evres([\"P1\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, p2, 2)\n        check_evres([\"P1\", \"P2\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors,\n                         w.Inputs.predictors.closing_sentinel, 2)\n        check_evres([\"P1\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, p2, 2)\n        check_evres([\"P1\", \"P3\", \"P2\"])\n\n    def test_missing_target_cls(self):\n        mask = np.zeros(len(self.iris), dtype=bool)\n        mask[::2] = True\n        train_data = self.iris[~mask]\n        predict_data = self.iris[mask]\n        model = LogisticRegressionLearner()(train_data)\n\n        self.send_signal(self.widget.Inputs.predictors, model)\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[0] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n    def test_missing_target_reg(self):\n        mask = np.zeros(len(self.housing), dtype=bool)\n        mask[::2] = True\n        train_data = self.housing[~mask]\n        predict_data = self.housing[mask]\n        model = LinearRegressionLearner()(train_data)\n\n        self.send_signal(self.widget.Inputs.predictors, model)\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[0] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 552,
                            "end_lineno": 578,
                            "parent": {
                                "file_name": "Orange/widgets/evaluate/tests/test_owpredictions.py",
                                "type": "class",
                                "name": "TestOWPredictions",
                                "content": "class TestOWPredictions(WidgetTest):\n\n    def setUp(self):\n        self.widget = self.create_widget(OWPredictions)  # type: OWPredictions\n        self.iris = Table(\"iris\")\n        self.housing = Table(\"housing\")\n\n    def test_rowCount_from_model(self):\n        \"\"\"Don't crash if the bottom row is visible\"\"\"\n        self.send_signal(self.widget.Inputs.data, self.iris[:5])\n        self.widget.dataview.sizeHintForColumn(0)\n\n    def test_nan_target_input(self):\n        data = self.iris[::10].copy()\n        with data.unlocked():\n            data.Y[1] = np.nan\n        yvec, _ = data.get_column_view(data.domain.class_var)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.send_signal(self.widget.Inputs.predictors, ConstantLearner()(data), 1)\n        pred = self.get_output(self.widget.Outputs.predictions)\n        self.assertIsInstance(pred, Table)\n        np.testing.assert_array_equal(\n            yvec, pred.get_column_view(data.domain.class_var)[0])\n\n        evres = self.get_output(self.widget.Outputs.evaluation_results)\n        self.assertIsInstance(evres, Results)\n        self.assertIsInstance(evres.data, Table)\n        ev_yvec, _ = evres.data.get_column_view(data.domain.class_var)\n\n        self.assertTrue(np.all(~np.isnan(ev_yvec)))\n        self.assertTrue(np.all(~np.isnan(evres.actual)))\n\n        with data.unlocked():\n            data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, data)\n        evres = self.get_output(self.widget.Outputs.evaluation_results)\n        self.assertEqual(len(evres.data), 0)\n\n    def test_no_values_target(self):\n        train = Table(\"titanic\")\n        model = ConstantLearner()(train)\n        self.send_signal(self.widget.Inputs.predictors, model)\n        domain = Domain([DiscreteVariable(\"status\", values=(\"first\", \"third\")),\n                         DiscreteVariable(\"age\", values=(\"adult\", \"child\")),\n                         DiscreteVariable(\"sex\", values=(\"female\", \"male\"))],\n                        [DiscreteVariable(\"survived\", values=())])\n        test = Table(domain, np.array([[0, 0, 1], [0, 1, 0], [1, 0, 0]]),\n                     np.full((3, 1), np.nan))\n        self.send_signal(self.widget.Inputs.data, test)\n        pred = self.get_output(self.widget.Outputs.predictions)\n        self.assertEqual(len(pred), len(test))\n\n        results = self.get_output(self.widget.Outputs.evaluation_results)\n\n        cm_widget = self.create_widget(OWConfusionMatrix)\n        self.send_signal(cm_widget.Inputs.evaluation_results, results,\n                         widget=cm_widget)\n\n        ra_widget = self.create_widget(OWROCAnalysis)\n        self.send_signal(ra_widget.Inputs.evaluation_results, results,\n                         widget=ra_widget)\n\n        lc_widget = self.create_widget(OWLiftCurve)\n        self.send_signal(lc_widget.Inputs.evaluation_results, results,\n                         widget=lc_widget)\n\n        cp_widget = self.create_widget(OWCalibrationPlot)\n        self.send_signal(cp_widget.Inputs.evaluation_results, results,\n                         widget=cp_widget)\n\n    def test_mismatching_targets(self):\n        warning = self.widget.Warning\n\n        maj_iris = ConstantLearner()(self.iris)\n        dom = self.iris.domain\n        iris3 = self.iris.transform(Domain(dom[:3], dom[3]))\n        maj_iris3 = ConstantLearner()(iris3)\n\n        self.send_signal(self.widget.Inputs.predictors, maj_iris, 1)\n        self.send_signal(self.widget.Inputs.predictors, maj_iris3, 2)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None, 2)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, maj_iris3, 2)\n        self.assertTrue(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n    def test_no_class_on_test(self):\n        \"\"\"Allow test data with no class\"\"\"\n        titanic = Table(\"titanic\")\n        majority_titanic = ConstantLearner()(titanic)\n\n        no_class = titanic.transform(Domain(titanic.domain.attributes, None))\n        self.send_signal(self.widget.Inputs.predictors, majority_titanic, 1)\n        self.send_signal(self.widget.Inputs.data, no_class)\n        out = self.get_output(self.widget.Outputs.predictions)\n        np.testing.assert_allclose(out.get_column_view(\"constant\")[0], 0)\n\n    def test_bad_data(self):\n        \"\"\"\n        Firstly it creates predictions with TreeLearner. Then sends predictions and\n        different data with different domain to Predictions widget. Those different\n        data and domain are similar to original data and domain but they have three\n        different target values instead of two.\n        GH-2129\n        \"\"\"\n        filestr1 = \"\"\"\\\n        age\\tsex\\tsurvived\n        d\\td\\td\n        \\t\\tclass\n        adult\\tmale\\tyes\n        adult\\tfemale\\tno\n        child\\tmale\\tyes\n        child\\tfemale\\tyes\n        \"\"\"\n        file1 = io.StringIO(filestr1)\n        table = TabReader(file1).read()\n        learner = TreeLearner()\n        tree = learner(table)\n\n        filestr2 = \"\"\"\\\n        age\\tsex\\tsurvived\n        d\\td\\td\n        \\t\\tclass\n        adult\\tmale\\tyes\n        adult\\tfemale\\tno\n        child\\tmale\\tyes\n        child\\tfemale\\tunknown\n        \"\"\"\n        file2 = io.StringIO(filestr2)\n        bad_table = TabReader(file2).read()\n\n        self.send_signal(self.widget.Inputs.predictors, tree, 1)\n\n        with excepthook_catch():\n            self.send_signal(self.widget.Inputs.data, bad_table)\n\n    def test_continuous_class(self):\n        data = Table(\"housing\")\n        cl_data = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.predictors, cl_data, 1)\n        self.send_signal(self.widget.Inputs.data, data)\n\n    def test_changed_class_var(self):\n        def set_input(data, model):\n            self.send_signals([\n                (self.widget.Inputs.data, data),\n                (self.widget.Inputs.predictors, model)\n            ])\n        iris = self.iris\n        learner = ConstantLearner()\n        heart_disease = Table(\"heart_disease\")\n        # catch exceptions in item delegates etc. during switching inputs\n        with excepthook_catch():\n            set_input(iris[:5], learner(iris))\n            set_input(Table(\"housing\"), None)\n            set_input(heart_disease[:5], learner(heart_disease))\n\n    def test_predictor_fails(self):\n        titanic = Table(\"titanic\")\n        failing_model = ConstantLearner()(titanic)\n        failing_model.predict = Mock(side_effect=ValueError(\"foo\"))\n        self.send_signal(self.widget.Inputs.predictors, failing_model, 1)\n        self.send_signal(self.widget.Inputs.data, titanic)\n\n        model2 = ConstantLearner()(titanic)\n        self.send_signal(self.widget.Inputs.predictors, model2, 2)\n\n    def test_sort_matching(self):\n        def get_items_order(model):\n            return model.mapToSourceRows(np.arange(model.rowCount()))\n\n        w = self.widget\n\n        titanic = Table(\"titanic\")\n        majority_titanic = LogisticRegressionLearner()(titanic)\n        self.send_signal(self.widget.Inputs.predictors, majority_titanic)\n        self.send_signal(self.widget.Inputs.data, titanic)\n\n        pred_model = w.predictionsview.model()\n        data_model = w.dataview.model()\n        n = pred_model.rowCount()\n\n        # no sort\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, np.arange(n))\n        np.testing.assert_array_equal(data_order, np.arange(n))\n\n        # sort by first column in prediction table\n        pred_model.sort(0)\n        w.predictionsview.horizontalHeader().sectionClicked.emit(0)\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, data_order)\n\n        # sort by second column in data table\n        data_model.sort(1)\n        w.dataview.horizontalHeader().sectionClicked.emit(0)\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, data_order)\n\n        # restore order\n        w.reset_button.click()\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, np.arange(n))\n        np.testing.assert_array_equal(data_order, np.arange(n))\n\n    def test_sort_predictions(self):\n        \"\"\"\n        Test whether sorting of probabilities by FilterSortProxy is correct.\n        \"\"\"\n        def get_items_order(model):\n            return model.mapToSourceRows(np.arange(model.rowCount()))\n\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        _, log_reg_probs = log_reg_iris(self.iris, Model.ValueProbs)\n        # here I assume that classes are in order 0, 1, 2 in widget\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        widget_order = get_items_order(pred_model)\n\n        # correct order first sort by probs[:,0] then probs[:,1], ...\n        keys = tuple(\n            log_reg_probs[:, i] for i in range(\n                log_reg_probs.shape[1] - 1, -1, -1))\n        sort_order = np.lexsort(keys)\n        np.testing.assert_array_equal(widget_order, sort_order)\n\n    def test_reset_no_data(self):\n        \"\"\"\n        Check no error when resetting the view without model and data\n        \"\"\"\n        self.widget.reset_button.click()\n\n    def test_colors_same_domain(self):\n        \"\"\"\n        Test whether the color selection for values is correct.\n        \"\"\"\n        # pylint: disable=protected-access\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        # case 1: one same model\n        predictor_iris = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(\n            colors, self.iris.domain.class_var.colors)\n\n        # case 2: two same models\n        predictor_iris1 = ConstantLearner()(self.iris)\n        predictor_iris2 = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        # assume that colors have same order since values have same order\n        np.testing.assert_array_equal(\n            colors, self.iris.domain.class_var.colors)\n\n        # case 3: two same models - changed color order\n        idom = self.iris.domain\n        dom = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom.class_var.colors = dom.class_var.colors[::-1]\n        iris2 = self.iris.transform(dom)\n\n        predictor_iris1 = ConstantLearner()(iris2)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris2.domain.class_var.colors)\n\n    def test_colors_diff_domain(self):\n        \"\"\"\n        Test whether the color selection for values is correct.\n        \"\"\"\n        # pylint: disable=protected-access\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        # case 1: two domains one subset other\n        idom = self.iris.domain\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[:2])\n        )\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n        # case 2: two domains one subset other - different color order\n        idom = self.iris.domain\n        colors = idom.class_var.colors[::-1]\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[:2])\n        )\n        dom1.class_var.colors = colors\n        dom2.class_var.colors = colors[:2]\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n        # case 3: domain color, values miss-match - use default colors\n        idom = self.iris.domain\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom1.class_var.colors = dom1.class_var.colors[::-1]\n        iris1 = self.iris.transform(dom1)\n        iris2 = self.iris.transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, LimitedDiscretePalette(3).palette)\n\n        # case 4: two domains different values order, matching colors\n        idom = self.iris.domain\n        # this way we know that default colors are not used\n        colors = LimitedDiscretePalette(5).palette[2:]\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[::-1])\n        )\n        dom1.class_var.colors = colors\n        dom2.class_var.colors = colors[::-1]  # colors mixed same than values\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n    def test_colors_continuous(self):\n        \"\"\"\n        When only continuous variables in predictor no color should be selected\n        we do not work with classes.\n        When we add one classifier there should be colors.\n        \"\"\"\n        # pylint: disable=protected-access\n        data = Table(\"housing\")\n        cl_data = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.predictors, cl_data)\n        self.send_signal(self.widget.Inputs.data, data)\n        colors = self.widget._get_colors()\n        self.assertListEqual([], colors)\n\n        predictor_iris = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris, 1)\n        colors = self.widget._get_colors()\n        self.assertEqual(3, len(colors))\n\n        self.widget.send_report()  # just a quick check that it doesn't crash\n\n    def test_unique_output_domain(self):\n        data = possible_duplicate_table('constant')\n        predictor = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.send_signal(self.widget.Inputs.predictors, predictor)\n\n        output = self.get_output(self.widget.Outputs.predictions)\n        self.assertEqual(output.domain.metas[0].name, 'constant (1)')\n\n    def test_select(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        self.widget.predictionsview.selectRow(1)\n        sel = {(index.row(), index.column())\n               for index in self.widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(1, col) for col in range(5)})\n\n    def test_select_data_first(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        self.widget.predictionsview.selectRow(1)\n        sel = {(index.row(), index.column())\n               for index in self.widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(1, col) for col in range(5)})\n\n    def test_selection_in_setting(self):\n        widget = self.create_widget(OWPredictions,\n                                    stored_settings={\"selection\": [1, 3, 4]})\n        self.send_signal(widget.Inputs.data, self.iris)\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(widget.Inputs.predictors, log_reg_iris)\n        sel = {(index.row(), index.column())\n               for index in widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(row, col)\n                               for row in [1, 3, 4] for col in range(5)})\n        out = self.get_output(widget.Outputs.predictions)\n        exp = self.iris[np.array([1, 3, 4])]\n        np.testing.assert_equal(out.X, exp.X)\n\n    def test_unregister_prediction_model(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.widget.selection_store.unregister = Mock()\n        prev_model = self.widget.predictionsview.model()\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.widget.selection_store.unregister.called_with(prev_model)\n\n    def test_multi_inputs(self):\n        w = self.widget\n        data = self.iris[::5].copy()\n\n        p1 = ConstantLearner()(data)\n        p1.name = \"P1\"\n        p2 = ConstantLearner()(data)\n        p2.name = \"P2\"\n        p3 = ConstantLearner()(data)\n        p3.name = \"P3\"\n        for i, p in enumerate([p1, p2, p3], 1):\n            self.send_signal(w.Inputs.predictors, p, i)\n        self.send_signal(w.Inputs.data, data)\n\n        def check_evres(expected):\n            out = self.get_output(w.Outputs.evaluation_results)\n            self.assertSequenceEqual(out.learner_names, expected)\n\n        check_evres([\"P1\", \"P2\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, None, 2)\n        check_evres([\"P1\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, p2, 2)\n        check_evres([\"P1\", \"P2\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors,\n                         w.Inputs.predictors.closing_sentinel, 2)\n        check_evres([\"P1\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, p2, 2)\n        check_evres([\"P1\", \"P3\", \"P2\"])\n\n    def test_missing_target_cls(self):\n        mask = np.zeros(len(self.iris), dtype=bool)\n        mask[::2] = True\n        train_data = self.iris[~mask]\n        predict_data = self.iris[mask]\n        model = LogisticRegressionLearner()(train_data)\n\n        self.send_signal(self.widget.Inputs.predictors, model)\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[0] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n    def test_missing_target_reg(self):\n        mask = np.zeros(len(self.housing), dtype=bool)\n        mask[::2] = True\n        train_data = self.housing[~mask]\n        predict_data = self.housing[mask]\n        model = LinearRegressionLearner()(train_data)\n\n        self.send_signal(self.widget.Inputs.predictors, model)\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[0] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 32,
                                "end_lineno": 578,
                                "parent": null
                            }
                        }
                    ],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "8226727eec78794ec1dd7b0e338de7c65ef60474^",
    "new_commit_hash": "8226727eec78794ec1dd7b0e338de7c65ef60474",
    "commit_message": "Predictions: Remove rows with missing target when scoring",
    "commit_date": "2022-01-28T12:32:50+01:00",
    "metadata": {}
}
{
    "repo_name": "orange3",
    "new_commit_hash": "8226727eec78794ec1dd7b0e338de7c65ef60474",
    "test_file_codes": [
        "\"\"\"Tests for OWPredictions\"\"\"\n# pylint: disable=protected-access\nimport io\nimport unittest\nfrom unittest.mock import Mock\n\nimport numpy as np\n\nfrom AnyQt.QtCore import QItemSelectionModel, QItemSelection, Qt\n\nfrom Orange.base import Model\nfrom Orange.classification import LogisticRegressionLearner\nfrom Orange.data.io import TabReader\nfrom Orange.regression import LinearRegressionLearner\nfrom Orange.widgets.tests.base import WidgetTest\nfrom Orange.widgets.evaluate.owpredictions import (\n    OWPredictions, SharedSelectionModel, SharedSelectionStore, DataModel,\n    PredictionsModel)\nfrom Orange.widgets.evaluate.owcalibrationplot import OWCalibrationPlot\nfrom Orange.widgets.evaluate.owconfusionmatrix import OWConfusionMatrix\nfrom Orange.widgets.evaluate.owliftcurve import OWLiftCurve\nfrom Orange.widgets.evaluate.owrocanalysis import OWROCAnalysis\n\nfrom Orange.data import Table, Domain, DiscreteVariable\nfrom Orange.modelling import ConstantLearner, TreeLearner\nfrom Orange.evaluation import Results\nfrom Orange.widgets.tests.utils import excepthook_catch, \\\n    possible_duplicate_table\nfrom Orange.widgets.utils.colorpalettes import LimitedDiscretePalette\n\n\nclass TestOWPredictions(WidgetTest):\n\n    def setUp(self):\n        self.widget = self.create_widget(OWPredictions)  # type: OWPredictions\n        self.iris = Table(\"iris\")\n        self.housing = Table(\"housing\")\n\n    def test_rowCount_from_model(self):\n        \"\"\"Don't crash if the bottom row is visible\"\"\"\n        self.send_signal(self.widget.Inputs.data, self.iris[:5])\n        self.widget.dataview.sizeHintForColumn(0)\n\n    def test_nan_target_input(self):\n        data = self.iris[::10].copy()\n        with data.unlocked():\n            data.Y[1] = np.nan\n        yvec, _ = data.get_column_view(data.domain.class_var)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.send_signal(self.widget.Inputs.predictors, ConstantLearner()(data), 1)\n        pred = self.get_output(self.widget.Outputs.predictions)\n        self.assertIsInstance(pred, Table)\n        np.testing.assert_array_equal(\n            yvec, pred.get_column_view(data.domain.class_var)[0])\n\n        evres = self.get_output(self.widget.Outputs.evaluation_results)\n        self.assertIsInstance(evres, Results)\n        self.assertIsInstance(evres.data, Table)\n        ev_yvec, _ = evres.data.get_column_view(data.domain.class_var)\n\n        self.assertTrue(np.all(~np.isnan(ev_yvec)))\n        self.assertTrue(np.all(~np.isnan(evres.actual)))\n\n        with data.unlocked():\n            data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, data)\n        evres = self.get_output(self.widget.Outputs.evaluation_results)\n        self.assertEqual(len(evres.data), 0)\n\n    def test_no_values_target(self):\n        train = Table(\"titanic\")\n        model = ConstantLearner()(train)\n        self.send_signal(self.widget.Inputs.predictors, model)\n        domain = Domain([DiscreteVariable(\"status\", values=(\"first\", \"third\")),\n                         DiscreteVariable(\"age\", values=(\"adult\", \"child\")),\n                         DiscreteVariable(\"sex\", values=(\"female\", \"male\"))],\n                        [DiscreteVariable(\"survived\", values=())])\n        test = Table(domain, np.array([[0, 0, 1], [0, 1, 0], [1, 0, 0]]),\n                     np.full((3, 1), np.nan))\n        self.send_signal(self.widget.Inputs.data, test)\n        pred = self.get_output(self.widget.Outputs.predictions)\n        self.assertEqual(len(pred), len(test))\n\n        results = self.get_output(self.widget.Outputs.evaluation_results)\n\n        cm_widget = self.create_widget(OWConfusionMatrix)\n        self.send_signal(cm_widget.Inputs.evaluation_results, results,\n                         widget=cm_widget)\n\n        ra_widget = self.create_widget(OWROCAnalysis)\n        self.send_signal(ra_widget.Inputs.evaluation_results, results,\n                         widget=ra_widget)\n\n        lc_widget = self.create_widget(OWLiftCurve)\n        self.send_signal(lc_widget.Inputs.evaluation_results, results,\n                         widget=lc_widget)\n\n        cp_widget = self.create_widget(OWCalibrationPlot)\n        self.send_signal(cp_widget.Inputs.evaluation_results, results,\n                         widget=cp_widget)\n\n    def test_mismatching_targets(self):\n        warning = self.widget.Warning\n\n        maj_iris = ConstantLearner()(self.iris)\n        dom = self.iris.domain\n        iris3 = self.iris.transform(Domain(dom[:3], dom[3]))\n        maj_iris3 = ConstantLearner()(iris3)\n\n        self.send_signal(self.widget.Inputs.predictors, maj_iris, 1)\n        self.send_signal(self.widget.Inputs.predictors, maj_iris3, 2)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.assertTrue(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None, 2)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, maj_iris3, 2)\n        self.assertTrue(warning.wrong_targets.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(warning.wrong_targets.is_shown())\n\n    def test_no_class_on_test(self):\n        \"\"\"Allow test data with no class\"\"\"\n        titanic = Table(\"titanic\")\n        majority_titanic = ConstantLearner()(titanic)\n\n        no_class = titanic.transform(Domain(titanic.domain.attributes, None))\n        self.send_signal(self.widget.Inputs.predictors, majority_titanic, 1)\n        self.send_signal(self.widget.Inputs.data, no_class)\n        out = self.get_output(self.widget.Outputs.predictions)\n        np.testing.assert_allclose(out.get_column_view(\"constant\")[0], 0)\n\n    def test_bad_data(self):\n        \"\"\"\n        Firstly it creates predictions with TreeLearner. Then sends predictions and\n        different data with different domain to Predictions widget. Those different\n        data and domain are similar to original data and domain but they have three\n        different target values instead of two.\n        GH-2129\n        \"\"\"\n        filestr1 = \"\"\"\\\n        age\\tsex\\tsurvived\n        d\\td\\td\n        \\t\\tclass\n        adult\\tmale\\tyes\n        adult\\tfemale\\tno\n        child\\tmale\\tyes\n        child\\tfemale\\tyes\n        \"\"\"\n        file1 = io.StringIO(filestr1)\n        table = TabReader(file1).read()\n        learner = TreeLearner()\n        tree = learner(table)\n\n        filestr2 = \"\"\"\\\n        age\\tsex\\tsurvived\n        d\\td\\td\n        \\t\\tclass\n        adult\\tmale\\tyes\n        adult\\tfemale\\tno\n        child\\tmale\\tyes\n        child\\tfemale\\tunknown\n        \"\"\"\n        file2 = io.StringIO(filestr2)\n        bad_table = TabReader(file2).read()\n\n        self.send_signal(self.widget.Inputs.predictors, tree, 1)\n\n        with excepthook_catch():\n            self.send_signal(self.widget.Inputs.data, bad_table)\n\n    def test_continuous_class(self):\n        data = Table(\"housing\")\n        cl_data = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.predictors, cl_data, 1)\n        self.send_signal(self.widget.Inputs.data, data)\n\n    def test_changed_class_var(self):\n        def set_input(data, model):\n            self.send_signals([\n                (self.widget.Inputs.data, data),\n                (self.widget.Inputs.predictors, model)\n            ])\n        iris = self.iris\n        learner = ConstantLearner()\n        heart_disease = Table(\"heart_disease\")\n        # catch exceptions in item delegates etc. during switching inputs\n        with excepthook_catch():\n            set_input(iris[:5], learner(iris))\n            set_input(Table(\"housing\"), None)\n            set_input(heart_disease[:5], learner(heart_disease))\n\n    def test_predictor_fails(self):\n        titanic = Table(\"titanic\")\n        failing_model = ConstantLearner()(titanic)\n        failing_model.predict = Mock(side_effect=ValueError(\"foo\"))\n        self.send_signal(self.widget.Inputs.predictors, failing_model, 1)\n        self.send_signal(self.widget.Inputs.data, titanic)\n\n        model2 = ConstantLearner()(titanic)\n        self.send_signal(self.widget.Inputs.predictors, model2, 2)\n\n    def test_sort_matching(self):\n        def get_items_order(model):\n            return model.mapToSourceRows(np.arange(model.rowCount()))\n\n        w = self.widget\n\n        titanic = Table(\"titanic\")\n        majority_titanic = LogisticRegressionLearner()(titanic)\n        self.send_signal(self.widget.Inputs.predictors, majority_titanic)\n        self.send_signal(self.widget.Inputs.data, titanic)\n\n        pred_model = w.predictionsview.model()\n        data_model = w.dataview.model()\n        n = pred_model.rowCount()\n\n        # no sort\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, np.arange(n))\n        np.testing.assert_array_equal(data_order, np.arange(n))\n\n        # sort by first column in prediction table\n        pred_model.sort(0)\n        w.predictionsview.horizontalHeader().sectionClicked.emit(0)\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, data_order)\n\n        # sort by second column in data table\n        data_model.sort(1)\n        w.dataview.horizontalHeader().sectionClicked.emit(0)\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, data_order)\n\n        # restore order\n        w.reset_button.click()\n        pred_order = get_items_order(pred_model)\n        data_order = get_items_order(data_model)\n        np.testing.assert_array_equal(pred_order, np.arange(n))\n        np.testing.assert_array_equal(data_order, np.arange(n))\n\n    def test_sort_predictions(self):\n        \"\"\"\n        Test whether sorting of probabilities by FilterSortProxy is correct.\n        \"\"\"\n        def get_items_order(model):\n            return model.mapToSourceRows(np.arange(model.rowCount()))\n\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        _, log_reg_probs = log_reg_iris(self.iris, Model.ValueProbs)\n        # here I assume that classes are in order 0, 1, 2 in widget\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        widget_order = get_items_order(pred_model)\n\n        # correct order first sort by probs[:,0] then probs[:,1], ...\n        keys = tuple(\n            log_reg_probs[:, i] for i in range(\n                log_reg_probs.shape[1] - 1, -1, -1))\n        sort_order = np.lexsort(keys)\n        np.testing.assert_array_equal(widget_order, sort_order)\n\n    def test_reset_no_data(self):\n        \"\"\"\n        Check no error when resetting the view without model and data\n        \"\"\"\n        self.widget.reset_button.click()\n\n    def test_colors_same_domain(self):\n        \"\"\"\n        Test whether the color selection for values is correct.\n        \"\"\"\n        # pylint: disable=protected-access\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        # case 1: one same model\n        predictor_iris = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(\n            colors, self.iris.domain.class_var.colors)\n\n        # case 2: two same models\n        predictor_iris1 = ConstantLearner()(self.iris)\n        predictor_iris2 = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        # assume that colors have same order since values have same order\n        np.testing.assert_array_equal(\n            colors, self.iris.domain.class_var.colors)\n\n        # case 3: two same models - changed color order\n        idom = self.iris.domain\n        dom = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom.class_var.colors = dom.class_var.colors[::-1]\n        iris2 = self.iris.transform(dom)\n\n        predictor_iris1 = ConstantLearner()(iris2)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris2.domain.class_var.colors)\n\n    def test_colors_diff_domain(self):\n        \"\"\"\n        Test whether the color selection for values is correct.\n        \"\"\"\n        # pylint: disable=protected-access\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        # case 1: two domains one subset other\n        idom = self.iris.domain\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[:2])\n        )\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n        # case 2: two domains one subset other - different color order\n        idom = self.iris.domain\n        colors = idom.class_var.colors[::-1]\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[:2])\n        )\n        dom1.class_var.colors = colors\n        dom2.class_var.colors = colors[:2]\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n        # case 3: domain color, values miss-match - use default colors\n        idom = self.iris.domain\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom1.class_var.colors = dom1.class_var.colors[::-1]\n        iris1 = self.iris.transform(dom1)\n        iris2 = self.iris.transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, LimitedDiscretePalette(3).palette)\n\n        # case 4: two domains different values order, matching colors\n        idom = self.iris.domain\n        # this way we know that default colors are not used\n        colors = LimitedDiscretePalette(5).palette[2:]\n        dom1 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values)\n        )\n        dom2 = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[::-1])\n        )\n        dom1.class_var.colors = colors\n        dom2.class_var.colors = colors[::-1]  # colors mixed same than values\n        iris1 = self.iris[:100].transform(dom1)\n        iris2 = self.iris[:100].transform(dom2)\n\n        predictor_iris1 = ConstantLearner()(iris1)\n        predictor_iris2 = ConstantLearner()(iris2)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris1)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris2, 1)\n        colors = self.widget._get_colors()\n        np.testing.assert_array_equal(colors, iris1.domain.class_var.colors)\n\n    def test_colors_continuous(self):\n        \"\"\"\n        When only continuous variables in predictor no color should be selected\n        we do not work with classes.\n        When we add one classifier there should be colors.\n        \"\"\"\n        # pylint: disable=protected-access\n        data = Table(\"housing\")\n        cl_data = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.predictors, cl_data)\n        self.send_signal(self.widget.Inputs.data, data)\n        colors = self.widget._get_colors()\n        self.assertListEqual([], colors)\n\n        predictor_iris = ConstantLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, predictor_iris, 1)\n        colors = self.widget._get_colors()\n        self.assertEqual(3, len(colors))\n\n        self.widget.send_report()  # just a quick check that it doesn't crash\n\n    def test_unique_output_domain(self):\n        data = possible_duplicate_table('constant')\n        predictor = ConstantLearner()(data)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.send_signal(self.widget.Inputs.predictors, predictor)\n\n        output = self.get_output(self.widget.Outputs.predictions)\n        self.assertEqual(output.domain.metas[0].name, 'constant (1)')\n\n    def test_select(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        self.widget.predictionsview.selectRow(1)\n        sel = {(index.row(), index.column())\n               for index in self.widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(1, col) for col in range(5)})\n\n    def test_select_data_first(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n\n        pred_model = self.widget.predictionsview.model()\n        pred_model.sort(0)\n        self.widget.predictionsview.selectRow(1)\n        sel = {(index.row(), index.column())\n               for index in self.widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(1, col) for col in range(5)})\n\n    def test_selection_in_setting(self):\n        widget = self.create_widget(OWPredictions,\n                                    stored_settings={\"selection\": [1, 3, 4]})\n        self.send_signal(widget.Inputs.data, self.iris)\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(widget.Inputs.predictors, log_reg_iris)\n        sel = {(index.row(), index.column())\n               for index in widget.dataview.selectionModel().selectedIndexes()}\n        self.assertEqual(sel, {(row, col)\n                               for row in [1, 3, 4] for col in range(5)})\n        out = self.get_output(widget.Outputs.predictions)\n        exp = self.iris[np.array([1, 3, 4])]\n        np.testing.assert_equal(out.X, exp.X)\n\n    def test_unregister_prediction_model(self):\n        log_reg_iris = LogisticRegressionLearner()(self.iris)\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.widget.selection_store.unregister = Mock()\n        prev_model = self.widget.predictionsview.model()\n        self.send_signal(self.widget.Inputs.predictors, log_reg_iris)\n        self.widget.selection_store.unregister.called_with(prev_model)\n\n    def test_multi_inputs(self):\n        w = self.widget\n        data = self.iris[::5].copy()\n\n        p1 = ConstantLearner()(data)\n        p1.name = \"P1\"\n        p2 = ConstantLearner()(data)\n        p2.name = \"P2\"\n        p3 = ConstantLearner()(data)\n        p3.name = \"P3\"\n        for i, p in enumerate([p1, p2, p3], 1):\n            self.send_signal(w.Inputs.predictors, p, i)\n        self.send_signal(w.Inputs.data, data)\n\n        def check_evres(expected):\n            out = self.get_output(w.Outputs.evaluation_results)\n            self.assertSequenceEqual(out.learner_names, expected)\n\n        check_evres([\"P1\", \"P2\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, None, 2)\n        check_evres([\"P1\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, p2, 2)\n        check_evres([\"P1\", \"P2\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors,\n                         w.Inputs.predictors.closing_sentinel, 2)\n        check_evres([\"P1\", \"P3\"])\n\n        self.send_signal(w.Inputs.predictors, p2, 2)\n        check_evres([\"P1\", \"P3\", \"P2\"])\n\n    def test_missing_target_cls(self):\n        mask = np.zeros(len(self.iris), dtype=bool)\n        mask[::2] = True\n        train_data = self.iris[~mask]\n        predict_data = self.iris[mask]\n        model = LogisticRegressionLearner()(train_data)\n\n        self.send_signal(self.widget.Inputs.predictors, model)\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[0] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n    def test_missing_target_reg(self):\n        mask = np.zeros(len(self.housing), dtype=bool)\n        mask[::2] = True\n        train_data = self.housing[~mask]\n        predict_data = self.housing[mask]\n        model = LinearRegressionLearner()(train_data)\n\n        self.send_signal(self.widget.Inputs.predictors, model)\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[0] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        with predict_data.unlocked():\n            predict_data.Y[:] = np.nan\n        self.send_signal(self.widget.Inputs.data, predict_data)\n        self.assertTrue(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n        self.send_signal(self.widget.Inputs.predictors, None)\n        self.assertFalse(self.widget.Warning.missing_targets.is_shown())\n        self.assertFalse(self.widget.Error.scorer_failed.is_shown())\n\n\nclass SelectionModelTest(unittest.TestCase):\n    def setUp(self):\n        iris = Table(\"iris\")\n\n        self.datamodel1 = DataModel(iris[:5, :2])\n        self.store = SharedSelectionStore(self.datamodel1)\n        self.model1 = SharedSelectionModel(self.store, self.datamodel1, None)\n\n        self.datamodel2 = DataModel(iris[-5:, :3])\n        self.model2 = SharedSelectionModel(self.store, self.datamodel2, None)\n\n    def itsel(self, rows):\n        sel = QItemSelection()\n        for row in rows:\n            index = self.store.model.index(row, 0)\n            sel.select(index, index)\n        return sel\n\n\nclass SharedSelectionStoreTest(SelectionModelTest):\n    def test_registration(self):\n        self.store.unregister(self.model1)\n        self.store.unregister(self.model1)  # should have no effect and no error\n        self.store.unregister(self.model2)\n\n    def test_select_rows(self):\n        emit1 = self.model1.emit_selection_rows_changed = Mock()\n        emit2 = self.model2.emit_selection_rows_changed = Mock()\n\n        store = self.store\n        store.select_rows({1, 2}, QItemSelectionModel.Select)\n        self.assertEqual(store.rows, {1, 2})\n        emit1.assert_called_with([1, 2], [])\n        emit2.assert_called_with([1, 2], [])\n\n        store.select_rows({1, 2, 4}, QItemSelectionModel.Select)\n        self.assertEqual(store.rows, {1, 2, 4})\n        emit1.assert_called_with([4], [])\n        emit2.assert_called_with([4], [])\n\n        store.select_rows({3, 4}, QItemSelectionModel.Toggle)\n        self.assertEqual(store.rows, {1, 2, 3})\n        emit1.assert_called_with([3], [4])\n        emit2.assert_called_with([3], [4])\n\n        store.select_rows({0, 2}, QItemSelectionModel.Deselect)\n        self.assertEqual(store.rows, {1, 3})\n        emit1.assert_called_with([], [2])\n        emit2.assert_called_with([], [2])\n\n        store.select_rows({2, 3, 4}, QItemSelectionModel.ClearAndSelect)\n        self.assertEqual(store.rows, {2, 3, 4})\n        emit1.assert_called_with([2, 4], [1])\n        emit2.assert_called_with([2, 4], [1])\n\n        store.select_rows({2, 3, 4}, QItemSelectionModel.Clear)\n        self.assertEqual(store.rows, set())\n        emit1.assert_called_with([], [2, 3, 4])\n        emit2.assert_called_with([], [2, 3, 4])\n\n        store.select_rows({2, 3, 4}, QItemSelectionModel.ClearAndSelect)\n        emit1.reset_mock()\n        emit2.reset_mock()\n        store.select_rows({2, 4}, QItemSelectionModel.Select)\n        emit1.assert_not_called()\n        emit2.assert_not_called()\n\n    def test_select_maps_from_proxy(self):\n        store = self.store\n        store.select_rows = Mock()\n\n        # Map QItemSelection\n        store.model.setSortIndices(np.array([4, 0, 1, 2, 3]))\n        store.select(self.itsel([1, 2]), QItemSelectionModel.Select)\n        store.select_rows.assert_called_with({0, 1}, QItemSelectionModel.Select)\n\n        # Map QModelIndex\n        store.model.setSortIndices(np.array([4, 0, 1, 2, 3]))\n        store.select(store.model.index(0, 0), QItemSelectionModel.Select)\n        store.select_rows.assert_called_with({4}, QItemSelectionModel.Select)\n\n        # Map empty selection\n        store.select(self.itsel([]), QItemSelectionModel.Select)\n        store.select_rows.assert_called_with(set(), QItemSelectionModel.Select)\n\n    def test_clear(self):\n        store = self.store\n\n        store.select_rows({1, 2, 4}, QItemSelectionModel.Select)\n        self.assertEqual(store.rows, {1, 2, 4})\n\n        emit1 = self.model1.emit_selection_rows_changed = Mock()\n        emit2 = self.model2.emit_selection_rows_changed = Mock()\n\n        store.clear_selection()\n        self.assertEqual(store.rows, set())\n        emit1.assert_called_with([], [1, 2, 4])\n        emit2.assert_called_with([], [1, 2, 4])\n\n    def test_reset(self):\n        store = self.store\n\n        store.select_rows({1, 2, 4}, QItemSelectionModel.Select)\n        self.assertEqual(store.rows, {1, 2, 4})\n\n        emit1 = self.model1.emit_selection_rows_changed = Mock()\n        emit2 = self.model2.emit_selection_rows_changed = Mock()\n\n        store.reset()\n        self.assertEqual(store.rows, set())\n        emit1.assert_not_called()\n        emit2.assert_not_called()\n\n    def test_emit_changed_maps_to_proxy(self):\n        store = self.store\n        emit1 = self.model1.emit_selection_rows_changed = Mock()\n        self.model2.emit_selection_rows_changed = Mock()\n\n        def assert_called(exp_selected, exp_deselected):\n            # pylint: disable=unsubscriptable-object\n            selected, deselected = emit1.call_args[0]\n            self.assertEqual(list(selected), exp_selected)\n            self.assertEqual(list(deselected), exp_deselected)\n\n\n        store.model.setSortIndices([4, 0, 1, 2, 3])\n        store.select_rows({3, 4}, QItemSelectionModel.Select)\n        assert_called([4, 0], [])\n\n        store.model.setSortIndices(None)\n        store.select_rows({4}, QItemSelectionModel.Deselect)\n        assert_called([], [4])\n\n        store.model.setSortIndices([1, 0, 3, 4, 2])\n        store.model.setSortIndices(None)\n        store.select_rows({2, 3}, QItemSelectionModel.Deselect)\n        assert_called([], [3])\n\n\nclass SharedSelectionModelTest(SelectionModelTest):\n    def test_select(self):\n        self.store.select = Mock()\n        sel = self.itsel({1, 2})\n        self.model1.select(sel, QItemSelectionModel.Deselect)\n        self.store.select.assert_called_with(sel, QItemSelectionModel.Deselect)\n\n    def test_selection_from_rows(self):\n        sel = self.model1.selection_from_rows({1, 2})\n        self.assertEqual(len(sel), 2)\n        ind1 = sel[0]\n        self.assertIs(ind1.model(), self.model1.model())\n        self.assertEqual(ind1.left(), 0)\n        self.assertEqual(ind1.right(), self.model1.model().columnCount() - 1)\n        self.assertEqual(ind1.top(), 1)\n        self.assertEqual(ind1.bottom(), 1)\n\n    def test_emit_selection_rows_changed(self):\n        m1 = Mock()\n        m2 = Mock()\n        self.model1.selectionChanged.connect(m1)\n        self.model2.selectionChanged.connect(m2)\n        self.model1.model().setSortIndices(np.array([1, 0, 2, 4, 3]))\n        self.model1.select(self.itsel({1, 3}), QItemSelectionModel.Select)\n        self.assertEqual(self.store.rows, {0, 4})\n\n        for model, m in zip((self.model1, self.model2), (m1, m2)):\n            sel = m.call_args[0][0]\n            self.assertEqual(len(sel), 2)\n            for ind, row in zip(sel, (1, 3)):\n                self.assertIs(ind.model(), model.model())\n                self.assertEqual(ind.left(), 0)\n                self.assertEqual(ind.right(), model.model().columnCount() - 1)\n                self.assertEqual(ind.top(), row)\n                self.assertEqual(ind.bottom(), row)\n\n    def test_methods(self):\n        def rowcol(sel):\n            return {(index.row(), index.column()) for index in sel}\n\n        self.model1.model().setSortIndices(np.array([1, 0, 2, 4, 3]))\n        self.model2.model().setSortIndices(np.array([1, 0, 2, 4, 3]))\n\n        self.assertFalse(self.model1.hasSelection())\n        self.assertFalse(self.model1.isColumnSelected(1))\n        self.assertFalse(self.model1.isRowSelected(1))\n        self.assertEqual(self.model1.selectedColumns(1), [])\n        self.assertEqual(self.model1.selectedRows(1), [])\n        self.assertEqual(self.model1.selectedIndexes(), [])\n\n        self.model1.select(self.itsel({1, 3}), QItemSelectionModel.Select)\n\n        self.assertEqual(rowcol(self.model1.selection().indexes()),\n                         {(1, 0), (1, 1), (3, 0), (3, 1)})\n        self.assertEqual(rowcol(self.model2.selection().indexes()),\n                         {(1, 0), (1, 1), (1, 2), (3, 0), (3, 1), (3, 2)})\n\n        self.assertTrue(self.model1.hasSelection())\n        self.assertFalse(self.model1.isColumnSelected(1))\n        self.assertTrue(self.model1.isRowSelected(1))\n        self.assertFalse(self.model1.isRowSelected(2))\n        self.assertEqual(self.model1.selectedColumns(1), [])\n        self.assertEqual(rowcol(self.model1.selectedRows(1)), {(1, 1), (3, 1)})\n        self.assertEqual(rowcol(self.model1.selectedIndexes()),\n                         {(1, 0), (1, 1), (3, 0), (3, 1)})\n\n        self.model1.select(self.itsel({0, 1, 2, 4}), QItemSelectionModel.Select)\n        self.assertTrue(self.model1.isColumnSelected(1))\n        self.assertEqual(rowcol(self.model1.selectedColumns(1)),\n                         {(1, 0), (1, 1)})\n\n        self.model1.clearSelection()\n        self.assertFalse(self.model1.hasSelection())\n        self.assertFalse(self.model1.isColumnSelected(1))\n        self.assertEqual(self.model1.selectedColumns(1), [])\n        self.assertEqual(self.model1.selectedRows(1), [])\n        self.assertEqual(self.model1.selectedIndexes(), [])\n\n        self.model1.select(self.itsel({0, 1, 2, 3, 4}),\n                           QItemSelectionModel.Select)\n        self.model1.reset()\n        self.assertFalse(self.model1.hasSelection())\n        self.assertFalse(self.model1.isColumnSelected(1))\n        self.assertEqual(self.model1.selectedColumns(1), [])\n        self.assertEqual(self.model1.selectedRows(1), [])\n        self.assertEqual(self.model1.selectedIndexes(), [])\n\n\nclass PredictionsModelTest(unittest.TestCase):\n    @classmethod\n    def setUpClass(cls) -> None:\n        cls.values = np.array([[0, 1, 1, 2, 0], [0, 0, 0, 1, 0]], dtype=float)\n        cls.probs = [np.array([[80, 10, 10],\n                               [30, 70, 0],\n                               [15, 80, 5],\n                               [0,  10, 90],\n                               [55, 40, 5]]) / 100,\n                     np.array([[80, 0, 20],\n                               [90, 5, 5],\n                               [70, 10, 20],\n                               [10, 60, 30],\n                               [50, 25, 25]]) / 100]\n        cls.no_probs = [np.zeros((5, 0)), np.zeros((5, 0))]\n\n    def test_model_classification(self):\n        model = PredictionsModel(self.values, self.probs)\n        self.assertEqual(model.rowCount(), 5)\n        self.assertEqual(model.columnCount(), 2)\n\n        val, prob = model.data(model.index(0, 1))\n        self.assertEqual(val, 0)\n        np.testing.assert_equal(prob, [0.8, 0, 0.2])\n\n        val, prob = model.data(model.index(3, 1))\n        self.assertEqual(val, 1)\n        np.testing.assert_equal(prob, [0.1, 0.6, 0.3])\n\n    def test_model_regression(self):\n        model = PredictionsModel(self.values, self.no_probs)\n        self.assertEqual(model.rowCount(), 5)\n        self.assertEqual(model.columnCount(), 2)\n\n        val, prob = model.data(model.index(0, 1))\n        self.assertEqual(val, 0)\n        np.testing.assert_equal(prob, [])\n\n        val, prob = model.data(model.index(3, 1))\n        self.assertEqual(val, 1)\n        np.testing.assert_equal(prob, [])\n\n    def test_model_header(self):\n        model = PredictionsModel(self.values, self.probs)\n        self.assertIsNone(model.headerData(0, Qt.Horizontal))\n        self.assertEqual(model.headerData(3, Qt.Vertical), \"4\")\n\n        model = PredictionsModel(self.values, self.probs, [\"a\", \"b\"])\n        self.assertEqual(model.headerData(0, Qt.Horizontal), \"a\")\n        self.assertEqual(model.headerData(1, Qt.Horizontal), \"b\")\n        self.assertEqual(model.headerData(3, Qt.Vertical), \"4\")\n\n        model = PredictionsModel(self.values, self.probs, [\"a\"])\n        self.assertEqual(model.headerData(0, Qt.Horizontal), \"a\")\n        self.assertIsNone(model.headerData(1, Qt.Horizontal))\n        self.assertEqual(model.headerData(3, Qt.Vertical), \"4\")\n\n    def test_model_empty(self):\n        model = PredictionsModel()\n        self.assertEqual(model.rowCount(), 0)\n        self.assertEqual(model.columnCount(), 0)\n        self.assertIsNone(model.headerData(1, Qt.Horizontal))\n\n    def test_sorting_classification(self):\n        model = PredictionsModel(self.values, self.probs)\n\n        val, prob = model.data(model.index(0, 1))\n        self.assertEqual(val, 0)\n        np.testing.assert_equal(prob, [0.8, 0, 0.2])\n\n        val, prob = model.data(model.index(3, 1))\n        self.assertEqual(val, 1)\n        np.testing.assert_equal(prob, [0.1, 0.6, 0.3])\n\n        model.setProbInd([2])\n        model.sort(0, Qt.DescendingOrder)\n        val, prob = model.data(model.index(0, 0))\n        self.assertEqual(val, 2)\n        np.testing.assert_equal(prob, [0, 0.1, 0.9])\n        val, prob = model.data(model.index(0, 1))\n        self.assertEqual(val, 1)\n        np.testing.assert_equal(prob, [0.1, 0.6, 0.3])\n\n        model.setProbInd([2])\n        model.sort(1, Qt.AscendingOrder)\n        val, prob = model.data(model.index(0, 1))\n        self.assertEqual(val, 0)\n        np.testing.assert_equal(prob, [0.9, 0.05, 0.05])\n        val, prob = model.data(model.index(0, 0))\n        self.assertEqual(val, 1)\n        np.testing.assert_equal(prob, [0.3, 0.7, 0])\n\n        model.setProbInd([1, 0])\n        model.sort(0, Qt.AscendingOrder)\n        np.testing.assert_equal(model.data(model.index(0, 0))[1], [0, .1, .9])\n        np.testing.assert_equal(model.data(model.index(1, 0))[1], [0.8, .1, .1])\n\n        model.setProbInd([1, 2])\n        model.sort(0, Qt.AscendingOrder)\n        np.testing.assert_equal(model.data(model.index(0, 0))[1], [0.8, .1, .1])\n        np.testing.assert_equal(model.data(model.index(1, 0))[1], [0, .1, .9])\n\n        model.setProbInd([])\n        model.sort(0, Qt.AscendingOrder)\n        self.assertEqual([model.data(model.index(i, 0))[0]\n                          for i in range(model.rowCount())], [0, 0, 1, 1, 2])\n\n        model.setProbInd([])\n        model.sort(0, Qt.DescendingOrder)\n        self.assertEqual([model.data(model.index(i, 0))[0]\n                          for i in range(model.rowCount())], [2, 1, 1, 0, 0])\n\n    def test_sorting_regression(self):\n        model = PredictionsModel(self.values, self.no_probs)\n\n        self.assertEqual(model.data(model.index(0, 1))[0], 0)\n        self.assertEqual(model.data(model.index(3, 1))[0], 1)\n\n        model.setProbInd([2])\n        model.sort(0, Qt.AscendingOrder)\n        self.assertEqual([model.data(model.index(i, 0))[0]\n                          for i in range(model.rowCount())], [0, 0, 1, 1, 2])\n\n        model.setProbInd([])\n        model.sort(0, Qt.DescendingOrder)\n        self.assertEqual([model.data(model.index(i, 0))[0]\n                          for i in range(model.rowCount())], [2, 1, 1, 0, 0])\n\n        model.setProbInd(None)\n        model.sort(0, Qt.AscendingOrder)\n        self.assertEqual([model.data(model.index(i, 0))[0]\n                          for i in range(model.rowCount())], [0, 0, 1, 1, 2])\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
    ],
    "test_file_names": [
        "test_1.py"
    ],
    "setup_res_code": 0,
    "setup_res_stdout": "Starting Orange installation attempts...\nAttempting installation with Python 3.7...\nrunning build_ext\nCompiling Orange/classification/_tree_scorers.pyx because it changed.\nCompiling Orange/data/_contingency.pyx because it changed.\nCompiling Orange/data/_io.pyx because it changed.\nCompiling Orange/data/_valuecount.pyx because it changed.\nCompiling Orange/data/_variable.pyx because it changed.\nCompiling Orange/distance/_distance.pyx because it changed.\nCompiling Orange/preprocess/_discretize.pyx because it changed.\nCompiling Orange/preprocess/_relieff.pyx because it changed.\nCompiling Orange/projection/_som.pyx because it changed.\n[1/9] Cythonizing Orange/classification/_tree_scorers.pyx\n[2/9] Cythonizing Orange/data/_contingency.pyx\n[3/9] Cythonizing Orange/data/_io.pyx\n[4/9] Cythonizing Orange/data/_valuecount.pyx\n[5/9] Cythonizing Orange/data/_variable.pyx\n[6/9] Cythonizing Orange/distance/_distance.pyx\n[7/9] Cythonizing Orange/preprocess/_discretize.pyx\n[8/9] Cythonizing Orange/preprocess/_relieff.pyx\n[9/9] Cythonizing Orange/projection/_som.pyx\nbuilding 'Orange.classification._tree_scorers' extension\ncreating build\ncreating build/temp.linux-x86_64-3.7\ncreating build/temp.linux-x86_64-3.7/Orange\ncreating build/temp.linux-x86_64-3.7/Orange/classification\ncc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c Orange/classification/_tree_scorers.c -o build/temp.linux-x86_64-3.7/Orange/classification/_tree_scorers.o\ncreating build/lib.linux-x86_64-3.7\ncreating build/lib.linux-x86_64-3.7/Orange\ncreating build/lib.linux-x86_64-3.7/Orange/classification\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/classification/_tree_scorers.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o build/lib.linux-x86_64-3.7/Orange/classification/_tree_scorers.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.data._contingency' extension\ncreating build/temp.linux-x86_64-3.7/Orange/data\ncc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c Orange/data/_contingency.c -o build/temp.linux-x86_64-3.7/Orange/data/_contingency.o\ncreating build/lib.linux-x86_64-3.7/Orange/data\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/data/_contingency.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o build/lib.linux-x86_64-3.7/Orange/data/_contingency.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.data._io' extension\ncc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c Orange/data/_io.c -o build/temp.linux-x86_64-3.7/Orange/data/_io.o\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/data/_io.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o build/lib.linux-x86_64-3.7/Orange/data/_io.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.data._valuecount' extension\ncc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c Orange/data/_valuecount.c -o build/temp.linux-x86_64-3.7/Orange/data/_valuecount.o\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/data/_valuecount.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o build/lib.linux-x86_64-3.7/Orange/data/_valuecount.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.data._variable' extension\ncc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c Orange/data/_variable.c -o build/temp.linux-x86_64-3.7/Orange/data/_variable.o\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/data/_variable.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o build/lib.linux-x86_64-3.7/Orange/data/_variable.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.distance._distance' extension\ncreating build/temp.linux-x86_64-3.7/Orange/distance\ncc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c Orange/distance/_distance.c -o build/temp.linux-x86_64-3.7/Orange/distance/_distance.o\ncreating build/lib.linux-x86_64-3.7/Orange/distance\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/distance/_distance.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o build/lib.linux-x86_64-3.7/Orange/distance/_distance.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.preprocess._discretize' extension\ncreating build/temp.linux-x86_64-3.7/Orange/preprocess\ncc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c Orange/preprocess/_discretize.c -o build/temp.linux-x86_64-3.7/Orange/preprocess/_discretize.o\ncreating build/lib.linux-x86_64-3.7/Orange/preprocess\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/preprocess/_discretize.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o build/lib.linux-x86_64-3.7/Orange/preprocess/_discretize.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.preprocess._relieff' extension\ncc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c Orange/preprocess/_relieff.cpp -o build/temp.linux-x86_64-3.7/Orange/preprocess/_relieff.o\nc++ -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/preprocess/_relieff.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o build/lib.linux-x86_64-3.7/Orange/preprocess/_relieff.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.projection._som' extension\ncreating build/temp.linux-x86_64-3.7/Orange/projection\ncc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c Orange/projection/_som.c -o build/temp.linux-x86_64-3.7/Orange/projection/_som.o\ncreating build/lib.linux-x86_64-3.7/Orange/projection\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/projection/_som.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o build/lib.linux-x86_64-3.7/Orange/projection/_som.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.classification._simple_tree' extension\ncc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c Orange/classification/_simple_tree.c -o build/temp.linux-x86_64-3.7/Orange/classification/_simple_tree.o\ncc -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/classification/_simple_tree.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o build/lib.linux-x86_64-3.7/Orange/classification/_simple_tree.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.widgets.utils._grid_density' extension\ncreating build/temp.linux-x86_64-3.7/Orange/widgets\ncreating build/temp.linux-x86_64-3.7/Orange/widgets/utils\ncc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c Orange/widgets/utils/_grid_density.cpp -o build/temp.linux-x86_64-3.7/Orange/widgets/utils/_grid_density.o\ncreating build/lib.linux-x86_64-3.7/Orange/widgets\ncreating build/lib.linux-x86_64-3.7/Orange/widgets/utils\nc++ -pthread -shared -L/tools/deps/lib build/temp.linux-x86_64-3.7/Orange/widgets/utils/_grid_density.o -L/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib -lm -lpython3.7m -o build/lib.linux-x86_64-3.7/Orange/widgets/utils/_grid_density.cpython-37m-x86_64-linux-gnu.so\ncopying build/lib.linux-x86_64-3.7/Orange/classification/_tree_scorers.cpython-37m-x86_64-linux-gnu.so -> Orange/classification\ncopying build/lib.linux-x86_64-3.7/Orange/data/_contingency.cpython-37m-x86_64-linux-gnu.so -> Orange/data\ncopying build/lib.linux-x86_64-3.7/Orange/data/_io.cpython-37m-x86_64-linux-gnu.so -> Orange/data\ncopying build/lib.linux-x86_64-3.7/Orange/data/_valuecount.cpython-37m-x86_64-linux-gnu.so -> Orange/data\ncopying build/lib.linux-x86_64-3.7/Orange/data/_variable.cpython-37m-x86_64-linux-gnu.so -> Orange/data\ncopying build/lib.linux-x86_64-3.7/Orange/distance/_distance.cpython-37m-x86_64-linux-gnu.so -> Orange/distance\ncopying build/lib.linux-x86_64-3.7/Orange/preprocess/_discretize.cpython-37m-x86_64-linux-gnu.so -> Orange/preprocess\ncopying build/lib.linux-x86_64-3.7/Orange/preprocess/_relieff.cpython-37m-x86_64-linux-gnu.so -> Orange/preprocess\ncopying build/lib.linux-x86_64-3.7/Orange/projection/_som.cpython-37m-x86_64-linux-gnu.so -> Orange/projection\ncopying build/lib.linux-x86_64-3.7/Orange/classification/_simple_tree.cpython-37m-x86_64-linux-gnu.so -> Orange/classification\ncopying build/lib.linux-x86_64-3.7/Orange/widgets/utils/_grid_density.cpython-37m-x86_64-linux-gnu.so -> Orange/widgets/utils\nrunning develop\nrunning egg_info\ncreating Orange3.egg-info\nwriting Orange3.egg-info/PKG-INFO\nwriting dependency_links to Orange3.egg-info/dependency_links.txt\nwriting entry points to Orange3.egg-info/entry_points.txt\nwriting requirements to Orange3.egg-info/requires.txt\nwriting top-level names to Orange3.egg-info/top_level.txt\nwriting manifest file 'Orange3.egg-info/SOURCES.txt'\nreading manifest file 'Orange3.egg-info/SOURCES.txt'\nreading manifest template 'MANIFEST.in'\nadding license file 'LICENSE'\nwriting manifest file 'Orange3.egg-info/SOURCES.txt'\nrunning build_ext\ncopying build/lib.linux-x86_64-3.7/Orange/classification/_tree_scorers.cpython-37m-x86_64-linux-gnu.so -> Orange/classification\ncopying build/lib.linux-x86_64-3.7/Orange/data/_contingency.cpython-37m-x86_64-linux-gnu.so -> Orange/data\ncopying build/lib.linux-x86_64-3.7/Orange/data/_io.cpython-37m-x86_64-linux-gnu.so -> Orange/data\ncopying build/lib.linux-x86_64-3.7/Orange/data/_valuecount.cpython-37m-x86_64-linux-gnu.so -> Orange/data\ncopying build/lib.linux-x86_64-3.7/Orange/data/_variable.cpython-37m-x86_64-linux-gnu.so -> Orange/data\ncopying build/lib.linux-x86_64-3.7/Orange/distance/_distance.cpython-37m-x86_64-linux-gnu.so -> Orange/distance\ncopying build/lib.linux-x86_64-3.7/Orange/preprocess/_discretize.cpython-37m-x86_64-linux-gnu.so -> Orange/preprocess\ncopying build/lib.linux-x86_64-3.7/Orange/preprocess/_relieff.cpython-37m-x86_64-linux-gnu.so -> Orange/preprocess\ncopying build/lib.linux-x86_64-3.7/Orange/projection/_som.cpython-37m-x86_64-linux-gnu.so -> Orange/projection\ncopying build/lib.linux-x86_64-3.7/Orange/classification/_simple_tree.cpython-37m-x86_64-linux-gnu.so -> Orange/classification\ncopying build/lib.linux-x86_64-3.7/Orange/widgets/utils/_grid_density.cpython-37m-x86_64-linux-gnu.so -> Orange/widgets/utils\nCreating /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/Orange3.egg-link (link to .)\nAdding Orange3 3.32.0.dev0+8226727 to easy-install.pth file\nInstalling orange-canvas script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\n\nInstalled /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474\nProcessing dependencies for Orange3==3.32.0.dev0+8226727\nSearching for XlsxWriter==3.2.0\nBest match: XlsxWriter 3.2.0\nAdding XlsxWriter 3.2.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for xlrd==2.0.1\nBest match: xlrd 2.0.1\nAdding xlrd 2.0.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for setuptools==59.8.0\nBest match: setuptools 59.8.0\nAdding setuptools 59.8.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for serverfiles==0.3.1\nBest match: serverfiles 0.3.1\nAdding serverfiles 0.3.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for scipy==1.7.3\nBest match: scipy 1.7.3\nAdding scipy 1.7.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for scikit-learn==1.0.2\nBest match: scikit-learn 1.0.2\nAdding scikit-learn 1.0.2 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for requests==2.31.0\nBest match: requests 2.31.0\nAdding requests 2.31.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for qtconsole==5.4.4\nBest match: qtconsole 5.4.4\nAdding qtconsole 5.4.4 to easy-install.pth file\nInstalling jupyter-qtconsole script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for PyYAML==6.0.1\nBest match: PyYAML 6.0.1\nAdding PyYAML 6.0.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for python-louvain==0.16\nBest match: python-louvain 0.16\nAdding python-louvain 0.16 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for pyqtgraph==0.12.4\nBest match: pyqtgraph 0.12.4\nAdding pyqtgraph 0.12.4 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for pygments==2.17.2\nBest match: pygments 2.17.2\nAdding pygments 2.17.2 to easy-install.pth file\nInstalling pygmentize script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for pip==24.0\nBest match: pip 24.0\nAdding pip 24.0 to easy-install.pth file\nInstalling pip script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\nInstalling pip3 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\nInstalling pip3.10 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for pandas==1.3.5\nBest match: pandas 1.3.5\nAdding pandas 1.3.5 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for orange-widget-base==4.22.0\nBest match: orange-widget-base 4.22.0\nAdding orange-widget-base 4.22.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for orange-canvas-core==0.1.35\nBest match: orange-canvas-core 0.1.35\nAdding orange-canvas-core 0.1.35 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for openpyxl==3.1.3\nBest match: openpyxl 3.1.3\nAdding openpyxl 3.1.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for openTSNE==1.0.1\nBest match: openTSNE 1.0.1\nAdding openTSNE 1.0.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for numpy==1.17.5\nBest match: numpy 1.17.5\nAdding numpy 1.17.5 to easy-install.pth file\nInstalling f2py script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\nInstalling f2py3 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\nInstalling f2py3.7 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for networkx==2.6.3\nBest match: networkx 2.6.3\nAdding networkx 2.6.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for matplotlib==3.5.3\nBest match: matplotlib 3.5.3\nAdding matplotlib 3.5.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for keyrings.alt==4.2.0\nBest match: keyrings.alt 4.2.0\nAdding keyrings.alt 4.2.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for keyring==24.1.1\nBest match: keyring 24.1.1\nAdding keyring 24.1.1 to easy-install.pth file\nInstalling keyring script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for joblib==1.3.2\nBest match: joblib 1.3.2\nAdding joblib 1.3.2 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for httpx==0.19.0\nBest match: httpx 0.19.0\nAdding httpx 0.19.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for chardet==5.2.0\nBest match: chardet 5.2.0\nAdding chardet 5.2.0 to easy-install.pth file\nInstalling chardetect script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for Bottleneck==1.4.0\nBest match: Bottleneck 1.4.0\nAdding Bottleneck 1.4.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for baycomp==1.0.3\nBest match: baycomp 1.0.3\nAdding baycomp 1.0.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for PyQtWebEngine==5.15.6\nBest match: PyQtWebEngine 5.15.6\nAdding PyQtWebEngine 5.15.6 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for PyQt5==5.15.10\nBest match: PyQt5 5.15.10\nAdding PyQt5 5.15.10 to easy-install.pth file\nInstalling pylupdate5 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\nInstalling pyrcc5 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\nInstalling pyuic5 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for AnyQt==0.2.0\nBest match: AnyQt 0.2.0\nAdding AnyQt 0.2.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for threadpoolctl==3.1.0\nBest match: threadpoolctl 3.1.0\nAdding threadpoolctl 3.1.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for charset-normalizer==3.4.1\nBest match: charset-normalizer 3.4.1\nAdding charset-normalizer 3.4.1 to easy-install.pth file\nInstalling normalizer script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for certifi==2024.12.14\nBest match: certifi 2024.12.14\nAdding certifi 2024.12.14 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for urllib3==2.0.7\nBest match: urllib3 2.0.7\nAdding urllib3 2.0.7 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for idna==3.10\nBest match: idna 3.10\nAdding idna 3.10 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for jupyter-core==4.12.0\nBest match: jupyter-core 4.12.0\nAdding jupyter-core 4.12.0 to easy-install.pth file\nInstalling jupyter script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\nInstalling jupyter-migrate script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\nInstalling jupyter-troubleshoot script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for traitlets==5.9.0\nBest match: traitlets 5.9.0\nAdding traitlets 5.9.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for ipykernel==6.16.2\nBest match: ipykernel 6.16.2\nAdding ipykernel 6.16.2 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for jupyter-client==7.4.9\nBest match: jupyter-client 7.4.9\nAdding jupyter-client 7.4.9 to easy-install.pth file\nInstalling jupyter-kernel script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\nInstalling jupyter-kernelspec script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\nInstalling jupyter-run script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for QtPy==2.4.2\nBest match: QtPy 2.4.2\nAdding QtPy 2.4.2 to easy-install.pth file\nInstalling qtpy script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for packaging==24.0\nBest match: packaging 24.0\nAdding packaging 24.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for pyzmq==26.2.0\nBest match: pyzmq 26.2.0\nAdding pyzmq 26.2.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for ipython-genutils==0.2.0\nBest match: ipython-genutils 0.2.0\nAdding ipython-genutils 0.2.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for pytz==2024.2\nBest match: pytz 2024.2\nAdding pytz 2024.2 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for python-dateutil==2.9.0.post0\nBest match: python-dateutil 2.9.0.post0\nAdding python-dateutil 2.9.0.post0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for typing-extensions==4.7.1\nBest match: typing-extensions 4.7.1\nAdding typing-extensions 4.7.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for requests-cache==1.1.1\nBest match: requests-cache 1.1.1\nAdding requests-cache 1.1.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for dictdiffer==0.9.0\nBest match: dictdiffer 0.9.0\nAdding dictdiffer 0.9.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for commonmark==0.9.1\nBest match: commonmark 0.9.1\nAdding commonmark 0.9.1 to easy-install.pth file\nInstalling cmark script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for importlib-metadata==6.7.0\nBest match: importlib-metadata 6.7.0\nAdding importlib-metadata 6.7.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for docutils==0.20.1\nBest match: docutils 0.20.1\nAdding docutils 0.20.1 to easy-install.pth file\nInstalling docutils script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for qasync==0.25.0\nBest match: qasync 0.25.0\nAdding qasync 0.25.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for et-xmlfile==1.1.0\nBest match: et-xmlfile 1.1.0\nAdding et-xmlfile 1.1.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for Pillow==9.5.0\nBest match: Pillow 9.5.0\nAdding Pillow 9.5.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for fonttools==4.38.0\nBest match: fonttools 4.38.0\nAdding fonttools 4.38.0 to easy-install.pth file\nInstalling fonttools script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\nInstalling pyftmerge script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\nInstalling pyftsubset script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\nInstalling ttx script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for cycler==0.11.0\nBest match: cycler 0.11.0\nAdding cycler 0.11.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for kiwisolver==1.4.5\nBest match: kiwisolver 1.4.5\nAdding kiwisolver 1.4.5 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for pyparsing==3.1.4\nBest match: pyparsing 3.1.4\nAdding pyparsing 3.1.4 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for jaraco.classes==3.2.3\nBest match: jaraco.classes 3.2.3\nAdding jaraco.classes 3.2.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for SecretStorage==3.3.3\nBest match: SecretStorage 3.3.3\nAdding SecretStorage 3.3.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for importlib-resources==5.12.0\nBest match: importlib-resources 5.12.0\nAdding importlib-resources 5.12.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for jeepney==0.8.0\nBest match: jeepney 0.8.0\nAdding jeepney 0.8.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for rfc3986==1.5.0\nBest match: rfc3986 1.5.0\nAdding rfc3986 1.5.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for httpcore==0.13.7\nBest match: httpcore 0.13.7\nAdding httpcore 0.13.7 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for sniffio==1.3.1\nBest match: sniffio 1.3.1\nAdding sniffio 1.3.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for PyQtWebEngine-Qt5==5.15.16\nBest match: PyQtWebEngine-Qt5 5.15.16\nAdding PyQtWebEngine-Qt5 5.15.16 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for PyQt5-sip==12.13.0\nBest match: PyQt5-sip 12.13.0\nAdding PyQt5-sip 12.13.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for PyQt5-Qt5==5.15.16\nBest match: PyQt5-Qt5 5.15.16\nAdding PyQt5-Qt5 5.15.16 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for psutil==6.1.1\nBest match: psutil 6.1.1\nAdding psutil 6.1.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for matplotlib-inline==0.1.6\nBest match: matplotlib-inline 0.1.6\nAdding matplotlib-inline 0.1.6 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for nest-asyncio==1.6.0\nBest match: nest-asyncio 1.6.0\nAdding nest-asyncio 1.6.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for tornado==6.2\nBest match: tornado 6.2\nAdding tornado 6.2 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for debugpy==1.7.0\nBest match: debugpy 1.7.0\nAdding debugpy 1.7.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for ipython==7.34.0\nBest match: ipython 7.34.0\nAdding ipython 7.34.0 to easy-install.pth file\nInstalling iptest script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\nInstalling iptest3 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\nInstalling ipython script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\nInstalling ipython3 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for entrypoints==0.4\nBest match: entrypoints 0.4\nAdding entrypoints 0.4 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for six==1.17.0\nBest match: six 1.17.0\nAdding six 1.17.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for cattrs==23.1.2\nBest match: cattrs 23.1.2\nAdding cattrs 23.1.2 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for platformdirs==4.0.0\nBest match: platformdirs 4.0.0\nAdding platformdirs 4.0.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for attrs==24.2.0\nBest match: attrs 24.2.0\nAdding attrs 24.2.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for url-normalize==1.4.3\nBest match: url-normalize 1.4.3\nAdding url-normalize 1.4.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for zipp==3.15.0\nBest match: zipp 3.15.0\nAdding zipp 3.15.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for more-itertools==9.1.0\nBest match: more-itertools 9.1.0\nAdding more-itertools 9.1.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for cryptography==44.0.0\nBest match: cryptography 44.0.0\nAdding cryptography 44.0.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for anyio==3.7.1\nBest match: anyio 3.7.1\nAdding anyio 3.7.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for h11==0.12.0\nBest match: h11 0.12.0\nAdding h11 0.12.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for pexpect==4.9.0\nBest match: pexpect 4.9.0\nAdding pexpect 4.9.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for decorator==5.1.1\nBest match: decorator 5.1.1\nAdding decorator 5.1.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for prompt-toolkit==3.0.48\nBest match: prompt-toolkit 3.0.48\nAdding prompt-toolkit 3.0.48 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for pickleshare==0.7.5\nBest match: pickleshare 0.7.5\nAdding pickleshare 0.7.5 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for jedi==0.19.2\nBest match: jedi 0.19.2\nAdding jedi 0.19.2 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for backcall==0.2.0\nBest match: backcall 0.2.0\nAdding backcall 0.2.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for exceptiongroup==1.2.2\nBest match: exceptiongroup 1.2.2\nAdding exceptiongroup 1.2.2 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for cffi==1.15.1\nBest match: cffi 1.15.1\nAdding cffi 1.15.1 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for ptyprocess==0.7.0\nBest match: ptyprocess 0.7.0\nAdding ptyprocess 0.7.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for wcwidth==0.2.13\nBest match: wcwidth 0.2.13\nAdding wcwidth 0.2.13 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for parso==0.8.4\nBest match: parso 0.8.4\nAdding parso 0.8.4 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nSearching for pycparser==2.21\nBest match: pycparser 2.21\nAdding pycparser 2.21 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages\nFinished processing dependencies for Orange3==3.32.0.dev0+8226727\nVerifying Orange installation...\n✅ Orange installation successful!\nSuccessfully installed orange using Python 3.7\n",
    "setup_res_stderr": "Using CPython 3.7.9\nCreating virtual environment at: .venv\nActivate with: source .venv/bin/activate\nResolved 18 packages in 54ms\nPrepared 18 packages in 33ms\nInstalled 18 packages in 193ms\n + cython==0.29.37\n + exceptiongroup==1.2.2\n + importlib-metadata==6.7.0\n + iniconfig==2.0.0\n + numpy==1.17.5\n + packaging==24.0\n + pluggy==1.2.0\n + pyqt5==5.15.10\n + pyqt5-qt5==5.15.16\n + pyqt5-sip==12.13.0\n + pyqtwebengine==5.15.6\n + pyqtwebengine-qt5==5.15.16\n + pytest==7.4.4\n + setuptools==59.8.0\n + tomli==2.0.1\n + typing-extensions==4.7.1\n + wheel==0.42.0\n + zipp==3.15.0\nResolved 55 packages in 513ms\nInstalled 48 packages in 912ms\n + anyio==3.7.1\n + baycomp==1.0.3\n + bottleneck==1.4.0\n + certifi==2024.12.14\n + cffi==1.15.1\n + chardet==5.2.0\n + charset-normalizer==3.4.1\n + cryptography==44.0.0\n + cycler==0.11.0\n + et-xmlfile==1.1.0\n + fonttools==4.38.0\n + h11==0.12.0\n + httpcore==0.13.7\n + httpx==0.19.0\n + idna==3.10\n + importlib-resources==5.12.0\n + jaraco-classes==3.2.3\n + jeepney==0.8.0\n + joblib==1.3.2\n + keyring==24.1.1\n + keyrings-alt==4.2.0\n + kiwisolver==1.4.5\n + matplotlib==3.5.3\n + more-itertools==9.1.0\n + networkx==2.6.3\n + openpyxl==3.1.3\n + opentsne==1.0.1\n + pandas==1.3.5\n + pillow==9.5.0\n + pip==24.0\n + pycparser==2.21\n + pyparsing==3.1.4\n + python-dateutil==2.9.0.post0\n + python-louvain==0.16\n + pytz==2024.2\n + pyyaml==6.0.1\n + requests==2.31.0\n + rfc3986==1.5.0\n + scikit-learn==1.0.2\n + scipy==1.7.3\n + secretstorage==3.3.3\n + serverfiles==0.3.1\n + six==1.17.0\n + sniffio==1.3.1\n + threadpoolctl==3.1.0\n + urllib3==2.0.7\n + xlrd==2.0.1\n + xlsxwriter==3.2.0\nResolved 59 packages in 443ms\nInstalled 38 packages in 349ms\n + anyqt==0.2.0\n + attrs==24.2.0\n + backcall==0.2.0\n + cattrs==23.1.2\n + commonmark==0.9.1\n + debugpy==1.7.0\n + decorator==5.1.1\n + dictdiffer==0.9.0\n + docutils==0.20.1\n + entrypoints==0.4\n + ipykernel==6.16.2\n + ipython==7.34.0\n + ipython-genutils==0.2.0\n + jedi==0.19.2\n + jupyter-client==7.4.9\n + jupyter-core==4.12.0\n + matplotlib-inline==0.1.6\n + nest-asyncio==1.6.0\n + orange-canvas-core==0.1.35\n + orange-widget-base==4.22.0\n + parso==0.8.4\n + pexpect==4.9.0\n + pickleshare==0.7.5\n + platformdirs==4.0.0\n + prompt-toolkit==3.0.48\n + psutil==6.1.1\n + ptyprocess==0.7.0\n + pygments==2.17.2\n + pyqtgraph==0.12.4\n + pyzmq==26.2.0\n + qasync==0.25.0\n + qtconsole==5.4.4\n + qtpy==2.4.2\n + requests-cache==1.1.1\n + tornado==6.2\n + traitlets==5.9.0\n + url-normalize==1.4.3\n + wcwidth==0.2.13\nResolved 2 packages in 4ms\nInstalled 2 packages in 35ms\n + psycopg2==2.9.9\n + pymssql==2.3.2\nResolved 19 packages in 30ms\nInstalled 5 packages in 1.31s\n + catboost==1.2.7\n + graphviz==0.20.1\n + plotly==5.18.0\n + tenacity==8.2.3\n + xgboost==1.6.2\nAudited 2 packages in 34ms\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/classification/_tree_scorers.c:775:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \"Using deprecated NumPy API, disable it with \" \"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-Wcpp]\n   17 | #warning \"Using deprecated NumPy API, disable it with \" \\\n      |  ^~~~~~~\nOrange/classification/_tree_scorers.c: In function ‘__pyx_pf_6Orange_14classification_13_tree_scorers_2find_threshold_entropy’:\nOrange/classification/_tree_scorers.c:3427:27: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 3427 |   __pyx_t_7 = ((__pyx_v_N <= __pyx_v_min_leaf) != 0);\n      |                           ^~\nOrange/classification/_tree_scorers.c: In function ‘__pyx_pf_6Orange_14classification_13_tree_scorers_6find_threshold_MSE’:\nOrange/classification/_tree_scorers.c:4883:27: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 4883 |   __pyx_t_1 = ((__pyx_v_N <= __pyx_v_min_leaf) != 0);\n      |                           ^~\nOrange/classification/_tree_scorers.c: In function ‘__pyx_pf_6Orange_14classification_13_tree_scorers_8find_binarization_MSE’:\nOrange/classification/_tree_scorers.c:5629:43: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 5629 |           for (__pyx_t_19 = 0; __pyx_t_19 < __pyx_t_18; __pyx_t_19+=1) {\n      |                                           ^\nOrange/classification/_tree_scorers.c:5750:39: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 5750 |           __pyx_t_20 = ((__pyx_v_left >= __pyx_v_min_leaf) != 0);\n      |                                       ^~\nOrange/classification/_tree_scorers.c:5756:53: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 5756 |           __pyx_t_20 = (((__pyx_v_N - __pyx_v_left) >= __pyx_v_min_leaf) != 0);\n      |                                                     ^~\nOrange/classification/_tree_scorers.c: In function ‘__pyx_pf_6Orange_14classification_13_tree_scorers_14compute_predictions_csr’:\nOrange/classification/_tree_scorers.c:7217:41: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 7217 |         for (__pyx_t_12 = 0; __pyx_t_12 < __pyx_t_11; __pyx_t_12+=1) {\n      |                                         ^\nOrange/classification/_tree_scorers.c: In function ‘__pyx_pf_6Orange_14classification_13_tree_scorers_16compute_predictions_csc’:\nOrange/classification/_tree_scorers.c:7874:41: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 7874 |         for (__pyx_t_12 = 0; __pyx_t_12 < __pyx_t_11; __pyx_t_12+=1) {\n      |                                         ^\nOrange/classification/_tree_scorers.c:7934:150: warning: comparison of integer expressions of different signedness: ‘__pyx_t_5numpy_int32_t’ {aka ‘int’} and ‘unsigned int’ [-Wsign-compare]\n 7934 |               __pyx_t_16 = (((*((__pyx_t_5numpy_int32_t const  *) ( /* dim=0 */ (__pyx_v_indices.data + __pyx_t_15 * __pyx_v_indices.strides[0]) ))) != __pyx_v_i) != 0);\n      |                                                                                                                                                      ^~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/data/_contingency.c:775:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \"Using deprecated NumPy API, disable it with \" \"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-Wcpp]\n   17 | #warning \"Using deprecated NumPy API, disable it with \" \\\n      |  ^~~~~~~\nOrange/data/_io.c: In function ‘__pyx_pf_6Orange_4data_3_io_sparse_read_float.constprop’:\nOrange/data/_io.c:4120:43: warning: ‘__pyx_v_decs’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4120 |           __pyx_v_value = ((__pyx_v_value * __pyx_v_decs) + (__pyx_v_c & 0xf));\n      |                            ~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~\nOrange/data/_io.c:4318:24: warning: ‘__pyx_v_value’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4318 |           __pyx_t_10 = PyFloat_FromDouble(__pyx_v_value); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 223, __pyx_L5_error)\n      |                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nOrange/data/_io.c:683:40: warning: ‘__pyx_v_attr_index’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n  683 |   #define PyInt_FromLong               PyLong_FromLong\n      |                                        ^~~~~~~~~~~~~~~\nOrange/data/_io.c:1590:7: note: ‘__pyx_v_attr_index’ was declared here\n 1590 |   int __pyx_v_attr_index;\n      |       ^~~~~~~~~~~~~~~~~~\nOrange/data/_io.c:3183:25: warning: ‘__pyx_v_atomp’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 3183 |           __pyx_v_atomp = (__pyx_v_atomp + 1);\n      |           ~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/data/_valuecount.c:775:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \"Using deprecated NumPy API, disable it with \" \"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-Wcpp]\n   17 | #warning \"Using deprecated NumPy API, disable it with \" \\\n      |  ^~~~~~~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/distance/_distance.c:775:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \"Using deprecated NumPy API, disable it with \" \"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-Wcpp]\n   17 | #warning \"Using deprecated NumPy API, disable it with \" \\\n      |  ^~~~~~~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/preprocess/_discretize.c:774:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \"Using deprecated NumPy API, disable it with \" \"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-Wcpp]\n   17 | #warning \"Using deprecated NumPy API, disable it with \" \\\n      |  ^~~~~~~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/preprocess/_relieff.cpp:790:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \"Using deprecated NumPy API, disable it with \" \"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-Wcpp]\n   17 | #warning \"Using deprecated NumPy API, disable it with \" \\\n      |  ^~~~~~~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/projection/_som.c:775:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning \"Using deprecated NumPy API, disable it with \" \"#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-Wcpp]\n   17 | #warning \"Using deprecated NumPy API, disable it with \" \\\n      |  ^~~~~~~\nOrange/projection/_som.c: In function ‘__pyx_pw_6Orange_10projection_4_som_5update_hex’:\nOrange/projection/_som.c:3863:7: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 3863 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:3862:7: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 3862 |   int __pyx_v_win_x;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c: In function ‘__pyx_pf_6Orange_10projection_4_som_8update_sparse.constprop’:\nOrange/projection/_som.c:4934:7: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4934 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:4933:7: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4933 |   int __pyx_v_win_x;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c: In function ‘__pyx_pw_6Orange_10projection_4_som_7get_winners_sparse’:\nOrange/projection/_som.c:4732:223: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4732 |           *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_int16_t *, __pyx_pybuffernd_winners.rcbuffer->pybuffer.buf, __pyx_t_26, __pyx_pybuffernd_winners.diminfo[0].strides, __pyx_t_27, __pyx_pybuffernd_winners.diminfo[1].strides) = __pyx_v_win_y;\n      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~\nOrange/projection/_som.c:4307:7: note: ‘__pyx_v_win_y’ was declared here\n 4307 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:4721:223: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4721 |           *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_int16_t *, __pyx_pybuffernd_winners.rcbuffer->pybuffer.buf, __pyx_t_27, __pyx_pybuffernd_winners.diminfo[0].strides, __pyx_t_26, __pyx_pybuffernd_winners.diminfo[1].strides) = __pyx_v_win_x;\n      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~\nOrange/projection/_som.c:4306:7: note: ‘__pyx_v_win_x’ was declared here\n 4306 |   int __pyx_v_win_x;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c: In function ‘__pyx_pf_6Orange_10projection_4_som_10update_sparse_hex.constprop’:\nOrange/projection/_som.c:5682:7: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 5682 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:5681:7: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 5681 |   int __pyx_v_win_x;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c: In function ‘__pyx_pw_6Orange_10projection_4_som_1get_winners’:\nOrange/projection/_som.c:3184:223: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 3184 |           *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_int16_t *, __pyx_pybuffernd_winners.rcbuffer->pybuffer.buf, __pyx_t_22, __pyx_pybuffernd_winners.diminfo[0].strides, __pyx_t_23, __pyx_pybuffernd_winners.diminfo[1].strides) = __pyx_v_win_y;\n      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~\nOrange/projection/_som.c:2887:7: note: ‘__pyx_v_win_y’ was declared here\n 2887 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:3173:223: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 3173 |           *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_int16_t *, __pyx_pybuffernd_winners.rcbuffer->pybuffer.buf, __pyx_t_23, __pyx_pybuffernd_winners.diminfo[0].strides, __pyx_t_22, __pyx_pybuffernd_winners.diminfo[1].strides) = __pyx_v_win_x;\n      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~\nOrange/projection/_som.c:2886:7: note: ‘__pyx_v_win_x’ was declared here\n 2886 |   int __pyx_v_win_x;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c: In function ‘__pyx_pw_6Orange_10projection_4_som_3update’:\nOrange/projection/_som.c:3357:7: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 3357 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/classification/_simple_tree.c: In function ‘gain_ratio_c’:\nOrange/classification/_simple_tree.c:63:9: warning: implicit declaration of function ‘qsort_r’; did you mean ‘qsort’? [-Wimplicit-function-declaration]\n   63 |         qsort_r(base, nel, size, compar, thunk)\n      |         ^~~~~~~\nOrange/classification/_simple_tree.c:147:9: note: in expansion of macro ‘QSORT_R_FUNC’\n  147 |         QSORT_R_FUNC(examples, size, sizeof(struct Example), (void*) &compar_attr, compar_examples);\n      |         ^~~~~~~~~~~~\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/setuptools/command/easy_install.py:159: EasyInstallDeprecationWarning: easy_install command is deprecated. Use build and pip and other standards-based tools.\n  EasyInstallDeprecationWarning,\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474/.venv/lib/python3.7/site-packages/setuptools/command/install.py:37: SetuptoolsDeprecationWarning: setup.py install is deprecated. Use build and pip and other standards-based tools.\n  setuptools.SetuptoolsDeprecationWarning,\nwarning: no files found matching 'README*' under directory 'Orange'\nwarning: no files found matching 'LICENSE' under directory 'Orange'\nwarning: no files found matching '*.basket' under directory 'Orange/datasets'\nwarning: no files found matching '*.js' under directory 'Orange/widgets'\nwarning: no files found matching '*.css' under directory 'Orange/widgets'\nwarning: no files found matching '*.html' under directory 'Orange/widgets'\nwarning: no files found matching 'COPYING'\n",
    "new_commit_res_code": 0,
    "new_commit_res_stdout": "============================= test session starts ==============================\nplatform linux -- Python 3.7.9, pytest-7.4.4, pluggy-1.2.0\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474\nplugins: anyio-3.7.1\ncollected 45 items\n\nr2e_tests/test_1.py sss..........................................        [100%]\n\n==================================== PASSES ====================================\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_bad_data\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_changed_class_var\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_colors_continuous\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_colors_diff_domain\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_colors_same_domain\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_continuous_class\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_image_export\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_minimum_size\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_mismatching_targets\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_missing_target_cls\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_missing_target_reg\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_msg_base_class\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_multi_inputs\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_nan_target_input\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_no_class_on_test\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_no_values_target\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_predictor_fails\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_reset_no_data\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_rowCount_from_model\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_select\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_select_data_first\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_selection_in_setting\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_sort_matching\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_sort_predictions\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_unique_output_domain\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_unregister_prediction_model\nPASSED r2e_tests/test_1.py::SharedSelectionStoreTest::test_clear\nPASSED r2e_tests/test_1.py::SharedSelectionStoreTest::test_emit_changed_maps_to_proxy\nPASSED r2e_tests/test_1.py::SharedSelectionStoreTest::test_registration\nPASSED r2e_tests/test_1.py::SharedSelectionStoreTest::test_reset\nPASSED r2e_tests/test_1.py::SharedSelectionStoreTest::test_select_maps_from_proxy\nPASSED r2e_tests/test_1.py::SharedSelectionStoreTest::test_select_rows\nPASSED r2e_tests/test_1.py::SharedSelectionModelTest::test_emit_selection_rows_changed\nPASSED r2e_tests/test_1.py::SharedSelectionModelTest::test_methods\nPASSED r2e_tests/test_1.py::SharedSelectionModelTest::test_select\nPASSED r2e_tests/test_1.py::SharedSelectionModelTest::test_selection_from_rows\nPASSED r2e_tests/test_1.py::PredictionsModelTest::test_model_classification\nPASSED r2e_tests/test_1.py::PredictionsModelTest::test_model_empty\nPASSED r2e_tests/test_1.py::PredictionsModelTest::test_model_header\nPASSED r2e_tests/test_1.py::PredictionsModelTest::test_model_regression\nPASSED r2e_tests/test_1.py::PredictionsModelTest::test_sorting_classification\nPASSED r2e_tests/test_1.py::PredictionsModelTest::test_sorting_regression\nSKIPPED [1] .venv/lib/python3.7/site-packages/orangewidget/tests/base.py:244: image exporting not tested as .widget was not set\nSKIPPED [1] .venv/lib/python3.7/site-packages/orangewidget/tests/base.py:238: minimum size not tested as .widget was not set\nSKIPPED [1] .venv/lib/python3.7/site-packages/orangewidget/tests/base.py:250: msg base class not tested as .widget was not set\n======================== 42 passed, 3 skipped in 8.66s =========================\nWidgets left: 0    Max widgets: 3600 \n\n",
    "new_commit_res_stderr": "",
    "old_commit_res_code": 1,
    "old_commit_res_stdout": "============================= test session starts ==============================\nplatform linux -- Python 3.7.9, pytest-7.4.4, pluggy-1.2.0\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_8226727eec78794ec1dd7b0e338de7c65ef60474\nplugins: anyio-3.7.1\ncollected 45 items\n\nr2e_tests/test_1.py sss.........FF...............................        [100%]\n\n=================================== FAILURES ===================================\n__________________ TestOWPredictions.test_missing_target_cls ___________________\n\nself = <r2e_tests.test_1.TestOWPredictions testMethod=test_missing_target_cls>\n\n    def test_missing_target_cls(self):\n        mask = np.zeros(len(self.iris), dtype=bool)\n        mask[::2] = True\n        train_data = self.iris[~mask]\n        predict_data = self.iris[mask]\n        model = LogisticRegressionLearner()(train_data)\n    \n        self.send_signal(self.widget.Inputs.predictors, model)\n        self.send_signal(self.widget.Inputs.data, predict_data)\n>       self.assertFalse(self.widget.Warning.missing_targets.is_shown())\nE       AttributeError: 'Warning' object has no attribute 'missing_targets'\n\nr2e_tests/test_1.py:533: AttributeError\n__________________ TestOWPredictions.test_missing_target_reg ___________________\n\nself = <r2e_tests.test_1.TestOWPredictions testMethod=test_missing_target_reg>\n\n    def test_missing_target_reg(self):\n        mask = np.zeros(len(self.housing), dtype=bool)\n        mask[::2] = True\n        train_data = self.housing[~mask]\n        predict_data = self.housing[mask]\n        model = LinearRegressionLearner()(train_data)\n    \n        self.send_signal(self.widget.Inputs.predictors, model)\n        self.send_signal(self.widget.Inputs.data, predict_data)\n>       self.assertFalse(self.widget.Warning.missing_targets.is_shown())\nE       AttributeError: 'Warning' object has no attribute 'missing_targets'\n\nr2e_tests/test_1.py:561: AttributeError\n==================================== PASSES ====================================\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_bad_data\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_changed_class_var\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_colors_continuous\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_colors_diff_domain\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_colors_same_domain\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_continuous_class\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_image_export\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_minimum_size\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_mismatching_targets\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_msg_base_class\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_multi_inputs\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_nan_target_input\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_no_class_on_test\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_no_values_target\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_predictor_fails\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_reset_no_data\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_rowCount_from_model\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_select\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_select_data_first\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_selection_in_setting\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_sort_matching\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_sort_predictions\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_unique_output_domain\nPASSED r2e_tests/test_1.py::TestOWPredictions::test_unregister_prediction_model\nPASSED r2e_tests/test_1.py::SharedSelectionStoreTest::test_clear\nPASSED r2e_tests/test_1.py::SharedSelectionStoreTest::test_emit_changed_maps_to_proxy\nPASSED r2e_tests/test_1.py::SharedSelectionStoreTest::test_registration\nPASSED r2e_tests/test_1.py::SharedSelectionStoreTest::test_reset\nPASSED r2e_tests/test_1.py::SharedSelectionStoreTest::test_select_maps_from_proxy\nPASSED r2e_tests/test_1.py::SharedSelectionStoreTest::test_select_rows\nPASSED r2e_tests/test_1.py::SharedSelectionModelTest::test_emit_selection_rows_changed\nPASSED r2e_tests/test_1.py::SharedSelectionModelTest::test_methods\nPASSED r2e_tests/test_1.py::SharedSelectionModelTest::test_select\nPASSED r2e_tests/test_1.py::SharedSelectionModelTest::test_selection_from_rows\nPASSED r2e_tests/test_1.py::PredictionsModelTest::test_model_classification\nPASSED r2e_tests/test_1.py::PredictionsModelTest::test_model_empty\nPASSED r2e_tests/test_1.py::PredictionsModelTest::test_model_header\nPASSED r2e_tests/test_1.py::PredictionsModelTest::test_model_regression\nPASSED r2e_tests/test_1.py::PredictionsModelTest::test_sorting_classification\nPASSED r2e_tests/test_1.py::PredictionsModelTest::test_sorting_regression\nSKIPPED [1] .venv/lib/python3.7/site-packages/orangewidget/tests/base.py:244: image exporting not tested as .widget was not set\nSKIPPED [1] .venv/lib/python3.7/site-packages/orangewidget/tests/base.py:238: minimum size not tested as .widget was not set\nSKIPPED [1] .venv/lib/python3.7/site-packages/orangewidget/tests/base.py:250: msg base class not tested as .widget was not set\nFAILED r2e_tests/test_1.py::TestOWPredictions::test_missing_target_cls - Attr...\nFAILED r2e_tests/test_1.py::TestOWPredictions::test_missing_target_reg - Attr...\n=================== 2 failed, 40 passed, 3 skipped in 6.06s ====================\nWidgets left: 0    Max widgets: 3600 \n\n",
    "old_commit_res_stderr": ""
}
Orange/widgets/evaluate/owpredictions.py Orange/widgets/evaluate/tests/test_owpredictions.py
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** AttributeError When Scoring Predictions with Missing Target Values

**Description:**
When attempting to score predictions using a dataset that includes instances with missing target values, the application encounters an `AttributeError`. Specifically, the system tries to access a `missing_targets` attribute in the `Warning` object, which does not exist.

**Example Code:**
```python
import numpy as np
from Orange.classification import LogisticRegressionLearner
from Orange.widgets.evaluate.owpredictions import OWPredictions

# Setup widget and data with missing targets
widget = OWPredictions()
data = some_dataset  # Assume this is a valid Orange data table
mask = np.zeros(len(data), dtype=bool)
mask[::2] = True
train_data = data[~mask]
predict_data = data[mask]
model = LogisticRegressionLearner()(train_data)

# Send signals to the widget
widget.send_signal(widget.Inputs.predictors, model)
widget.send_signal(widget.Inputs.data, predict_data)

# Attempt to check for missing targets warning
# This line raises AttributeError
widget.Warning.missing_targets.is_shown()
```

**Expected Behavior:**
The widget should gracefully handle instances with missing target values by ignoring these rows during scoring and displaying a warning to inform the user about the ignored instances.

**Actual Behavior:**
An `AttributeError` is raised because the `Warning` object does not have a `missing_targets` attribute, preventing the application from informing the user about the missing targets.

[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### Orange/widgets/evaluate/owpredictions.py
from contextlib import contextmanager
from functools import partial
from operator import itemgetter
from itertools import chain
from typing import Set, Sequence, Union, Optional, List, NamedTuple

import numpy
from AnyQt.QtWidgets import (
    QTableView, QListWidget, QSplitter, QToolTip, QStyle, QApplication,
    QSizePolicy
)
from AnyQt.QtGui import QPainter, QStandardItem, QPen, QColor
from AnyQt.QtCore import (
    Qt, QSize, QRect, QRectF, QPoint, QLocale,
    QModelIndex, pyqtSignal, QTimer,
    QItemSelectionModel, QItemSelection)

from orangewidget.report import plural
from orangewidget.utils.itemmodels import AbstractSortTableModel

import Orange
from Orange.evaluation import Results
from Orange.base import Model
from Orange.data import ContinuousVariable, DiscreteVariable, Domain
from Orange.data.table import DomainTransformationError
from Orange.data.util import get_unique_names
from Orange.widgets import gui, settings
from Orange.widgets.evaluate.utils import (
    ScoreTable, usable_scorers, learner_name, scorer_caller)
from Orange.widgets.utils.widgetpreview import WidgetPreview
from Orange.widgets.widget import OWWidget, Msg, Input, Output, MultiInput
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
from Orange.widgets.utils.colorpalettes import LimitedDiscretePalette
from Orange.widgets.utils.itemdelegates import TableDataDelegate

# Input slot for the Predictors channel
PredictorSlot = NamedTuple(
    "PredictorSlot", [
        ("predictor", Model),  # The `Model` instance
        ("name", str),       # Predictor name
        ("results", Optional[Results]),    # Computed prediction results or None.
])


class OWPredictions(OWWidget):
    name = "Predictions"
    icon = "icons/Predictions.svg"
    priority = 200
    description = "Display predictions of models for an input dataset."
    keywords = []

    buttons_area_orientation = None

    class Inputs:
        data = Input("Data", Orange.data.Table)
        predictors = MultiInput("Predictors", Model, filter_none=True)

    class Outputs:
        predictions = Output("Predictions", Orange.data.Table)
        evaluation_results = Output("Evaluation Results", Results)

    class Warning(OWWidget.Warning):
        empty_data = Msg("Empty dataset")
        wrong_targets = Msg(
            "Some model(s) predict a different target (see more ...)\n{}")

    class Error(OWWidget.Error):
        predictor_failed = Msg("Some predictor(s) failed (see more ...)\n{}")
        scorer_failed = Msg("Some scorer(s) failed (see more ...)\n{}")

    settingsHandler = settings.ClassValuesContextHandler()
    score_table = settings.SettingProvider(ScoreTable)

    #: List of selected class value indices in the `class_values` list
    selected_classes = settings.ContextSetting([])
    selection = settings.Setting([], schema_only=True)

    def __init__(self):
        super().__init__()

        self.data = None  # type: Optional[Orange.data.Table]
        self.predictors = []  # type: List[PredictorSlot]
        self.class_values = []  # type: List[str]
        self._delegates = []
        self.left_width = 10
        self.selection_store = None
        self.__pending_selection = self.selection

        controlBox = gui.vBox(self.controlArea, "Show probabilities for")

        gui.listBox(controlBox, self, "selected_classes", "class_values",
                    callback=self._update_prediction_delegate,
                    selectionMode=QListWidget.ExtendedSelection,
                    sizePolicy=(QSizePolicy.Preferred, QSizePolicy.MinimumExpanding),
                    sizeHint=QSize(1, 350),
                    minimumHeight=100)
        self.reset_button = gui.button(
            controlBox, self, "Restore Original Order",
            callback=self._reset_order,
            tooltip="Show rows in the original order")

        table_opts = dict(horizontalScrollBarPolicy=Qt.ScrollBarAlwaysOn,
                          horizontalScrollMode=QTableView.ScrollPerPixel,
                          selectionMode=QTableView.ExtendedSelection,
                          focusPolicy=Qt.StrongFocus)
        self.dataview = TableView(
            sortingEnabled=True,
            verticalScrollBarPolicy=Qt.ScrollBarAlwaysOn,
            **table_opts)
        self.predictionsview = TableView(
            sortingEnabled=True,
            verticalScrollBarPolicy=Qt.ScrollBarAlwaysOff,
            **table_opts)
        self.dataview.verticalHeader().hide()
        dsbar = self.dataview.verticalScrollBar()
        psbar = self.predictionsview.verticalScrollBar()
        psbar.valueChanged.connect(dsbar.setValue)
        dsbar.valueChanged.connect(psbar.setValue)

        self.dataview.verticalHeader().setDefaultSectionSize(22)
        self.predictionsview.verticalHeader().setDefaultSectionSize(22)
        self.dataview.verticalHeader().sectionResized.connect(
            lambda index, _, size:
            self.predictionsview.verticalHeader().resizeSection(index, size))

        self.dataview.setItemDelegate(DataItemDelegate(self.dataview))

        self.splitter = QSplitter(
            orientation=Qt.Horizontal, childrenCollapsible=False, handleWidth=2)
        self.splitter.splitterMoved.connect(self.splitter_resized)
        self.splitter.addWidget(self.predictionsview)
        self.splitter.addWidget(self.dataview)

        self.score_table = ScoreTable(self)
        self.vsplitter = gui.vBox(self.mainArea)
        self.vsplitter.layout().addWidget(self.splitter)
        self.vsplitter.layout().addWidget(self.score_table.view)

    def get_selection_store(self, model):
        # Both models map the same, so it doesn't matter which one is used
        # to initialize SharedSelectionStore
        if self.selection_store is None:
            self.selection_store = SharedSelectionStore(model)
        return self.selection_store

    @Inputs.data
    @check_sql_input
    def set_data(self, data):
        self.Warning.empty_data(shown=data is not None and not data)
        self.data = data
        self.selection_store = None
        if not data:
            self.dataview.setModel(None)
            self.predictionsview.setModel(None)
        else:
            # force full reset of the view's HeaderView state
            self.dataview.setModel(None)
            model = DataModel(data, parent=None)
            self.dataview.setModel(model)
            sel_model = SharedSelectionModel(
                self.get_selection_store(model), model, self.dataview)
            self.dataview.setSelectionModel(sel_model)
            if self.__pending_selection is not None:
                self.selection = self.__pending_selection
                self.__pending_selection = None
                self.selection_store.select_rows(
                    set(self.selection), QItemSelectionModel.ClearAndSelect)
            sel_model.selectionChanged.connect(self.commit)
            sel_model.selectionChanged.connect(self._store_selection)

            self.dataview.model().list_sorted.connect(
                partial(
                    self._update_data_sort_order, self.dataview,
                    self.predictionsview))

        self._invalidate_predictions()

    def _store_selection(self):
        self.selection = list(self.selection_store.rows)

    @property
    def class_var(self):
        return self.data and self.data.domain.class_var

    @Inputs.predictors
    def set_predictor(self, index, predictor: Model):
        item = self.predictors[index]
        self.predictors[index] = item._replace(
            predictor=predictor, name=predictor.name, results=None
        )

    @Inputs.predictors.insert
    def insert_predictor(self, index, predictor: Model):
        item = PredictorSlot(predictor, predictor.name, None)
        self.predictors.insert(index, item)

    @Inputs.predictors.remove
    def remove_predictor(self, index):
        self.predictors.pop(index)

    def _set_class_values(self):
        class_values = []
        for slot in self.predictors:
            class_var = slot.predictor.domain.class_var
            if class_var and class_var.is_discrete:
                for value in class_var.values:
                    if value not in class_values:
                        class_values.append(value)

        if self.class_var and self.class_var.is_discrete:
            values = self.class_var.values
            self.class_values = sorted(
                class_values, key=lambda val: val not in values)
            self.selected_classes = [
                i for i, name in enumerate(class_values) if name in values]
        else:
            self.class_values = class_values  # This assignment updates listview
            self.selected_classes = []

    def handleNewSignals(self):
        # Disconnect the model: the model and the delegate will be inconsistent
        # between _set_class_values and update_predictions_model.
        self.predictionsview.setModel(None)
        self._set_class_values()
        self._call_predictors()
        self._update_scores()
        self._update_predictions_model()
        self._update_prediction_delegate()
        self._set_errors()
        self.commit()

    def _call_predictors(self):
        if not self.data:
            return
        if self.class_var:
            domain = self.data.domain
            classless_data = self.data.transform(
                Domain(domain.attributes, None, domain.metas))
        else:
            classless_data = self.data

        for index, slot in enumerate(self.predictors):
            if isinstance(slot.results, Results):
                continue

            predictor = slot.predictor
            try:
                if predictor.domain.class_var.is_discrete:
                    pred, prob = predictor(classless_data, Model.ValueProbs)
                else:
                    pred = predictor(classless_data, Model.Value)
                    prob = numpy.zeros((len(pred), 0))
            except (ValueError, DomainTransformationError) as err:
                self.predictors[index] = \
                    slot._replace(results=f"{predictor.name}: {err}")
                continue

            results = Results()
            results.data = self.data
            results.domain = self.data.domain
            results.row_indices = numpy.arange(len(self.data))
            results.folds = (Ellipsis, )
            results.actual = self.data.Y
            results.unmapped_probabilities = prob
            results.unmapped_predicted = pred
            results.probabilities = results.predicted = None
            self.predictors[index] = slot._replace(results=results)

            target = predictor.domain.class_var
            if target != self.class_var:
                continue

            if target is not self.class_var and target.is_discrete:
                backmappers, n_values = predictor.get_backmappers(self.data)
                prob = predictor.backmap_probs(prob, n_values, backmappers)
                pred = predictor.backmap_value(pred, prob, n_values, backmappers)
            results.predicted = pred.reshape((1, len(self.data)))
            results.probabilities = prob.reshape((1,) + prob.shape)

    def _update_scores(self):
        model = self.score_table.model
        model.clear()
        scorers = usable_scorers(self.class_var) if self.class_var else []
        self.score_table.update_header(scorers)
        errors = []
        for pred in self.predictors:
            results = pred.results
            if not isinstance(results, Results) or results.predicted is None:
                continue
            row = [QStandardItem(learner_name(pred.predictor)),
                   QStandardItem("N/A"), QStandardItem("N/A")]
            for scorer in scorers:
                item = QStandardItem()
                try:
                    score = scorer_caller(scorer, results)()[0]
                    item.setText(f"{score:.3f}")
                except Exception as exc:  # pylint: disable=broad-except
                    item.setToolTip(str(exc))
                    # false pos.; pylint: disable=unsupported-membership-test
                    if scorer.name in self.score_table.shown_scores:
                        errors.append(str(exc))
                row.append(item)
            self.score_table.model.appendRow(row)

        view = self.score_table.view
        if model.rowCount():
            view.setVisible(True)
            view.ensurePolished()
            view.setFixedHeight(
                5 + view.horizontalHeader().height() +
                view.verticalHeader().sectionSize(0) * model.rowCount())
        else:
            view.setVisible(False)

        self.Error.scorer_failed("\n".join(errors), shown=bool(errors))

    def _set_errors(self):
        # Not all predictors are run every time, so errors can't be collected
        # in _call_predictors
        errors = "\n".join(
            f"- {p.predictor.name}: {p.results}"
            for p in self.predictors
            if isinstance(p.results, str) and p.results)
        self.Error.predictor_failed(errors, shown=bool(errors))

        if self.class_var:
            inv_targets = "\n".join(
                f"- {pred.name} predicts '{pred.domain.class_var.name}'"
                for pred in (p.predictor for p in self.predictors
                             if isinstance(p.results, Results)
                             and p.results.probabilities is None))
            self.Warning.wrong_targets(inv_targets, shown=bool(inv_targets))
        else:
            self.Warning.wrong_targets.clear()

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

    def _invalidate_predictions(self):
        for i, pred in enumerate(self.predictors):
            self.predictors[i] = pred._replace(results=None)

    def _non_errored_predictors(self):
        return [p for p in self.predictors
                if isinstance(p.results, Results)]

    def _reordered_probabilities(self, prediction):
        cur_values = prediction.predictor.domain.class_var.values
        new_ind = [self.class_values.index(x) for x in cur_values]
        probs = prediction.results.unmapped_probabilities
        new_probs = numpy.full(
            (probs.shape[0], len(self.class_values)), numpy.nan)
        new_probs[:, new_ind] = probs
        return new_probs

    def _update_predictions_model(self):
        headers = []
        all_values = []
        all_probs = []
        for p in self._non_errored_predictors():
            values = p.results.unmapped_predicted
            target = p.predictor.domain.class_var
            if target.is_discrete:
                # order probabilities in order from Show prob. for
                prob = self._reordered_probabilities(p)
                values = numpy.array(target.values)[values.astype(int)]
            else:
                prob = numpy.zeros((len(values), 0))
            all_values.append(values)
            all_probs.append(prob)
            headers.append(p.predictor.name)

        if all_values:
            model = PredictionsModel(all_values, all_probs, headers)
            model.list_sorted.connect(
                partial(
                    self._update_data_sort_order, self.predictionsview,
                    self.dataview))
        else:
            model = None

        if self.selection_store is not None:
            self.selection_store.unregister(
                self.predictionsview.selectionModel())

        self.predictionsview.setModel(model)
        if model is not None:
            self.predictionsview.setSelectionModel(
                SharedSelectionModel(self.get_selection_store(model),
                                     model, self.predictionsview))

        hheader = self.predictionsview.horizontalHeader()
        hheader.setSortIndicatorShown(False)
        hheader.setSectionsClickable(True)

    def _update_data_sort_order(self, sort_source_view, sort_dest_view):
        sort_source_view.horizontalHeader().setSortIndicatorShown(True)
        sort_dest_view.horizontalHeader().setSortIndicatorShown(False)

        sort_dest = sort_dest_view.model()
        sort_source = sort_source_view.model()
        if sort_dest is not None:
            if sort_source is not None and sort_source.sortColumn() >= 0:
                sort_dest.setSortIndices(sort_source.mapToSourceRows(...))
            else:
                sort_dest.setSortIndices(None)
        self.commit()

    def _reset_order(self):
        datamodel = self.dataview.model()
        predmodel = self.predictionsview.model()
        if datamodel is not None:
            datamodel.setSortIndices(None)
            datamodel.sort(-1)
        if predmodel is not None:
            predmodel.setSortIndices(None)
            predmodel.sort(-1)
        self.predictionsview.horizontalHeader().setSortIndicatorShown(False)
        self.dataview.horizontalHeader().setSortIndicatorShown(False)

    def _all_color_values(self):
        """
        Return list of colors together with their values from all predictors
        classes. Colors and values are sorted according to the values order
        for simpler comparison.
        """
        predictors = self._non_errored_predictors()
        color_values = [
            list(zip(*sorted(zip(
                p.predictor.domain.class_var.colors,
                p.predictor.domain.class_var.values
            ), key=itemgetter(1))))
            for p in predictors if p.predictor.domain.class_var.is_discrete
        ]
        return color_values if color_values else [([], [])]

    @staticmethod
    def _colors_match(colors1, values1, color2, values2):
        """
        Test whether colors for values match. Colors matches when all
        values match for shorter list and colors match for shorter list.
        It is assumed that values will be sorted together with their colors.
        """
        shorter_length = min(len(colors1), len(color2))
        return (values1[:shorter_length] == values2[:shorter_length]
                and (numpy.array(colors1[:shorter_length]) ==
                     numpy.array(color2[:shorter_length])).all())

    def _get_colors(self):
        """
        Defines colors for values. If colors match in all models use the union
        otherwise use standard colors.
        """
        all_colors_values = self._all_color_values()
        base_color, base_values = all_colors_values[0]
        for c, v in all_colors_values[1:]:
            if not self._colors_match(base_color, base_values, c, v):
                base_color = []
                break
            # replace base_color if longer
            if len(v) > len(base_color):
                base_color = c
                base_values = v

        if len(base_color) != len(self.class_values):
            return LimitedDiscretePalette(len(self.class_values)).palette
        # reorder colors to widgets order
        colors = [None] * len(self.class_values)
        for c, v in zip(base_color, base_values):
            colors[self.class_values.index(v)] = c
        return colors

    def _update_prediction_delegate(self):
        self._delegates.clear()
        colors = self._get_colors()
        for col, slot in enumerate(self.predictors):
            target = slot.predictor.domain.class_var
            shown_probs = (
                () if target.is_continuous else
                [val if self.class_values[val] in target.values else None
                 for val in self.selected_classes]
            )
            delegate = PredictionsItemDelegate(
                None if target.is_continuous else self.class_values,
                colors,
                shown_probs,
                target.format_str if target.is_continuous else None,
                parent=self.predictionsview
            )
            # QAbstractItemView does not take ownership of delegates, so we must
            self._delegates.append(delegate)
            self.predictionsview.setItemDelegateForColumn(col, delegate)
            self.predictionsview.setColumnHidden(col, False)

        self.predictionsview.resizeColumnsToContents()
        self._recompute_splitter_sizes()
        if self.predictionsview.model() is not None:
            self.predictionsview.model().setProbInd(self.selected_classes)

    def _recompute_splitter_sizes(self):
        if not self.data:
            return
        view = self.predictionsview
        self.left_width = \
            view.horizontalHeader().length() + view.verticalHeader().width()
        self._update_splitter()

    def _update_splitter(self):
        w1, w2 = self.splitter.sizes()
        self.splitter.setSizes([self.left_width, w1 + w2 - self.left_width])

    def splitter_resized(self):
        self.left_width = self.splitter.sizes()[0]

    def commit(self):
        self._commit_predictions()
        self._commit_evaluation_results()

    def _commit_evaluation_results(self):
        slots = [p for p in self._non_errored_predictors()
                 if p.results.predicted is not None]
        if not slots:
            self.Outputs.evaluation_results.send(None)
            return

        nanmask = numpy.isnan(self.data.get_column_view(self.class_var)[0])
        data = self.data[~nanmask]
        results = Results(data, store_data=True)
        results.folds = None
        results.row_indices = numpy.arange(len(data))
        results.actual = data.Y.ravel()
        results.predicted = numpy.vstack(
            tuple(p.results.predicted[0][~nanmask] for p in slots))
        if self.class_var and self.class_var.is_discrete:
            results.probabilities = numpy.array(
                [p.results.probabilities[0][~nanmask] for p in slots])
        results.learner_names = [p.name for p in slots]
        self.Outputs.evaluation_results.send(results)

    def _commit_predictions(self):
        if not self.data:
            self.Outputs.predictions.send(None)
            return

        newmetas = []
        newcolumns = []
        for slot in self._non_errored_predictors():
            if slot.predictor.domain.class_var.is_discrete:
                self._add_classification_out_columns(slot, newmetas, newcolumns)
            else:
                self._add_regression_out_columns(slot, newmetas, newcolumns)

        attrs = list(self.data.domain.attributes)
        metas = list(self.data.domain.metas)
        names = [var.name for var in chain(attrs, self.data.domain.class_vars, metas) if var]
        uniq_newmetas = []
        for new_ in newmetas:
            uniq = get_unique_names(names, new_.name)
            if uniq != new_.name:
                new_ = new_.copy(name=uniq)
            uniq_newmetas.append(new_)
            names.append(uniq)

        metas += uniq_newmetas
        domain = Orange.data.Domain(attrs, self.class_var, metas=metas)
        predictions = self.data.transform(domain)
        if newcolumns:
            newcolumns = numpy.hstack(
                [numpy.atleast_2d(cols) for cols in newcolumns])
            with predictions.unlocked(predictions.metas):
                predictions.metas[:, -newcolumns.shape[1]:] = newcolumns

        datamodel = self.dataview.model()
        predmodel = self.predictionsview.model()
        assert datamodel is not None  # because we have data
        assert self.selection_store is not None
        rows = numpy.array(list(self.selection_store.rows))
        if rows.size:
            # Reorder rows as they are ordered in view
            shown_rows = datamodel.mapFromSourceRows(rows)
            rows = rows[numpy.argsort(shown_rows)]
            predictions = predictions[rows]
        elif datamodel.sortColumn() >= 0 \
                or predmodel is not None and predmodel.sortColumn() > 0:
            # No selection: output all, but in the shown order
            predictions = predictions[datamodel.mapToSourceRows(...)]
        self.Outputs.predictions.send(predictions)

    @staticmethod
    def _add_classification_out_columns(slot, newmetas, newcolumns):
        # Mapped or unmapped predictions?!
        # Or provide a checkbox so the user decides?
        pred = slot.predictor
        name = pred.name
        values = pred.domain.class_var.values
        newmetas.append(DiscreteVariable(name=name, values=values))
        newcolumns.append(slot.results.unmapped_predicted.reshape(-1, 1))
        newmetas += [ContinuousVariable(name=f"{name} ({value})")
                     for value in values]
        newcolumns.append(slot.results.unmapped_probabilities)

    @staticmethod
    def _add_regression_out_columns(slot, newmetas, newcolumns):
        newmetas.append(ContinuousVariable(name=slot.predictor.name))
        newcolumns.append(slot.results.unmapped_predicted.reshape((-1, 1)))

    def send_report(self):
        def merge_data_with_predictions():
            data_model = self.dataview.model()
            predictions_view = self.predictionsview
            predictions_model = predictions_view.model()

            # use ItemDelegate to style prediction values
            delegates = [predictions_view.itemDelegateForColumn(i)
                         for i in range(predictions_model.columnCount())]

            # iterate only over visible columns of data's QTableView
            iter_data_cols = list(filter(lambda x: not self.dataview.isColumnHidden(x),
                                         range(data_model.columnCount())))

            # print header
            yield [''] + \
                  [predictions_model.headerData(col, Qt.Horizontal, Qt.DisplayRole)
                   for col in range(predictions_model.columnCount())] + \
                  [data_model.headerData(col, Qt.Horizontal, Qt.DisplayRole)
                   for col in iter_data_cols]

            # print data & predictions
            for i in range(data_model.rowCount()):
                yield [data_model.headerData(i, Qt.Vertical, Qt.DisplayRole)] + \
                      [delegate.displayText(
                          predictions_model.data(predictions_model.index(i, j)),
                          QLocale())
                       for j, delegate in enumerate(delegates)] + \
                      [data_model.data(data_model.index(i, j),
                                       role=Qt.DisplayRole)
                       for j in iter_data_cols]

        if self.data:
            text = self._get_details().replace('\n', '<br>')
            if self.selected_classes:
                text += '<br>Showing probabilities for: '
                text += ', '. join([self.class_values[i]
                                    for i in self.selected_classes])
            self.report_paragraph('Info', text)
            self.report_table("Data & Predictions", merge_data_with_predictions(),
                              header_rows=1, header_columns=1)

            self.report_table("Scores", self.score_table.view)

    def resizeEvent(self, event):
        super().resizeEvent(event)
        self._update_splitter()

    def showEvent(self, event):
        super().showEvent(event)
        QTimer.singleShot(0, self._update_splitter)


class ItemDelegate(TableDataDelegate):
    def initStyleOption(self, option, index):
        super().initStyleOption(option, index)
        if self.parent().selectionModel().isSelected(index):
            option.state |= QStyle.State_Selected
        if self.parent().window().isActiveWindow():
            option.state |= QStyle.State_Active | QStyle.State_HasFocus


class DataItemDelegate(ItemDelegate):
    pass


class PredictionsItemDelegate(ItemDelegate):
    """
    A Item Delegate for custom formatting of predictions/probabilities
    """
    #: Roles supplied by the `PredictionsModel`
    DefaultRoles = (Qt.DisplayRole, )

    def __init__(
            self, class_values, colors, shown_probabilities=(),
            target_format=None, parent=None,
    ):
        super().__init__(parent)
        self.class_values = class_values  # will be None for continuous
        self.colors = [QColor(*c) for c in colors]
        self.target_format = target_format  # target format for cont. vars
        self.shown_probabilities = self.fmt = self.tooltip = None  # set below
        self.setFormat(shown_probabilities)

    def setFormat(self, shown_probabilities=()):
        self.shown_probabilities = shown_probabilities
        if self.class_values is None:
            # is continuous class
            self.fmt = f"{{value:{self.target_format[1:]}}}"
        else:
            self.fmt = " \N{RIGHTWARDS ARROW} ".join(
                [" : ".join(f"{{dist[{i}]:.2f}}" if i is not None else "-"
                            for i in shown_probabilities)]
                * bool(shown_probabilities)
                + ["{value!s}"])
        self.tooltip = ""
        if shown_probabilities:
            val = ', '.join(
                self.class_values[i] if i is not None else "-"
                for i in shown_probabilities if i is not None
            )
            self.tooltip = f"p({val})"

    def displayText(self, value, _):
        try:
            value, dist = value
        except ValueError:
            return ""
        else:
            return self.fmt.format(value=value, dist=dist)

    def helpEvent(self, event, view, option, index):
        if self.tooltip is not None:
            # ... but can be an empty string, so the current tooltip is removed
            QToolTip.showText(event.globalPos(), self.tooltip, view)
            return True
        else:
            return super().helpEvent(event, view, option, index)

    def initStyleOption(self, option, index):
        super().initStyleOption(option, index)
        if self.class_values is None:
            option.displayAlignment = \
                (option.displayAlignment & Qt.AlignVertical_Mask) | \
                Qt.AlignRight

    def sizeHint(self, option, index):
        # reimplemented
        sh = super().sizeHint(option, index)
        if option.widget is not None:
            style = option.widget.style()
        else:
            style = QApplication.style()
        margin = style.pixelMetric(
            QStyle.PM_FocusFrameHMargin, option, option.widget) + 1
        metrics = option.fontMetrics
        height = sh.height() + metrics.leading() + 2 * margin
        return QSize(sh.width(), height)

    def distribution(self, index):
        value = self.cachedData(index, Qt.DisplayRole)
        if isinstance(value, tuple) and len(value) == 2:
            _, dist = value
            return dist
        else:
            return None

    def paint(self, painter, option, index):
        dist = self.distribution(index)
        if dist is None or self.colors is None:
            super().paint(painter, option, index)
            return

        if option.widget is not None:
            style = option.widget.style()
        else:
            style = QApplication.style()

        self.initStyleOption(option, index)

        text = option.text
        metrics = option.fontMetrics

        margin = style.pixelMetric(
            QStyle.PM_FocusFrameHMargin, option, option.widget) + 1
        bottommargin = min(margin, 1)
        rect = option.rect.adjusted(margin, margin, -margin, -bottommargin)
        option.text = ""
        textrect = style.subElementRect(
            QStyle.SE_ItemViewItemText, option, option.widget)
        # Are the margins included in the subElementRect?? -> No!
        textrect = textrect.adjusted(margin, margin, -margin, -bottommargin)
        spacing = max(metrics.leading(), 1)

        distheight = rect.height() - metrics.height() - spacing
        distheight = min(max(distheight, 2), metrics.height())
        painter.save()
        painter.setClipRect(option.rect)
        painter.setFont(option.font)
        painter.setRenderHint(QPainter.Antialiasing)

        style.drawPrimitive(
            QStyle.PE_PanelItemViewRow, option, painter, option.widget)
        style.drawPrimitive(
            QStyle.PE_PanelItemViewItem, option, painter, option.widget)

        textrect = textrect.adjusted(0, 0, 0, -distheight - spacing)
        distrect = QRect(
            textrect.bottomLeft() + QPoint(0, spacing),
            QSize(rect.width(), distheight))
        painter.setPen(QPen(Qt.lightGray, 0.3))
        self.drawDistBar(painter, distrect, dist)
        painter.restore()
        if text:
            option.text = text
            self.drawViewItemText(style, painter, option, textrect)

    def drawDistBar(self, painter, rect, distribution):
        painter.save()
        painter.translate(rect.topLeft())
        for i in self.shown_probabilities:
            if i is None:
                continue
            dvalue = distribution[i]
            if not dvalue > 0:  # This also skips nans
                continue
            painter.setBrush(self.colors[i])
            width = rect.width() * dvalue
            painter.drawRoundedRect(QRectF(0, 0, width, rect.height()), 1, 2)
            painter.translate(width, 0.0)
        painter.restore()


class PredictionsModel(AbstractSortTableModel):
    list_sorted = pyqtSignal()

    def __init__(self, values=None, probs=None, headers=None, parent=None):
        super().__init__(parent)
        self._values = values
        self._probs = probs
        self.__probInd = None
        if values is not None:
            assert len(self._values) == len(self._probs) != 0
            sizes = {len(x) for c in (values, probs) for x in c}
            assert len(sizes) == 1
            self.__columnCount = len(values)
            self.__rowCount = sizes.pop()
            if headers is None:
                headers = [None] * self.__columnCount
        else:
            assert probs is None
            assert headers is None
            self.__columnCount = self.__rowCount = 0
        self._header = headers

    def rowCount(self, parent=QModelIndex()):
        return 0 if parent.isValid() else self.__rowCount

    def columnCount(self, parent=QModelIndex()):
        return 0 if parent.isValid() else self.__columnCount

    def data(self, index, role=Qt.DisplayRole):
        if role in (Qt.DisplayRole, Qt.EditRole):
            column = index.column()
            row = self.mapToSourceRows(index.row())
            return self._values[column][row], self._probs[column][row]
        return None

    def headerData(self, section, orientation, role=Qt.DisplayRole):
        if role == Qt.DisplayRole:
            if orientation == Qt.Vertical:
                return str(section + 1)
            elif self._header is not None and section < len(self._header):
                return self._header[section]
        return None

    def setProbInd(self, indices):
        self.__probInd = indices
        self.sort(self.sortColumn())

    def sortColumnData(self, column):
        values = self._values[column]
        probs = self._probs[column]
        # Let us assume that probs can be None, numpy array or list of arrays
        if probs is not None and len(probs) and len(probs[0]) \
                and self.__probInd is not None and len(self.__probInd):
            return probs[:, self.__probInd]
        else:
            return values

    def sort(self, column, order=Qt.AscendingOrder):
        super().sort(column, order)
        self.list_sorted.emit()


# PredictionsModel and DataModel have the same signal and sort method, but
# extracting it into a common base class would require diamond inheritance
# because they're derived from different classes. It's not worth it.
class DataModel(TableModel):
    list_sorted = pyqtSignal()

    def sort(self, column, order=Qt.AscendingOrder):
        super().sort(column, order)
        self.list_sorted.emit()


class SharedSelectionStore:
    """
    An object shared between multiple selection models

    The object assumes that the underlying models are AbstractSortTableModel.
    Internally, the object stores indices of unmapped, source rows (as int).

    The class implements method `select` with the same signature as
    QItemSelectionModel.select. Selection models that share this object
    must call this method. After changing the selection, this method will
    call `emit_selection_rows_changed` of all selection models, so they
    can emit the signal selectionChanged.
    """
    def __init__(self, model):
        # unmapped indices of selected rows
        self._rows: Set[int] = set()
        self.model: AbstractSortTableModel = model
        self._selection_models: List[SharedSelectionModel] = []

    @property
    def rows(self) -> Set[int]:
        """Indices of selected rows in the source model"""
        return self._rows

    def register(self, selection_model):
        """
        Add a selection mode to the list of models

        Args:
            selection_model (SharedSelectionModel): a new model
        """
        self._selection_models.append(selection_model)

    def unregister(self, selection_model):
        """
        Remove a selection mode to the list of models

        Args:
            selection_model (SharedSelectionModel): a model to remove
        """
        if selection_model in self._selection_models:
            self._selection_models.remove(selection_model)

    def select(self, selection: Union[QModelIndex, QItemSelection], flags: int):
        """
        (De)Select given rows

        Args:
            selection (QModelIndex or QItemSelection):
                rows to select; indices are mapped to rows in the view
            flags (QItemSelectionModel.SelectionFlags):
                flags that tell whether to Clear, Select, Deselect or Toggle
        """
        rows = set()
        if isinstance(selection, QModelIndex):
            if selection.model() is not None:
                rows = {selection.model().mapToSourceRows(selection.row())}
        else:
            indices = selection.indexes()
            if indices:
                map_to = indices[0].model().mapToSourceRows
                rows = set(map_to([index.row() for index in indices]))
        self.select_rows(rows, flags)

    def select_rows(self, rows: Set[int], flags):
        """
        (De)Select given rows

        Args:
            selection (set of int):
                rows to select; indices refer to unmapped rows in model, not view
            flags (QItemSelectionModel.SelectionFlags):
                flags that tell whether to Clear, Select, Deselect or Toggle
        """
        with self._emit_changed():
            if flags & QItemSelectionModel.Clear:
                self._rows.clear()
            if flags & QItemSelectionModel.Select:
                self._rows |= rows
            if flags & QItemSelectionModel.Deselect:
                self._rows -= rows
            if flags & QItemSelectionModel.Toggle:
                self._rows ^= rows

    def clear_selection(self):
        """Clear selection and emit changeSelection signal to all models"""
        with self._emit_changed():
            self._rows.clear()

    def reset(self):
        """Clear selection without emiting a signal,"""
        self._rows.clear()

    @contextmanager
    def _emit_changed(self):
        """
        A context manager that calls `emit_selection_rows_changed after
        changing a selection.
        """
        def map_from_source(rows):
            return self.model.mapFromSourceRows(list(rows))

        old_rows = self._rows.copy()
        try:
            yield
        finally:
            if self.model.rowCount() != 0:
                deselected = map_from_source(old_rows - self._rows)
                selected = map_from_source(self._rows - old_rows)
                if len(selected) != 0 or len(deselected) != 0:
                    for model in self._selection_models:
                        model.emit_selection_rows_changed(selected, deselected)


class SharedSelectionModel(QItemSelectionModel):
    """
    A selection model that shares the selection with its peers.

    It assumes that the underlying model is a AbstractTableModel.
    """
    def __init__(self, shared_store, model, parent):
        super().__init__(model, parent)
        self.store: SharedSelectionStore = shared_store
        self.store.register(self)

    def select(self, selection, flags):
        self.store.select(selection, flags)

    def selection_from_rows(self, rows: Sequence[int]) -> QItemSelection:
        """
        Return selection across all columns for given row indices (as ints)

        Args:
            rows (sequence of int): row indices, as shown in the view, not model

        Returns: QItemSelection
        """
        model = self.model()
        index = model.index
        last_col = model.columnCount() - 1
        sel = QItemSelection()
        for row in rows:
            sel.select(index(row, 0), index(row, last_col))
        return sel

    def emit_selection_rows_changed(
            self, selected: Sequence[int], deselected: Sequence[int]):
        """
        Given a sequence of indices of selected and deselected rows,
        emit a selectionChanged signal.

        Args:
            selected (Sequence[int]): indices of selected rows
            deselected (Sequence[int]): indices of deselected rows
        """
        self.selectionChanged.emit(
            self.selection_from_rows(selected),
            self.selection_from_rows(deselected))

    def selection(self):
        rows = self.model().mapFromSourceRows(list(self.store.rows))
        return self.selection_from_rows(rows)

    def hasSelection(self) -> bool:
        return bool(self.store.rows)

    def isColumnSelected(self, *_) -> bool:
        return len(self.store.rows) == self.model().rowCount()

    def isRowSelected(self, row, _parent=None) -> bool:
        mapped_row = self.model().mapToSourceRows(row)
        return mapped_row in self.store.rows

    rowIntersectsSelection = isRowSelected

    def isSelected(self, index) -> bool:
        return self.model().mapToSourceRows(index.row()) in self.store.rows

    def selectedColumns(self, row: int):
        if self.isColumnSelected():
            index = self.model().index
            return [index(row, col)
                    for col in range(self.model().columnCount())]
        else:
            return []

    def _selected_rows_arr(self):
        return numpy.fromiter(self.store.rows, int, len(self.store.rows))

    def selectedRows(self, col: int):
        index = self.model().index
        rows = self.model().mapFromSourceRows(self._selected_rows_arr())
        return [index(row, col) for row in rows]

    def selectedIndexes(self):
        index = self.model().index
        rows = self.model().mapFromSourceRows(self._selected_rows_arr())
        return [index(row, col)
                for col in range(self.model().columnCount())
                for row in rows]

    def clearSelection(self):
        self.store.clear_selection()

    def reset(self):
        self.store.reset()
        self.clearCurrentIndex()


class TableView(gui.HScrollStepMixin, QTableView):
    MaxSizeHintSamples = 1000

    def sizeHintForColumn(self, column):
        """
        Reimplemented from `QTableView.sizeHintForColumn`

        Note: This does not match the QTableView's implementation,
        in particular size hints from editor/index widgets are not taken
        into account.

        Parameters
        ----------
        column : int
        """
        # This is probably not needed in Qt5?
        if self.model() is None:
            return -1

        self.ensurePolished()
        model = self.model()
        vheader = self.verticalHeader()
        top = vheader.visualIndexAt(0)
        bottom = vheader.visualIndexAt(self.viewport().height())
        if bottom < 0:
            bottom = self.model().rowCount()

        options = self.viewOptions()
        options.widget = self

        width = 0
        sample_count = 0

        for row in range(top, bottom):
            if not vheader.isSectionHidden(vheader.logicalIndex(row)):
                index = model.index(row, column)
                delegate = self.itemDelegate(index)
                if not delegate:
                    continue
                size = delegate.sizeHint(options, index)
                width = max(size.width(), width)
                sample_count += 1

            if sample_count >= TableView.MaxSizeHintSamples:
                break

        return width + 1 if self.showGrid() else width


def tool_tip(value):
    value, dist = value
    if dist is not None:
        return "{!s} {!s}".format(value, dist)
    else:
        return str(value)


if __name__ == "__main__":  # pragma: no cover
    filename = "iris.tab"
    iris = Orange.data.Table(filename)
    iris2 = iris[:100]

    def pred_error(data, *args, **kwargs):
        raise ValueError

    pred_error.domain = iris.domain
    pred_error.name = "To err is human"

    if iris.domain.has_discrete_class:
        idom = iris.domain
        dom = Domain(
            idom.attributes,
            DiscreteVariable(idom.class_var.name, idom.class_var.values[1::-1])
        )
        iris2 = iris2.transform(dom)
        predictors_ = [
            Orange.classification.SVMLearner(probability=True)(iris2),
            Orange.classification.LogisticRegressionLearner()(iris),
            pred_error
        ]
    elif iris.domain.has_continuous_class:
        predictors_ = [
            Orange.regression.RidgeRegressionLearner(alpha=1.0)(iris),
            Orange.regression.LinearRegressionLearner()(iris),
            pred_error
        ]
    else:
        predictors_ = [pred_error]

    WidgetPreview(OWPredictions).run(
        set_data=iris2,
        insert_predictor=list(enumerate(predictors_)))

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
[ISSUE]
**Title:** AttributeError When Scoring Predictions with Missing Target Values

**Description:**
When attempting to score predictions using a dataset that includes instances with missing target values, the application encounters an `AttributeError`. Specifically, the system tries to access a `missing_targets` attribute in the `Warning` object, which does not exist.

**Example Code:**
```python
import numpy as np
from Orange.classification import LogisticRegressionLearner
from Orange.widgets.evaluate.owpredictions import OWPredictions

# Setup widget and data with missing targets
widget = OWPredictions()
data = some_dataset  # Assume this is a valid Orange data table
mask = np.zeros(len(data), dtype=bool)
mask[::2] = True
train_data = data[~mask]
predict_data = data[mask]
model = LogisticRegressionLearner()(train_data)

# Send signals to the widget
widget.send_signal(widget.Inputs.predictors, model)
widget.send_signal(widget.Inputs.data, predict_data)

# Attempt to check for missing targets warning
# This line raises AttributeError
widget.Warning.missing_targets.is_shown()
```

**Expected Behavior:**
The widget should gracefully handle instances with missing target values by ignoring these rows during scoring and displaying a warning to inform the user about the ignored instances.

**Actual Behavior:**
An `AttributeError` is raised because the `Warning` object does not have a `missing_targets` attribute, preventing the application from informing the user about the missing targets.

[/ISSUE]
{
    "TestOWPredictions.test_bad_data": "PASSED",
    "TestOWPredictions.test_changed_class_var": "PASSED",
    "TestOWPredictions.test_colors_continuous": "PASSED",
    "TestOWPredictions.test_colors_diff_domain": "PASSED",
    "TestOWPredictions.test_colors_same_domain": "PASSED",
    "TestOWPredictions.test_continuous_class": "PASSED",
    "TestOWPredictions.test_image_export": "PASSED",
    "TestOWPredictions.test_minimum_size": "PASSED",
    "TestOWPredictions.test_mismatching_targets": "PASSED",
    "TestOWPredictions.test_missing_target_cls": "PASSED",
    "TestOWPredictions.test_missing_target_reg": "PASSED",
    "TestOWPredictions.test_msg_base_class": "PASSED",
    "TestOWPredictions.test_multi_inputs": "PASSED",
    "TestOWPredictions.test_nan_target_input": "PASSED",
    "TestOWPredictions.test_no_class_on_test": "PASSED",
    "TestOWPredictions.test_no_values_target": "PASSED",
    "TestOWPredictions.test_predictor_fails": "PASSED",
    "TestOWPredictions.test_reset_no_data": "PASSED",
    "TestOWPredictions.test_rowCount_from_model": "PASSED",
    "TestOWPredictions.test_select": "PASSED",
    "TestOWPredictions.test_select_data_first": "PASSED",
    "TestOWPredictions.test_selection_in_setting": "PASSED",
    "TestOWPredictions.test_sort_matching": "PASSED",
    "TestOWPredictions.test_sort_predictions": "PASSED",
    "TestOWPredictions.test_unique_output_domain": "PASSED",
    "TestOWPredictions.test_unregister_prediction_model": "PASSED",
    "SharedSelectionStoreTest.test_clear": "PASSED",
    "SharedSelectionStoreTest.test_emit_changed_maps_to_proxy": "PASSED",
    "SharedSelectionStoreTest.test_registration": "PASSED",
    "SharedSelectionStoreTest.test_reset": "PASSED",
    "SharedSelectionStoreTest.test_select_maps_from_proxy": "PASSED",
    "SharedSelectionStoreTest.test_select_rows": "PASSED",
    "SharedSelectionModelTest.test_emit_selection_rows_changed": "PASSED",
    "SharedSelectionModelTest.test_methods": "PASSED",
    "SharedSelectionModelTest.test_select": "PASSED",
    "SharedSelectionModelTest.test_selection_from_rows": "PASSED",
    "PredictionsModelTest.test_model_classification": "PASSED",
    "PredictionsModelTest.test_model_empty": "PASSED",
    "PredictionsModelTest.test_model_header": "PASSED",
    "PredictionsModelTest.test_model_regression": "PASSED",
    "PredictionsModelTest.test_sorting_classification": "PASSED",
    "PredictionsModelTest.test_sorting_regression": "PASSED"
}
diff --git a/Orange/widgets/evaluate/owpredictions.py b/Orange/widgets/evaluate/owpredictions.py
index 470c92b80..99b6df1bd 100644
--- a/Orange/widgets/evaluate/owpredictions.py
+++ b/Orange/widgets/evaluate/owpredictions.py
@@ -65,6 +65,8 @@ class OWPredictions(OWWidget):
         empty_data = Msg("Empty dataset")
         wrong_targets = Msg(
             "Some model(s) predict a different target (see more ...)\n{}")
+        missing_targets = Msg("Instances with missing targets "
+                              "are ignored while scoring.")
 
     class Error(OWWidget.Error):
         predictor_failed = Msg("Some predictor(s) failed (see more ...)\n{}")
@@ -291,19 +293,37 @@ class OWPredictions(OWWidget):
                 continue
             row = [QStandardItem(learner_name(pred.predictor)),
                    QStandardItem("N/A"), QStandardItem("N/A")]
+
+            actual = results.actual
+            predicted = results.predicted
+            probabilities = results.probabilities
+
+            mask = numpy.isnan(results.actual)
+            no_targets = mask.sum() == len(results.actual)
+            results.actual = results.actual[~mask]
+            results.predicted = results.predicted[:, ~mask]
+            results.probabilities = results.probabilities[:, ~mask]
+
             for scorer in scorers:
                 item = QStandardItem()
-                try:
-                    score = scorer_caller(scorer, results)()[0]
-                    item.setText(f"{score:.3f}")
-                except Exception as exc:  # pylint: disable=broad-except
-                    item.setToolTip(str(exc))
-                    # false pos.; pylint: disable=unsupported-membership-test
-                    if scorer.name in self.score_table.shown_scores:
-                        errors.append(str(exc))
+                if no_targets:
+                    item.setText("NA")
+                else:
+                    try:
+                        score = scorer_caller(scorer, results)()[0]
+                        item.setText(f"{score:.3f}")
+                    except Exception as exc:  # pylint: disable=broad-except
+                        item.setToolTip(str(exc))
+                        # false pos.; pylint: disable=unsupported-membership-test
+                        if scorer.name in self.score_table.shown_scores:
+                            errors.append(str(exc))
                 row.append(item)
             self.score_table.model.appendRow(row)
 
+            results.actual = actual
+            results.predicted = predicted
+            results.probabilities = probabilities
+
         view = self.score_table.view
         if model.rowCount():
             view.setVisible(True)
@@ -332,8 +352,12 @@ class OWPredictions(OWWidget):
                              if isinstance(p.results, Results)
                              and p.results.probabilities is None))
             self.Warning.wrong_targets(inv_targets, shown=bool(inv_targets))
+
+            show_warning = numpy.isnan(self.data.Y).any() and self.predictors
+            self.Warning.missing_targets(shown=show_warning)
         else:
             self.Warning.wrong_targets.clear()
+            self.Warning.missing_targets.clear()
 
     def _get_details(self):
         details = "Data:<br>"

{"Orange/widgets/evaluate/owpredictions.py": "from contextlib import contextmanager\nfrom functools import partial\nfrom operator import itemgetter\nfrom itertools import chain\nfrom typing import Set, Sequence, Union, Optional, List, NamedTuple\n\nimport numpy\nfrom AnyQt.QtWidgets import (\n    QTableView, QListWidget, QSplitter, QToolTip, QStyle, QApplication,\n    QSizePolicy\n)\nfrom AnyQt.QtGui import QPainter, QStandardItem, QPen, QColor\nfrom AnyQt.QtCore import (\n    Qt, QSize, QRect, QRectF, QPoint, QLocale,\n    QModelIndex, pyqtSignal, QTimer,\n    QItemSelectionModel, QItemSelection)\n\nfrom orangewidget.report import plural\nfrom orangewidget.utils.itemmodels import AbstractSortTableModel\n\nimport Orange\nfrom Orange.evaluation import Results\nfrom Orange.base import Model\nfrom Orange.data import ContinuousVariable, DiscreteVariable, Domain\nfrom Orange.data.table import DomainTransformationError\nfrom Orange.data.util import get_unique_names\nfrom Orange.widgets import gui, settings\nfrom Orange.widgets.evaluate.utils import (\n    ScoreTable, usable_scorers, learner_name, scorer_caller)\nfrom Orange.widgets.utils.widgetpreview import WidgetPreview\nfrom Orange.widgets.widget import OWWidget, Msg, Input, Output, MultiInput\nfrom Orange.widgets.utils.itemmodels import TableModel\nfrom Orange.widgets.utils.sql import check_sql_input\nfrom Orange.widgets.utils.state_summary import format_summary_details\nfrom Orange.widgets.utils.colorpalettes import LimitedDiscretePalette\nfrom Orange.widgets.utils.itemdelegates import TableDataDelegate\n\n# Input slot for the Predictors channel\nPredictorSlot = NamedTuple(\n    \"PredictorSlot\", [\n        (\"predictor\", Model),  # The `Model` instance\n        (\"name\", str),       # Predictor name\n        (\"results\", Optional[Results]),    # Computed prediction results or None.\n])\n\n\nclass OWPredictions(OWWidget):\n    name = \"Predictions\"\n    icon = \"icons/Predictions.svg\"\n    priority = 200\n    description = \"Display predictions of models for an input dataset.\"\n    keywords = []\n\n    buttons_area_orientation = None\n\n    class Inputs:\n        data = Input(\"Data\", Orange.data.Table)\n        predictors = MultiInput(\"Predictors\", Model, filter_none=True)\n\n    class Outputs:\n        predictions = Output(\"Predictions\", Orange.data.Table)\n        evaluation_results = Output(\"Evaluation Results\", Results)\n\n    class Warning(OWWidget.Warning):\n        empty_data = Msg(\"Empty dataset\")\n        wrong_targets = Msg(\n            \"Some model(s) predict a different target (see more ...)\\n{}\")\n\n    class Error(OWWidget.Error):\n        predictor_failed = Msg(\"Some predictor(s) failed (see more ...)\\n{}\")\n        scorer_failed = Msg(\"Some scorer(s) failed (see more ...)\\n{}\")\n\n    settingsHandler = settings.ClassValuesContextHandler()\n    score_table = settings.SettingProvider(ScoreTable)\n\n    #: List of selected class value indices in the `class_values` list\n    selected_classes = settings.ContextSetting([])\n    selection = settings.Setting([], schema_only=True)\n\n    def __init__(self):\n        super().__init__()\n\n        self.data = None  # type: Optional[Orange.data.Table]\n        self.predictors = []  # type: List[PredictorSlot]\n        self.class_values = []  # type: List[str]\n        self._delegates = []\n        self.left_width = 10\n        self.selection_store = None\n        self.__pending_selection = self.selection\n\n        controlBox = gui.vBox(self.controlArea, \"Show probabilities for\")\n\n        gui.listBox(controlBox, self, \"selected_classes\", \"class_values\",\n                    callback=self._update_prediction_delegate,\n                    selectionMode=QListWidget.ExtendedSelection,\n                    sizePolicy=(QSizePolicy.Preferred, QSizePolicy.MinimumExpanding),\n                    sizeHint=QSize(1, 350),\n                    minimumHeight=100)\n        self.reset_button = gui.button(\n            controlBox, self, \"Restore Original Order\",\n            callback=self._reset_order,\n            tooltip=\"Show rows in the original order\")\n\n        table_opts = dict(horizontalScrollBarPolicy=Qt.ScrollBarAlwaysOn,\n                          horizontalScrollMode=QTableView.ScrollPerPixel,\n                          selectionMode=QTableView.ExtendedSelection,\n                          focusPolicy=Qt.StrongFocus)\n        self.dataview = TableView(\n            sortingEnabled=True,\n            verticalScrollBarPolicy=Qt.ScrollBarAlwaysOn,\n            **table_opts)\n        self.predictionsview = TableView(\n            sortingEnabled=True,\n            verticalScrollBarPolicy=Qt.ScrollBarAlwaysOff,\n            **table_opts)\n        self.dataview.verticalHeader().hide()\n        dsbar = self.dataview.verticalScrollBar()\n        psbar = self.predictionsview.verticalScrollBar()\n        psbar.valueChanged.connect(dsbar.setValue)\n        dsbar.valueChanged.connect(psbar.setValue)\n\n        self.dataview.verticalHeader().setDefaultSectionSize(22)\n        self.predictionsview.verticalHeader().setDefaultSectionSize(22)\n        self.dataview.verticalHeader().sectionResized.connect(\n            lambda index, _, size:\n            self.predictionsview.verticalHeader().resizeSection(index, size))\n\n        self.dataview.setItemDelegate(DataItemDelegate(self.dataview))\n\n        self.splitter = QSplitter(\n            orientation=Qt.Horizontal, childrenCollapsible=False, handleWidth=2)\n        self.splitter.splitterMoved.connect(self.splitter_resized)\n        self.splitter.addWidget(self.predictionsview)\n        self.splitter.addWidget(self.dataview)\n\n        self.score_table = ScoreTable(self)\n        self.vsplitter = gui.vBox(self.mainArea)\n        self.vsplitter.layout().addWidget(self.splitter)\n        self.vsplitter.layout().addWidget(self.score_table.view)\n\n    def get_selection_store(self, model):\n        # Both models map the same, so it doesn't matter which one is used\n        # to initialize SharedSelectionStore\n        if self.selection_store is None:\n            self.selection_store = SharedSelectionStore(model)\n        return self.selection_store\n\n    @Inputs.data\n    @check_sql_input\n    def set_data(self, data):\n        self.Warning.empty_data(shown=data is not None and not data)\n        self.data = data\n        self.selection_store = None\n        if not data:\n            self.dataview.setModel(None)\n            self.predictionsview.setModel(None)\n        else:\n            # force full reset of the view's HeaderView state\n            self.dataview.setModel(None)\n            model = DataModel(data, parent=None)\n            self.dataview.setModel(model)\n            sel_model = SharedSelectionModel(\n                self.get_selection_store(model), model, self.dataview)\n            self.dataview.setSelectionModel(sel_model)\n            if self.__pending_selection is not None:\n                self.selection = self.__pending_selection\n                self.__pending_selection = None\n                self.selection_store.select_rows(\n                    set(self.selection), QItemSelectionModel.ClearAndSelect)\n            sel_model.selectionChanged.connect(self.commit)\n            sel_model.selectionChanged.connect(self._store_selection)\n\n            self.dataview.model().list_sorted.connect(\n                partial(\n                    self._update_data_sort_order, self.dataview,\n                    self.predictionsview))\n\n        self._invalidate_predictions()\n\n    def _store_selection(self):\n        self.selection = list(self.selection_store.rows)\n\n    @property\n    def class_var(self):\n        return self.data and self.data.domain.class_var\n\n    @Inputs.predictors\n    def set_predictor(self, index, predictor: Model):\n        item = self.predictors[index]\n        self.predictors[index] = item._replace(\n            predictor=predictor, name=predictor.name, results=None\n        )\n\n    @Inputs.predictors.insert\n    def insert_predictor(self, index, predictor: Model):\n        item = PredictorSlot(predictor, predictor.name, None)\n        self.predictors.insert(index, item)\n\n    @Inputs.predictors.remove\n    def remove_predictor(self, index):\n        self.predictors.pop(index)\n\n    def _set_class_values(self):\n        class_values = []\n        for slot in self.predictors:\n            class_var = slot.predictor.domain.class_var\n            if class_var and class_var.is_discrete:\n                for value in class_var.values:\n                    if value not in class_values:\n                        class_values.append(value)\n\n        if self.class_var and self.class_var.is_discrete:\n            values = self.class_var.values\n            self.class_values = sorted(\n                class_values, key=lambda val: val not in values)\n            self.selected_classes = [\n                i for i, name in enumerate(class_values) if name in values]\n        else:\n            self.class_values = class_values  # This assignment updates listview\n            self.selected_classes = []\n\n    def handleNewSignals(self):\n        # Disconnect the model: the model and the delegate will be inconsistent\n        # between _set_class_values and update_predictions_model.\n        self.predictionsview.setModel(None)\n        self._set_class_values()\n        self._call_predictors()\n        self._update_scores()\n        self._update_predictions_model()\n        self._update_prediction_delegate()\n        self._set_errors()\n        self.commit()\n\n    def _call_predictors(self):\n        if not self.data:\n            return\n        if self.class_var:\n            domain = self.data.domain\n            classless_data = self.data.transform(\n                Domain(domain.attributes, None, domain.metas))\n        else:\n            classless_data = self.data\n\n        for index, slot in enumerate(self.predictors):\n            if isinstance(slot.results, Results):\n                continue\n\n            predictor = slot.predictor\n            try:\n                if predictor.domain.class_var.is_discrete:\n                    pred, prob = predictor(classless_data, Model.ValueProbs)\n                else:\n                    pred = predictor(classless_data, Model.Value)\n                    prob = numpy.zeros((len(pred), 0))\n            except (ValueError, DomainTransformationError) as err:\n                self.predictors[index] = \\\n                    slot._replace(results=f\"{predictor.name}: {err}\")\n                continue\n\n            results = Results()\n            results.data = self.data\n            results.domain = self.data.domain\n            results.row_indices = numpy.arange(len(self.data))\n            results.folds = (Ellipsis, )\n            results.actual = self.data.Y\n            results.unmapped_probabilities = prob\n            results.unmapped_predicted = pred\n            results.probabilities = results.predicted = None\n            self.predictors[index] = slot._replace(results=results)\n\n            target = predictor.domain.class_var\n            if target != self.class_var:\n                continue\n\n            if target is not self.class_var and target.is_discrete:\n                backmappers, n_values = predictor.get_backmappers(self.data)\n                prob = predictor.backmap_probs(prob, n_values, backmappers)\n                pred = predictor.backmap_value(pred, prob, n_values, backmappers)\n            results.predicted = pred.reshape((1, len(self.data)))\n            results.probabilities = prob.reshape((1,) + prob.shape)\n\n    def _update_scores(self):\n        model = self.score_table.model\n        model.clear()\n        scorers = usable_scorers(self.class_var) if self.class_var else []\n        self.score_table.update_header(scorers)\n        errors = []\n        for pred in self.predictors:\n            results = pred.results\n            if not isinstance(results, Results) or results.predicted is None:\n                continue\n            row = [QStandardItem(learner_name(pred.predictor)),\n                   QStandardItem(\"N/A\"), QStandardItem(\"N/A\")]\n            for scorer in scorers:\n                item = QStandardItem()\n                try:\n                    score = scorer_caller(scorer, results)()[0]\n                    item.setText(f\"{score:.3f}\")\n                except Exception as exc:  # pylint: disable=broad-except\n                    item.setToolTip(str(exc))\n                    # false pos.; pylint: disable=unsupported-membership-test\n                    if scorer.name in self.score_table.shown_scores:\n                        errors.append(str(exc))\n                row.append(item)\n            self.score_table.model.appendRow(row)\n\n        view = self.score_table.view\n        if model.rowCount():\n            view.setVisible(True)\n            view.ensurePolished()\n            view.setFixedHeight(\n                5 + view.horizontalHeader().height() +\n                view.verticalHeader().sectionSize(0) * model.rowCount())\n        else:\n            view.setVisible(False)\n\n        self.Error.scorer_failed(\"\\n\".join(errors), shown=bool(errors))\n\n    def _set_errors(self):\n        # Not all predictors are run every time, so errors can't be collected\n        # in _call_predictors\n        errors = \"\\n\".join(\n            f\"- {p.predictor.name}: {p.results}\"\n            for p in self.predictors\n            if isinstance(p.results, str) and p.results)\n        self.Error.predictor_failed(errors, shown=bool(errors))\n\n        if self.class_var:\n            inv_targets = \"\\n\".join(\n                f\"- {pred.name} predicts '{pred.domain.class_var.name}'\"\n                for pred in (p.predictor for p in self.predictors\n                             if isinstance(p.results, Results)\n                             and p.results.probabilities is None))\n            self.Warning.wrong_targets(inv_targets, shown=bool(inv_targets))\n        else:\n            self.Warning.wrong_targets.clear()\n\n    def _get_details(self):\n        details = \"Data:<br>\"\n        details += format_summary_details(self.data, format=Qt.RichText)\n        details += \"<hr>\"\n        pred_names = [v.name for v in self.predictors]\n        n_predictors = len(self.predictors)\n        if n_predictors:\n            n_valid = len(self._non_errored_predictors())\n            details += plural(\"Model: {number} model{s}\", n_predictors)\n            if n_valid != n_predictors:\n                details += f\" ({n_predictors - n_valid} failed)\"\n            details += \"<ul>\"\n            for name in pred_names:\n                details += f\"<li>{name}</li>\"\n            details += \"</ul>\"\n        else:\n            details += \"Model:<br>No model on input.\"\n        return details\n\n    def _invalidate_predictions(self):\n        for i, pred in enumerate(self.predictors):\n            self.predictors[i] = pred._replace(results=None)\n\n    def _non_errored_predictors(self):\n        return [p for p in self.predictors\n                if isinstance(p.results, Results)]\n\n    def _reordered_probabilities(self, prediction):\n        cur_values = prediction.predictor.domain.class_var.values\n        new_ind = [self.class_values.index(x) for x in cur_values]\n        probs = prediction.results.unmapped_probabilities\n        new_probs = numpy.full(\n            (probs.shape[0], len(self.class_values)), numpy.nan)\n        new_probs[:, new_ind] = probs\n        return new_probs\n\n    def _update_predictions_model(self):\n        headers = []\n        all_values = []\n        all_probs = []\n        for p in self._non_errored_predictors():\n            values = p.results.unmapped_predicted\n            target = p.predictor.domain.class_var\n            if target.is_discrete:\n                # order probabilities in order from Show prob. for\n                prob = self._reordered_probabilities(p)\n                values = numpy.array(target.values)[values.astype(int)]\n            else:\n                prob = numpy.zeros((len(values), 0))\n            all_values.append(values)\n            all_probs.append(prob)\n            headers.append(p.predictor.name)\n\n        if all_values:\n            model = PredictionsModel(all_values, all_probs, headers)\n            model.list_sorted.connect(\n                partial(\n                    self._update_data_sort_order, self.predictionsview,\n                    self.dataview))\n        else:\n            model = None\n\n        if self.selection_store is not None:\n            self.selection_store.unregister(\n                self.predictionsview.selectionModel())\n\n        self.predictionsview.setModel(model)\n        if model is not None:\n            self.predictionsview.setSelectionModel(\n                SharedSelectionModel(self.get_selection_store(model),\n                                     model, self.predictionsview))\n\n        hheader = self.predictionsview.horizontalHeader()\n        hheader.setSortIndicatorShown(False)\n        hheader.setSectionsClickable(True)\n\n    def _update_data_sort_order(self, sort_source_view, sort_dest_view):\n        sort_source_view.horizontalHeader().setSortIndicatorShown(True)\n        sort_dest_view.horizontalHeader().setSortIndicatorShown(False)\n\n        sort_dest = sort_dest_view.model()\n        sort_source = sort_source_view.model()\n        if sort_dest is not None:\n            if sort_source is not None and sort_source.sortColumn() >= 0:\n                sort_dest.setSortIndices(sort_source.mapToSourceRows(...))\n            else:\n                sort_dest.setSortIndices(None)\n        self.commit()\n\n    def _reset_order(self):\n        datamodel = self.dataview.model()\n        predmodel = self.predictionsview.model()\n        if datamodel is not None:\n            datamodel.setSortIndices(None)\n            datamodel.sort(-1)\n        if predmodel is not None:\n            predmodel.setSortIndices(None)\n            predmodel.sort(-1)\n        self.predictionsview.horizontalHeader().setSortIndicatorShown(False)\n        self.dataview.horizontalHeader().setSortIndicatorShown(False)\n\n    def _all_color_values(self):\n        \"\"\"\n        Return list of colors together with their values from all predictors\n        classes. Colors and values are sorted according to the values order\n        for simpler comparison.\n        \"\"\"\n        predictors = self._non_errored_predictors()\n        color_values = [\n            list(zip(*sorted(zip(\n                p.predictor.domain.class_var.colors,\n                p.predictor.domain.class_var.values\n            ), key=itemgetter(1))))\n            for p in predictors if p.predictor.domain.class_var.is_discrete\n        ]\n        return color_values if color_values else [([], [])]\n\n    @staticmethod\n    def _colors_match(colors1, values1, color2, values2):\n        \"\"\"\n        Test whether colors for values match. Colors matches when all\n        values match for shorter list and colors match for shorter list.\n        It is assumed that values will be sorted together with their colors.\n        \"\"\"\n        shorter_length = min(len(colors1), len(color2))\n        return (values1[:shorter_length] == values2[:shorter_length]\n                and (numpy.array(colors1[:shorter_length]) ==\n                     numpy.array(color2[:shorter_length])).all())\n\n    def _get_colors(self):\n        \"\"\"\n        Defines colors for values. If colors match in all models use the union\n        otherwise use standard colors.\n        \"\"\"\n        all_colors_values = self._all_color_values()\n        base_color, base_values = all_colors_values[0]\n        for c, v in all_colors_values[1:]:\n            if not self._colors_match(base_color, base_values, c, v):\n                base_color = []\n                break\n            # replace base_color if longer\n            if len(v) > len(base_color):\n                base_color = c\n                base_values = v\n\n        if len(base_color) != len(self.class_values):\n            return LimitedDiscretePalette(len(self.class_values)).palette\n        # reorder colors to widgets order\n        colors = [None] * len(self.class_values)\n        for c, v in zip(base_color, base_values):\n            colors[self.class_values.index(v)] = c\n        return colors\n\n    def _update_prediction_delegate(self):\n        self._delegates.clear()\n        colors = self._get_colors()\n        for col, slot in enumerate(self.predictors):\n            target = slot.predictor.domain.class_var\n            shown_probs = (\n                () if target.is_continuous else\n                [val if self.class_values[val] in target.values else None\n                 for val in self.selected_classes]\n            )\n            delegate = PredictionsItemDelegate(\n                None if target.is_continuous else self.class_values,\n                colors,\n                shown_probs,\n                target.format_str if target.is_continuous else None,\n                parent=self.predictionsview\n            )\n            # QAbstractItemView does not take ownership of delegates, so we must\n            self._delegates.append(delegate)\n            self.predictionsview.setItemDelegateForColumn(col, delegate)\n            self.predictionsview.setColumnHidden(col, False)\n\n        self.predictionsview.resizeColumnsToContents()\n        self._recompute_splitter_sizes()\n        if self.predictionsview.model() is not None:\n            self.predictionsview.model().setProbInd(self.selected_classes)\n\n    def _recompute_splitter_sizes(self):\n        if not self.data:\n            return\n        view = self.predictionsview\n        self.left_width = \\\n            view.horizontalHeader().length() + view.verticalHeader().width()\n        self._update_splitter()\n\n    def _update_splitter(self):\n        w1, w2 = self.splitter.sizes()\n        self.splitter.setSizes([self.left_width, w1 + w2 - self.left_width])\n\n    def splitter_resized(self):\n        self.left_width = self.splitter.sizes()[0]\n\n    def commit(self):\n        self._commit_predictions()\n        self._commit_evaluation_results()\n\n    def _commit_evaluation_results(self):\n        slots = [p for p in self._non_errored_predictors()\n                 if p.results.predicted is not None]\n        if not slots:\n            self.Outputs.evaluation_results.send(None)\n            return\n\n        nanmask = numpy.isnan(self.data.get_column_view(self.class_var)[0])\n        data = self.data[~nanmask]\n        results = Results(data, store_data=True)\n        results.folds = None\n        results.row_indices = numpy.arange(len(data))\n        results.actual = data.Y.ravel()\n        results.predicted = numpy.vstack(\n            tuple(p.results.predicted[0][~nanmask] for p in slots))\n        if self.class_var and self.class_var.is_discrete:\n            results.probabilities = numpy.array(\n                [p.results.probabilities[0][~nanmask] for p in slots])\n        results.learner_names = [p.name for p in slots]\n        self.Outputs.evaluation_results.send(results)\n\n    def _commit_predictions(self):\n        if not self.data:\n            self.Outputs.predictions.send(None)\n            return\n\n        newmetas = []\n        newcolumns = []\n        for slot in self._non_errored_predictors():\n            if slot.predictor.domain.class_var.is_discrete:\n                self._add_classification_out_columns(slot, newmetas, newcolumns)\n            else:\n                self._add_regression_out_columns(slot, newmetas, newcolumns)\n\n        attrs = list(self.data.domain.attributes)\n        metas = list(self.data.domain.metas)\n        names = [var.name for var in chain(attrs, self.data.domain.class_vars, metas) if var]\n        uniq_newmetas = []\n        for new_ in newmetas:\n            uniq = get_unique_names(names, new_.name)\n            if uniq != new_.name:\n                new_ = new_.copy(name=uniq)\n            uniq_newmetas.append(new_)\n            names.append(uniq)\n\n        metas += uniq_newmetas\n        domain = Orange.data.Domain(attrs, self.class_var, metas=metas)\n        predictions = self.data.transform(domain)\n        if newcolumns:\n            newcolumns = numpy.hstack(\n                [numpy.atleast_2d(cols) for cols in newcolumns])\n            with predictions.unlocked(predictions.metas):\n                predictions.metas[:, -newcolumns.shape[1]:] = newcolumns\n\n        datamodel = self.dataview.model()\n        predmodel = self.predictionsview.model()\n        assert datamodel is not None  # because we have data\n        assert self.selection_store is not None\n        rows = numpy.array(list(self.selection_store.rows))\n        if rows.size:\n            # Reorder rows as they are ordered in view\n            shown_rows = datamodel.mapFromSourceRows(rows)\n            rows = rows[numpy.argsort(shown_rows)]\n            predictions = predictions[rows]\n        elif datamodel.sortColumn() >= 0 \\\n                or predmodel is not None and predmodel.sortColumn() > 0:\n            # No selection: output all, but in the shown order\n            predictions = predictions[datamodel.mapToSourceRows(...)]\n        self.Outputs.predictions.send(predictions)\n\n    @staticmethod\n    def _add_classification_out_columns(slot, newmetas, newcolumns):\n        # Mapped or unmapped predictions?!\n        # Or provide a checkbox so the user decides?\n        pred = slot.predictor\n        name = pred.name\n        values = pred.domain.class_var.values\n        newmetas.append(DiscreteVariable(name=name, values=values))\n        newcolumns.append(slot.results.unmapped_predicted.reshape(-1, 1))\n        newmetas += [ContinuousVariable(name=f\"{name} ({value})\")\n                     for value in values]\n        newcolumns.append(slot.results.unmapped_probabilities)\n\n    @staticmethod\n    def _add_regression_out_columns(slot, newmetas, newcolumns):\n        newmetas.append(ContinuousVariable(name=slot.predictor.name))\n        newcolumns.append(slot.results.unmapped_predicted.reshape((-1, 1)))\n\n    def send_report(self):\n        def merge_data_with_predictions():\n            data_model = self.dataview.model()\n            predictions_view = self.predictionsview\n            predictions_model = predictions_view.model()\n\n            # use ItemDelegate to style prediction values\n            delegates = [predictions_view.itemDelegateForColumn(i)\n                         for i in range(predictions_model.columnCount())]\n\n            # iterate only over visible columns of data's QTableView\n            iter_data_cols = list(filter(lambda x: not self.dataview.isColumnHidden(x),\n                                         range(data_model.columnCount())))\n\n            # print header\n            yield [''] + \\\n                  [predictions_model.headerData(col, Qt.Horizontal, Qt.DisplayRole)\n                   for col in range(predictions_model.columnCount())] + \\\n                  [data_model.headerData(col, Qt.Horizontal, Qt.DisplayRole)\n                   for col in iter_data_cols]\n\n            # print data & predictions\n            for i in range(data_model.rowCount()):\n                yield [data_model.headerData(i, Qt.Vertical, Qt.DisplayRole)] + \\\n                      [delegate.displayText(\n                          predictions_model.data(predictions_model.index(i, j)),\n                          QLocale())\n                       for j, delegate in enumerate(delegates)] + \\\n                      [data_model.data(data_model.index(i, j),\n                                       role=Qt.DisplayRole)\n                       for j in iter_data_cols]\n\n        if self.data:\n            text = self._get_details().replace('\\n', '<br>')\n            if self.selected_classes:\n                text += '<br>Showing probabilities for: '\n                text += ', '. join([self.class_values[i]\n                                    for i in self.selected_classes])\n            self.report_paragraph('Info', text)\n            self.report_table(\"Data & Predictions\", merge_data_with_predictions(),\n                              header_rows=1, header_columns=1)\n\n            self.report_table(\"Scores\", self.score_table.view)\n\n    def resizeEvent(self, event):\n        super().resizeEvent(event)\n        self._update_splitter()\n\n    def showEvent(self, event):\n        super().showEvent(event)\n        QTimer.singleShot(0, self._update_splitter)\n\n\nclass ItemDelegate(TableDataDelegate):\n    def initStyleOption(self, option, index):\n        super().initStyleOption(option, index)\n        if self.parent().selectionModel().isSelected(index):\n            option.state |= QStyle.State_Selected\n        if self.parent().window().isActiveWindow():\n            option.state |= QStyle.State_Active | QStyle.State_HasFocus\n\n\nclass DataItemDelegate(ItemDelegate):\n    pass\n\n\nclass PredictionsItemDelegate(ItemDelegate):\n    \"\"\"\n    A Item Delegate for custom formatting of predictions/probabilities\n    \"\"\"\n    #: Roles supplied by the `PredictionsModel`\n    DefaultRoles = (Qt.DisplayRole, )\n\n    def __init__(\n            self, class_values, colors, shown_probabilities=(),\n            target_format=None, parent=None,\n    ):\n        super().__init__(parent)\n        self.class_values = class_values  # will be None for continuous\n        self.colors = [QColor(*c) for c in colors]\n        self.target_format = target_format  # target format for cont. vars\n        self.shown_probabilities = self.fmt = self.tooltip = None  # set below\n        self.setFormat(shown_probabilities)\n\n    def setFormat(self, shown_probabilities=()):\n        self.shown_probabilities = shown_probabilities\n        if self.class_values is None:\n            # is continuous class\n            self.fmt = f\"{{value:{self.target_format[1:]}}}\"\n        else:\n            self.fmt = \" \\N{RIGHTWARDS ARROW} \".join(\n                [\" : \".join(f\"{{dist[{i}]:.2f}}\" if i is not None else \"-\"\n                            for i in shown_probabilities)]\n                * bool(shown_probabilities)\n                + [\"{value!s}\"])\n        self.tooltip = \"\"\n        if shown_probabilities:\n            val = ', '.join(\n                self.class_values[i] if i is not None else \"-\"\n                for i in shown_probabilities if i is not None\n            )\n            self.tooltip = f\"p({val})\"\n\n    def displayText(self, value, _):\n        try:\n            value, dist = value\n        except ValueError:\n            return \"\"\n        else:\n            return self.fmt.format(value=value, dist=dist)\n\n    def helpEvent(self, event, view, option, index):\n        if self.tooltip is not None:\n            # ... but can be an empty string, so the current tooltip is removed\n            QToolTip.showText(event.globalPos(), self.tooltip, view)\n            return True\n        else:\n            return super().helpEvent(event, view, option, index)\n\n    def initStyleOption(self, option, index):\n        super().initStyleOption(option, index)\n        if self.class_values is None:\n            option.displayAlignment = \\\n                (option.displayAlignment & Qt.AlignVertical_Mask) | \\\n                Qt.AlignRight\n\n    def sizeHint(self, option, index):\n        # reimplemented\n        sh = super().sizeHint(option, index)\n        if option.widget is not None:\n            style = option.widget.style()\n        else:\n            style = QApplication.style()\n        margin = style.pixelMetric(\n            QStyle.PM_FocusFrameHMargin, option, option.widget) + 1\n        metrics = option.fontMetrics\n        height = sh.height() + metrics.leading() + 2 * margin\n        return QSize(sh.width(), height)\n\n    def distribution(self, index):\n        value = self.cachedData(index, Qt.DisplayRole)\n        if isinstance(value, tuple) and len(value) == 2:\n            _, dist = value\n            return dist\n        else:\n            return None\n\n    def paint(self, painter, option, index):\n        dist = self.distribution(index)\n        if dist is None or self.colors is None:\n            super().paint(painter, option, index)\n            return\n\n        if option.widget is not None:\n            style = option.widget.style()\n        else:\n            style = QApplication.style()\n\n        self.initStyleOption(option, index)\n\n        text = option.text\n        metrics = option.fontMetrics\n\n        margin = style.pixelMetric(\n            QStyle.PM_FocusFrameHMargin, option, option.widget) + 1\n        bottommargin = min(margin, 1)\n        rect = option.rect.adjusted(margin, margin, -margin, -bottommargin)\n        option.text = \"\"\n        textrect = style.subElementRect(\n            QStyle.SE_ItemViewItemText, option, option.widget)\n        # Are the margins included in the subElementRect?? -> No!\n        textrect = textrect.adjusted(margin, margin, -margin, -bottommargin)\n        spacing = max(metrics.leading(), 1)\n\n        distheight = rect.height() - metrics.height() - spacing\n        distheight = min(max(distheight, 2), metrics.height())\n        painter.save()\n        painter.setClipRect(option.rect)\n        painter.setFont(option.font)\n        painter.setRenderHint(QPainter.Antialiasing)\n\n        style.drawPrimitive(\n            QStyle.PE_PanelItemViewRow, option, painter, option.widget)\n        style.drawPrimitive(\n            QStyle.PE_PanelItemViewItem, option, painter, option.widget)\n\n        textrect = textrect.adjusted(0, 0, 0, -distheight - spacing)\n        distrect = QRect(\n            textrect.bottomLeft() + QPoint(0, spacing),\n            QSize(rect.width(), distheight))\n        painter.setPen(QPen(Qt.lightGray, 0.3))\n        self.drawDistBar(painter, distrect, dist)\n        painter.restore()\n        if text:\n            option.text = text\n            self.drawViewItemText(style, painter, option, textrect)\n\n    def drawDistBar(self, painter, rect, distribution):\n        painter.save()\n        painter.translate(rect.topLeft())\n        for i in self.shown_probabilities:\n            if i is None:\n                continue\n            dvalue = distribution[i]\n            if not dvalue > 0:  # This also skips nans\n                continue\n            painter.setBrush(self.colors[i])\n            width = rect.width() * dvalue\n            painter.drawRoundedRect(QRectF(0, 0, width, rect.height()), 1, 2)\n            painter.translate(width, 0.0)\n        painter.restore()\n\n\nclass PredictionsModel(AbstractSortTableModel):\n    list_sorted = pyqtSignal()\n\n    def __init__(self, values=None, probs=None, headers=None, parent=None):\n        super().__init__(parent)\n        self._values = values\n        self._probs = probs\n        self.__probInd = None\n        if values is not None:\n            assert len(self._values) == len(self._probs) != 0\n            sizes = {len(x) for c in (values, probs) for x in c}\n            assert len(sizes) == 1\n            self.__columnCount = len(values)\n            self.__rowCount = sizes.pop()\n            if headers is None:\n                headers = [None] * self.__columnCount\n        else:\n            assert probs is None\n            assert headers is None\n            self.__columnCount = self.__rowCount = 0\n        self._header = headers\n\n    def rowCount(self, parent=QModelIndex()):\n        return 0 if parent.isValid() else self.__rowCount\n\n    def columnCount(self, parent=QModelIndex()):\n        return 0 if parent.isValid() else self.__columnCount\n\n    def data(self, index, role=Qt.DisplayRole):\n        if role in (Qt.DisplayRole, Qt.EditRole):\n            column = index.column()\n            row = self.mapToSourceRows(index.row())\n            return self._values[column][row], self._probs[column][row]\n        return None\n\n    def headerData(self, section, orientation, role=Qt.DisplayRole):\n        if role == Qt.DisplayRole:\n            if orientation == Qt.Vertical:\n                return str(section + 1)\n            elif self._header is not None and section < len(self._header):\n                return self._header[section]\n        return None\n\n    def setProbInd(self, indices):\n        self.__probInd = indices\n        self.sort(self.sortColumn())\n\n    def sortColumnData(self, column):\n        values = self._values[column]\n        probs = self._probs[column]\n        # Let us assume that probs can be None, numpy array or list of arrays\n        if probs is not None and len(probs) and len(probs[0]) \\\n                and self.__probInd is not None and len(self.__probInd):\n            return probs[:, self.__probInd]\n        else:\n            return values\n\n    def sort(self, column, order=Qt.AscendingOrder):\n        super().sort(column, order)\n        self.list_sorted.emit()\n\n\n# PredictionsModel and DataModel have the same signal and sort method, but\n# extracting it into a common base class would require diamond inheritance\n# because they're derived from different classes. It's not worth it.\nclass DataModel(TableModel):\n    list_sorted = pyqtSignal()\n\n    def sort(self, column, order=Qt.AscendingOrder):\n        super().sort(column, order)\n        self.list_sorted.emit()\n\n\nclass SharedSelectionStore:\n    \"\"\"\n    An object shared between multiple selection models\n\n    The object assumes that the underlying models are AbstractSortTableModel.\n    Internally, the object stores indices of unmapped, source rows (as int).\n\n    The class implements method `select` with the same signature as\n    QItemSelectionModel.select. Selection models that share this object\n    must call this method. After changing the selection, this method will\n    call `emit_selection_rows_changed` of all selection models, so they\n    can emit the signal selectionChanged.\n    \"\"\"\n    def __init__(self, model):\n        # unmapped indices of selected rows\n        self._rows: Set[int] = set()\n        self.model: AbstractSortTableModel = model\n        self._selection_models: List[SharedSelectionModel] = []\n\n    @property\n    def rows(self) -> Set[int]:\n        \"\"\"Indices of selected rows in the source model\"\"\"\n        return self._rows\n\n    def register(self, selection_model):\n        \"\"\"\n        Add a selection mode to the list of models\n\n        Args:\n            selection_model (SharedSelectionModel): a new model\n        \"\"\"\n        self._selection_models.append(selection_model)\n\n    def unregister(self, selection_model):\n        \"\"\"\n        Remove a selection mode to the list of models\n\n        Args:\n            selection_model (SharedSelectionModel): a model to remove\n        \"\"\"\n        if selection_model in self._selection_models:\n            self._selection_models.remove(selection_model)\n\n    def select(self, selection: Union[QModelIndex, QItemSelection], flags: int):\n        \"\"\"\n        (De)Select given rows\n\n        Args:\n            selection (QModelIndex or QItemSelection):\n                rows to select; indices are mapped to rows in the view\n            flags (QItemSelectionModel.SelectionFlags):\n                flags that tell whether to Clear, Select, Deselect or Toggle\n        \"\"\"\n        rows = set()\n        if isinstance(selection, QModelIndex):\n            if selection.model() is not None:\n                rows = {selection.model().mapToSourceRows(selection.row())}\n        else:\n            indices = selection.indexes()\n            if indices:\n                map_to = indices[0].model().mapToSourceRows\n                rows = set(map_to([index.row() for index in indices]))\n        self.select_rows(rows, flags)\n\n    def select_rows(self, rows: Set[int], flags):\n        \"\"\"\n        (De)Select given rows\n\n        Args:\n            selection (set of int):\n                rows to select; indices refer to unmapped rows in model, not view\n            flags (QItemSelectionModel.SelectionFlags):\n                flags that tell whether to Clear, Select, Deselect or Toggle\n        \"\"\"\n        with self._emit_changed():\n            if flags & QItemSelectionModel.Clear:\n                self._rows.clear()\n            if flags & QItemSelectionModel.Select:\n                self._rows |= rows\n            if flags & QItemSelectionModel.Deselect:\n                self._rows -= rows\n            if flags & QItemSelectionModel.Toggle:\n                self._rows ^= rows\n\n    def clear_selection(self):\n        \"\"\"Clear selection and emit changeSelection signal to all models\"\"\"\n        with self._emit_changed():\n            self._rows.clear()\n\n    def reset(self):\n        \"\"\"Clear selection without emiting a signal,\"\"\"\n        self._rows.clear()\n\n    @contextmanager\n    def _emit_changed(self):\n        \"\"\"\n        A context manager that calls `emit_selection_rows_changed after\n        changing a selection.\n        \"\"\"\n        def map_from_source(rows):\n            return self.model.mapFromSourceRows(list(rows))\n\n        old_rows = self._rows.copy()\n        try:\n            yield\n        finally:\n            if self.model.rowCount() != 0:\n                deselected = map_from_source(old_rows - self._rows)\n                selected = map_from_source(self._rows - old_rows)\n                if len(selected) != 0 or len(deselected) != 0:\n                    for model in self._selection_models:\n                        model.emit_selection_rows_changed(selected, deselected)\n\n\nclass SharedSelectionModel(QItemSelectionModel):\n    \"\"\"\n    A selection model that shares the selection with its peers.\n\n    It assumes that the underlying model is a AbstractTableModel.\n    \"\"\"\n    def __init__(self, shared_store, model, parent):\n        super().__init__(model, parent)\n        self.store: SharedSelectionStore = shared_store\n        self.store.register(self)\n\n    def select(self, selection, flags):\n        self.store.select(selection, flags)\n\n    def selection_from_rows(self, rows: Sequence[int]) -> QItemSelection:\n        \"\"\"\n        Return selection across all columns for given row indices (as ints)\n\n        Args:\n            rows (sequence of int): row indices, as shown in the view, not model\n\n        Returns: QItemSelection\n        \"\"\"\n        model = self.model()\n        index = model.index\n        last_col = model.columnCount() - 1\n        sel = QItemSelection()\n        for row in rows:\n            sel.select(index(row, 0), index(row, last_col))\n        return sel\n\n    def emit_selection_rows_changed(\n            self, selected: Sequence[int], deselected: Sequence[int]):\n        \"\"\"\n        Given a sequence of indices of selected and deselected rows,\n        emit a selectionChanged signal.\n\n        Args:\n            selected (Sequence[int]): indices of selected rows\n            deselected (Sequence[int]): indices of deselected rows\n        \"\"\"\n        self.selectionChanged.emit(\n            self.selection_from_rows(selected),\n            self.selection_from_rows(deselected))\n\n    def selection(self):\n        rows = self.model().mapFromSourceRows(list(self.store.rows))\n        return self.selection_from_rows(rows)\n\n    def hasSelection(self) -> bool:\n        return bool(self.store.rows)\n\n    def isColumnSelected(self, *_) -> bool:\n        return len(self.store.rows) == self.model().rowCount()\n\n    def isRowSelected(self, row, _parent=None) -> bool:\n        mapped_row = self.model().mapToSourceRows(row)\n        return mapped_row in self.store.rows\n\n    rowIntersectsSelection = isRowSelected\n\n    def isSelected(self, index) -> bool:\n        return self.model().mapToSourceRows(index.row()) in self.store.rows\n\n    def selectedColumns(self, row: int):\n        if self.isColumnSelected():\n            index = self.model().index\n            return [index(row, col)\n                    for col in range(self.model().columnCount())]\n        else:\n            return []\n\n    def _selected_rows_arr(self):\n        return numpy.fromiter(self.store.rows, int, len(self.store.rows))\n\n    def selectedRows(self, col: int):\n        index = self.model().index\n        rows = self.model().mapFromSourceRows(self._selected_rows_arr())\n        return [index(row, col) for row in rows]\n\n    def selectedIndexes(self):\n        index = self.model().index\n        rows = self.model().mapFromSourceRows(self._selected_rows_arr())\n        return [index(row, col)\n                for col in range(self.model().columnCount())\n                for row in rows]\n\n    def clearSelection(self):\n        self.store.clear_selection()\n\n    def reset(self):\n        self.store.reset()\n        self.clearCurrentIndex()\n\n\nclass TableView(gui.HScrollStepMixin, QTableView):\n    MaxSizeHintSamples = 1000\n\n    def sizeHintForColumn(self, column):\n        \"\"\"\n        Reimplemented from `QTableView.sizeHintForColumn`\n\n        Note: This does not match the QTableView's implementation,\n        in particular size hints from editor/index widgets are not taken\n        into account.\n\n        Parameters\n        ----------\n        column : int\n        \"\"\"\n        # This is probably not needed in Qt5?\n        if self.model() is None:\n            return -1\n\n        self.ensurePolished()\n        model = self.model()\n        vheader = self.verticalHeader()\n        top = vheader.visualIndexAt(0)\n        bottom = vheader.visualIndexAt(self.viewport().height())\n        if bottom < 0:\n            bottom = self.model().rowCount()\n\n        options = self.viewOptions()\n        options.widget = self\n\n        width = 0\n        sample_count = 0\n\n        for row in range(top, bottom):\n            if not vheader.isSectionHidden(vheader.logicalIndex(row)):\n                index = model.index(row, column)\n                delegate = self.itemDelegate(index)\n                if not delegate:\n                    continue\n                size = delegate.sizeHint(options, index)\n                width = max(size.width(), width)\n                sample_count += 1\n\n            if sample_count >= TableView.MaxSizeHintSamples:\n                break\n\n        return width + 1 if self.showGrid() else width\n\n\ndef tool_tip(value):\n    value, dist = value\n    if dist is not None:\n        return \"{!s} {!s}\".format(value, dist)\n    else:\n        return str(value)\n\n\nif __name__ == \"__main__\":  # pragma: no cover\n    filename = \"iris.tab\"\n    iris = Orange.data.Table(filename)\n    iris2 = iris[:100]\n\n    def pred_error(data, *args, **kwargs):\n        raise ValueError\n\n    pred_error.domain = iris.domain\n    pred_error.name = \"To err is human\"\n\n    if iris.domain.has_discrete_class:\n        idom = iris.domain\n        dom = Domain(\n            idom.attributes,\n            DiscreteVariable(idom.class_var.name, idom.class_var.values[1::-1])\n        )\n        iris2 = iris2.transform(dom)\n        predictors_ = [\n            Orange.classification.SVMLearner(probability=True)(iris2),\n            Orange.classification.LogisticRegressionLearner()(iris),\n            pred_error\n        ]\n    elif iris.domain.has_continuous_class:\n        predictors_ = [\n            Orange.regression.RidgeRegressionLearner(alpha=1.0)(iris),\n            Orange.regression.LinearRegressionLearner()(iris),\n            pred_error\n        ]\n    else:\n        predictors_ = [pred_error]\n\n    WidgetPreview(OWPredictions).run(\n        set_data=iris2,\n        insert_predictor=list(enumerate(predictors_)))\n"}
A user will ask you to solve a task. You should first draft your thinking process (inner monologue). Then, generate the solution.

Your response format must follow the template below:
<think>
Your thoughts or/and draft, like working through an exercise on scratch paper. Be as casual and as long as you want until you are confident to generate a correct solution.
</think>
<solution>
Final solution presented to the user.
</solution>
system
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** AttributeError When Scoring Predictions with Missing Target Values

**Description:**
When attempting to score predictions using a dataset that includes instances with missing target values, the application encounters an `AttributeError`. Specifically, the system tries to access a `missing_targets` attribute in the `Warning` object, which does not exist.

**Example Code:**
```python
import numpy as np
from Orange.classification import LogisticRegressionLearner
from Orange.widgets.evaluate.owpredictions import OWPredictions

# Setup widget and data with missing targets
widget = OWPredictions()
data = some_dataset  # Assume this is a valid Orange data table
mask = np.zeros(len(data), dtype=bool)
mask[::2] = True
train_data = data[~mask]
predict_data = data[mask]
model = LogisticRegressionLearner()(train_data)

# Send signals to the widget
widget.send_signal(widget.Inputs.predictors, model)
widget.send_signal(widget.Inputs.data, predict_data)

# Attempt to check for missing targets warning
# This line raises AttributeError
widget.Warning.missing_targets.is_shown()
```

**Expected Behavior:**
The widget should gracefully handle instances with missing target values by ignoring these rows during scoring and displaying a warning to inform the user about the ignored instances.

**Actual Behavior:**
An `AttributeError` is raised because the `Warning` object does not have a `missing_targets` attribute, preventing the application from informing the user about the missing targets.

[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### Orange/widgets/evaluate/owpredictions.py
from contextlib import contextmanager
from functools import partial
from operator import itemgetter
from itertools import chain
from typing import Set, Sequence, Union, Optional, List, NamedTuple

import numpy
from AnyQt.QtWidgets import (
    QTableView, QListWidget, QSplitter, QToolTip, QStyle, QApplication,
    QSizePolicy
)
from AnyQt.QtGui import QPainter, QStandardItem, QPen, QColor
from AnyQt.QtCore import (
    Qt, QSize, QRect, QRectF, QPoint, QLocale,
    QModelIndex, pyqtSignal, QTimer,
    QItemSelectionModel, QItemSelection)

from orangewidget.report import plural
from orangewidget.utils.itemmodels import AbstractSortTableModel

import Orange
from Orange.evaluation import Results
from Orange.base import Model
from Orange.data import ContinuousVariable, DiscreteVariable, Domain
from Orange.data.table import DomainTransformationError
from Orange.data.util import get_unique_names
from Orange.widgets import gui, settings
from Orange.widgets.evaluate.utils import (
    ScoreTable, usable_scorers, learner_name, scorer_caller)
from Orange.widgets.utils.widgetpreview import WidgetPreview
from Orange.widgets.widget import OWWidget, Msg, Input, Output, MultiInput
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
from Orange.widgets.utils.colorpalettes import LimitedDiscretePalette
from Orange.widgets.utils.itemdelegates import TableDataDelegate

# Input slot for the Predictors channel
PredictorSlot = NamedTuple(
    "PredictorSlot", [
        ("predictor", Model),  # The `Model` instance
        ("name", str),       # Predictor name
        ("results", Optional[Results]),    # Computed prediction results or None.
])


class OWPredictions(OWWidget):
    name = "Predictions"
    icon = "icons/Predictions.svg"
    priority = 200
    description = "Display predictions of models for an input dataset."
    keywords = []

    buttons_area_orientation = None

    class Inputs:
        data = Input("Data", Orange.data.Table)
        predictors = MultiInput("Predictors", Model, filter_none=True)

    class Outputs:
        predictions = Output("Predictions", Orange.data.Table)
        evaluation_results = Output("Evaluation Results", Results)

    class Warning(OWWidget.Warning):
        empty_data = Msg("Empty dataset")
        wrong_targets = Msg(
            "Some model(s) predict a different target (see more ...)\n{}")

    class Error(OWWidget.Error):
        predictor_failed = Msg("Some predictor(s) failed (see more ...)\n{}")
        scorer_failed = Msg("Some scorer(s) failed (see more ...)\n{}")

    settingsHandler = settings.ClassValuesContextHandler()
    score_table = settings.SettingProvider(ScoreTable)

    #: List of selected class value indices in the `class_values` list
    selected_classes = settings.ContextSetting([])
    selection = settings.Setting([], schema_only=True)

    def __init__(self):
        super().__init__()

        self.data = None  # type: Optional[Orange.data.Table]
        self.predictors = []  # type: List[PredictorSlot]
        self.class_values = []  # type: List[str]
        self._delegates = []
        self.left_width = 10
        self.selection_store = None
        self.__pending_selection = self.selection

        controlBox = gui.vBox(self.controlArea, "Show probabilities for")

        gui.listBox(controlBox, self, "selected_classes", "class_values",
                    callback=self._update_prediction_delegate,
                    selectionMode=QListWidget.ExtendedSelection,
                    sizePolicy=(QSizePolicy.Preferred, QSizePolicy.MinimumExpanding),
                    sizeHint=QSize(1, 350),
                    minimumHeight=100)
        self.reset_button = gui.button(
            controlBox, self, "Restore Original Order",
            callback=self._reset_order,
            tooltip="Show rows in the original order")

        table_opts = dict(horizontalScrollBarPolicy=Qt.ScrollBarAlwaysOn,
                          horizontalScrollMode=QTableView.ScrollPerPixel,
                          selectionMode=QTableView.ExtendedSelection,
                          focusPolicy=Qt.StrongFocus)
        self.dataview = TableView(
            sortingEnabled=True,
            verticalScrollBarPolicy=Qt.ScrollBarAlwaysOn,
            **table_opts)
        self.predictionsview = TableView(
            sortingEnabled=True,
            verticalScrollBarPolicy=Qt.ScrollBarAlwaysOff,
            **table_opts)
        self.dataview.verticalHeader().hide()
        dsbar = self.dataview.verticalScrollBar()
        psbar = self.predictionsview.verticalScrollBar()
        psbar.valueChanged.connect(dsbar.setValue)
        dsbar.valueChanged.connect(psbar.setValue)

        self.dataview.verticalHeader().setDefaultSectionSize(22)
        self.predictionsview.verticalHeader().setDefaultSectionSize(22)
        self.dataview.verticalHeader().sectionResized.connect(
            lambda index, _, size:
            self.predictionsview.verticalHeader().resizeSection(index, size))

        self.dataview.setItemDelegate(DataItemDelegate(self.dataview))

        self.splitter = QSplitter(
            orientation=Qt.Horizontal, childrenCollapsible=False, handleWidth=2)
        self.splitter.splitterMoved.connect(self.splitter_resized)
        self.splitter.addWidget(self.predictionsview)
        self.splitter.addWidget(self.dataview)

        self.score_table = ScoreTable(self)
        self.vsplitter = gui.vBox(self.mainArea)
        self.vsplitter.layout().addWidget(self.splitter)
        self.vsplitter.layout().addWidget(self.score_table.view)

    def get_selection_store(self, model):
        # Both models map the same, so it doesn't matter which one is used
        # to initialize SharedSelectionStore
        if self.selection_store is None:
            self.selection_store = SharedSelectionStore(model)
        return self.selection_store

    @Inputs.data
    @check_sql_input
    def set_data(self, data):
        self.Warning.empty_data(shown=data is not None and not data)
        self.data = data
        self.selection_store = None
        if not data:
            self.dataview.setModel(None)
            self.predictionsview.setModel(None)
        else:
            # force full reset of the view's HeaderView state
            self.dataview.setModel(None)
            model = DataModel(data, parent=None)
            self.dataview.setModel(model)
            sel_model = SharedSelectionModel(
                self.get_selection_store(model), model, self.dataview)
            self.dataview.setSelectionModel(sel_model)
            if self.__pending_selection is not None:
                self.selection = self.__pending_selection
                self.__pending_selection = None
                self.selection_store.select_rows(
                    set(self.selection), QItemSelectionModel.ClearAndSelect)
            sel_model.selectionChanged.connect(self.commit)
            sel_model.selectionChanged.connect(self._store_selection)

            self.dataview.model().list_sorted.connect(
                partial(
                    self._update_data_sort_order, self.dataview,
                    self.predictionsview))

        self._invalidate_predictions()

    def _store_selection(self):
        self.selection = list(self.selection_store.rows)

    @property
    def class_var(self):
        return self.data and self.data.domain.class_var

    @Inputs.predictors
    def set_predictor(self, index, predictor: Model):
        item = self.predictors[index]
        self.predictors[index] = item._replace(
            predictor=predictor, name=predictor.name, results=None
        )

    @Inputs.predictors.insert
    def insert_predictor(self, index, predictor: Model):
        item = PredictorSlot(predictor, predictor.name, None)
        self.predictors.insert(index, item)

    @Inputs.predictors.remove
    def remove_predictor(self, index):
        self.predictors.pop(index)

    def _set_class_values(self):
        class_values = []
        for slot in self.predictors:
            class_var = slot.predictor.domain.class_var
            if class_var and class_var.is_discrete:
                for value in class_var.values:
                    if value not in class_values:
                        class_values.append(value)

        if self.class_var and self.class_var.is_discrete:
            values = self.class_var.values
            self.class_values = sorted(
                class_values, key=lambda val: val not in values)
            self.selected_classes = [
                i for i, name in enumerate(class_values) if name in values]
        else:
            self.class_values = class_values  # This assignment updates listview
            self.selected_classes = []

    def handleNewSignals(self):
        # Disconnect the model: the model and the delegate will be inconsistent
        # between _set_class_values and update_predictions_model.
        self.predictionsview.setModel(None)
        self._set_class_values()
        self._call_predictors()
        self._update_scores()
        self._update_predictions_model()
        self._update_prediction_delegate()
        self._set_errors()
        self.commit()

    def _call_predictors(self):
        if not self.data:
            return
        if self.class_var:
            domain = self.data.domain
            classless_data = self.data.transform(
                Domain(domain.attributes, None, domain.metas))
        else:
            classless_data = self.data

        for index, slot in enumerate(self.predictors):
            if isinstance(slot.results, Results):
                continue

            predictor = slot.predictor
            try:
                if predictor.domain.class_var.is_discrete:
                    pred, prob = predictor(classless_data, Model.ValueProbs)
                else:
                    pred = predictor(classless_data, Model.Value)
                    prob = numpy.zeros((len(pred), 0))
            except (ValueError, DomainTransformationError) as err:
                self.predictors[index] = \
                    slot._replace(results=f"{predictor.name}: {err}")
                continue

            results = Results()
            results.data = self.data
            results.domain = self.data.domain
            results.row_indices = numpy.arange(len(self.data))
            results.folds = (Ellipsis, )
            results.actual = self.data.Y
            results.unmapped_probabilities = prob
            results.unmapped_predicted = pred
            results.probabilities = results.predicted = None
            self.predictors[index] = slot._replace(results=results)

            target = predictor.domain.class_var
            if target != self.class_var:
                continue

            if target is not self.class_var and target.is_discrete:
                backmappers, n_values = predictor.get_backmappers(self.data)
                prob = predictor.backmap_probs(prob, n_values, backmappers)
                pred = predictor.backmap_value(pred, prob, n_values, backmappers)
            results.predicted = pred.reshape((1, len(self.data)))
            results.probabilities = prob.reshape((1,) + prob.shape)

    def _update_scores(self):
        model = self.score_table.model
        model.clear()
        scorers = usable_scorers(self.class_var) if self.class_var else []
        self.score_table.update_header(scorers)
        errors = []
        for pred in self.predictors:
            results = pred.results
            if not isinstance(results, Results) or results.predicted is None:
                continue
            row = [QStandardItem(learner_name(pred.predictor)),
                   QStandardItem("N/A"), QStandardItem("N/A")]
            for scorer in scorers:
                item = QStandardItem()
                try:
                    score = scorer_caller(scorer, results)()[0]
                    item.setText(f"{score:.3f}")
                except Exception as exc:  # pylint: disable=broad-except
                    item.setToolTip(str(exc))
                    # false pos.; pylint: disable=unsupported-membership-test
                    if scorer.name in self.score_table.shown_scores:
                        errors.append(str(exc))
                row.append(item)
            self.score_table.model.appendRow(row)

        view = self.score_table.view
        if model.rowCount():
            view.setVisible(True)
            view.ensurePolished()
            view.setFixedHeight(
                5 + view.horizontalHeader().height() +
                view.verticalHeader().sectionSize(0) * model.rowCount())
        else:
            view.setVisible(False)

        self.Error.scorer_failed("\n".join(errors), shown=bool(errors))

    def _set_errors(self):
        # Not all predictors are run every time, so errors can't be collected
        # in _call_predictors
        errors = "\n".join(
            f"- {p.predictor.name}: {p.results}"
            for p in self.predictors
            if isinstance(p.results, str) and p.results)
        self.Error.predictor_failed(errors, shown=bool(errors))

        if self.class_var:
            inv_targets = "\n".join(
                f"- {pred.name} predicts '{pred.domain.class_var.name}'"
                for pred in (p.predictor for p in self.predictors
                             if isinstance(p.results, Results)
                             and p.results.probabilities is None))
            self.Warning.wrong_targets(inv_targets, shown=bool(inv_targets))
        else:
            self.Warning.wrong_targets.clear()

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

    def _invalidate_predictions(self):
        for i, pred in enumerate(self.predictors):
            self.predictors[i] = pred._replace(results=None)

    def _non_errored_predictors(self):
        return [p for p in self.predictors
                if isinstance(p.results, Results)]

    def _reordered_probabilities(self, prediction):
        cur_values = prediction.predictor.domain.class_var.values
        new_ind = [self.class_values.index(x) for x in cur_values]
        probs = prediction.results.unmapped_probabilities
        new_probs = numpy.full(
            (probs.shape[0], len(self.class_values)), numpy.nan)
        new_probs[:, new_ind] = probs
        return new_probs

    def _update_predictions_model(self):
        headers = []
        all_values = []
        all_probs = []
        for p in self._non_errored_predictors():
            values = p.results.unmapped_predicted
            target = p.predictor.domain.class_var
            if target.is_discrete:
                # order probabilities in order from Show prob. for
                prob = self._reordered_probabilities(p)
                values = numpy.array(target.values)[values.astype(int)]
            else:
                prob = numpy.zeros((len(values), 0))
            all_values.append(values)
            all_probs.append(prob)
            headers.append(p.predictor.name)

        if all_values:
            model = PredictionsModel(all_values, all_probs, headers)
            model.list_sorted.connect(
                partial(
                    self._update_data_sort_order, self.predictionsview,
                    self.dataview))
        else:
            model = None

        if self.selection_store is not None:
            self.selection_store.unregister(
                self.predictionsview.selectionModel())

        self.predictionsview.setModel(model)
        if model is not None:
            self.predictionsview.setSelectionModel(
                SharedSelectionModel(self.get_selection_store(model),
                                     model, self.predictionsview))

        hheader = self.predictionsview.horizontalHeader()
        hheader.setSortIndicatorShown(False)
        hheader.setSectionsClickable(True)

    def _update_data_sort_order(self, sort_source_view, sort_dest_view):
        sort_source_view.horizontalHeader().setSortIndicatorShown(True)
        sort_dest_view.horizontalHeader().setSortIndicatorShown(False)

        sort_dest = sort_dest_view.model()
        sort_source = sort_source_view.model()
        if sort_dest is not None:
            if sort_source is not None and sort_source.sortColumn() >= 0:
                sort_dest.setSortIndices(sort_source.mapToSourceRows(...))
            else:
                sort_dest.setSortIndices(None)
        self.commit()

    def _reset_order(self):
        datamodel = self.dataview.model()
        predmodel = self.predictionsview.model()
        if datamodel is not None:
            datamodel.setSortIndices(None)
            datamodel.sort(-1)
        if predmodel is not None:
            predmodel.setSortIndices(None)
            predmodel.sort(-1)
        self.predictionsview.horizontalHeader().setSortIndicatorShown(False)
        self.dataview.horizontalHeader().setSortIndicatorShown(False)

    def _all_color_values(self):
        """
        Return list of colors together with their values from all predictors
        classes. Colors and values are sorted according to the values order
        for simpler comparison.
        """
        predictors = self._non_errored_predictors()
        color_values = [
            list(zip(*sorted(zip(
                p.predictor.domain.class_var.colors,
                p.predictor.domain.class_var.values
            ), key=itemgetter(1))))
            for p in predictors if p.predictor.domain.class_var.is_discrete
        ]
        return color_values if color_values else [([], [])]

    @staticmethod
    def _colors_match(colors1, values1, color2, values2):
        """
        Test whether colors for values match. Colors matches when all
        values match for shorter list and colors match for shorter list.
        It is assumed that values will be sorted together with their colors.
        """
        shorter_length = min(len(colors1), len(color2))
        return (values1[:shorter_length] == values2[:shorter_length]
                and (numpy.array(colors1[:shorter_length]) ==
                     numpy.array(color2[:shorter_length])).all())

    def _get_colors(self):
        """
        Defines colors for values. If colors match in all models use the union
        otherwise use standard colors.
        """
        all_colors_values = self._all_color_values()
        base_color, base_values = all_colors_values[0]
        for c, v in all_colors_values[1:]:
            if not self._colors_match(base_color, base_values, c, v):
                base_color = []
                break
            # replace base_color if longer
            if len(v) > len(base_color):
                base_color = c
                base_values = v

        if len(base_color) != len(self.class_values):
            return LimitedDiscretePalette(len(self.class_values)).palette
        # reorder colors to widgets order
        colors = [None] * len(self.class_values)
        for c, v in zip(base_color, base_values):
            colors[self.class_values.index(v)] = c
        return colors

    def _update_prediction_delegate(self):
        self._delegates.clear()
        colors = self._get_colors()
        for col, slot in enumerate(self.predictors):
            target = slot.predictor.domain.class_var
            shown_probs = (
                () if target.is_continuous else
                [val if self.class_values[val] in target.values else None
                 for val in self.selected_classes]
            )
            delegate = PredictionsItemDelegate(
                None if target.is_continuous else self.class_values,
                colors,
                shown_probs,
                target.format_str if target.is_continuous else None,
                parent=self.predictionsview
            )
            # QAbstractItemView does not take ownership of delegates, so we must
            self._delegates.append(delegate)
            self.predictionsview.setItemDelegateForColumn(col, delegate)
            self.predictionsview.setColumnHidden(col, False)

        self.predictionsview.resizeColumnsToContents()
        self._recompute_splitter_sizes()
        if self.predictionsview.model() is not None:
            self.predictionsview.model().setProbInd(self.selected_classes)

    def _recompute_splitter_sizes(self):
        if not self.data:
            return
        view = self.predictionsview
        self.left_width = \
            view.horizontalHeader().length() + view.verticalHeader().width()
        self._update_splitter()

    def _update_splitter(self):
        w1, w2 = self.splitter.sizes()
        self.splitter.setSizes([self.left_width, w1 + w2 - self.left_width])

    def splitter_resized(self):
        self.left_width = self.splitter.sizes()[0]

    def commit(self):
        self._commit_predictions()
        self._commit_evaluation_results()

    def _commit_evaluation_results(self):
        slots = [p for p in self._non_errored_predictors()
                 if p.results.predicted is not None]
        if not slots:
            self.Outputs.evaluation_results.send(None)
            return

        nanmask = numpy.isnan(self.data.get_column_view(self.class_var)[0])
        data = self.data[~nanmask]
        results = Results(data, store_data=True)
        results.folds = None
        results.row_indices = numpy.arange(len(data))
        results.actual = data.Y.ravel()
        results.predicted = numpy.vstack(
            tuple(p.results.predicted[0][~nanmask] for p in slots))
        if self.class_var and self.class_var.is_discrete:
            results.probabilities = numpy.array(
                [p.results.probabilities[0][~nanmask] for p in slots])
        results.learner_names = [p.name for p in slots]
        self.Outputs.evaluation_results.send(results)

    def _commit_predictions(self):
        if not self.data:
            self.Outputs.predictions.send(None)
            return

        newmetas = []
        newcolumns = []
        for slot in self._non_errored_predictors():
            if slot.predictor.domain.class_var.is_discrete:
                self._add_classification_out_columns(slot, newmetas, newcolumns)
            else:
                self._add_regression_out_columns(slot, newmetas, newcolumns)

        attrs = list(self.data.domain.attributes)
        metas = list(self.data.domain.metas)
        names = [var.name for var in chain(attrs, self.data.domain.class_vars, metas) if var]
        uniq_newmetas = []
        for new_ in newmetas:
            uniq = get_unique_names(names, new_.name)
            if uniq != new_.name:
                new_ = new_.copy(name=uniq)
            uniq_newmetas.append(new_)
            names.append(uniq)

        metas += uniq_newmetas
        domain = Orange.data.Domain(attrs, self.class_var, metas=metas)
        predictions = self.data.transform(domain)
        if newcolumns:
            newcolumns = numpy.hstack(
                [numpy.atleast_2d(cols) for cols in newcolumns])
            with predictions.unlocked(predictions.metas):
                predictions.metas[:, -newcolumns.shape[1]:] = newcolumns

        datamodel = self.dataview.model()
        predmodel = self.predictionsview.model()
        assert datamodel is not None  # because we have data
        assert self.selection_store is not None
        rows = numpy.array(list(self.selection_store.rows))
        if rows.size:
            # Reorder rows as they are ordered in view
            shown_rows = datamodel.mapFromSourceRows(rows)
            rows = rows[numpy.argsort(shown_rows)]
            predictions = predictions[rows]
        elif datamodel.sortColumn() >= 0 \
                or predmodel is not None and predmodel.sortColumn() > 0:
            # No selection: output all, but in the shown order
            predictions = predictions[datamodel.mapToSourceRows(...)]
        self.Outputs.predictions.send(predictions)

    @staticmethod
    def _add_classification_out_columns(slot, newmetas, newcolumns):
        # Mapped or unmapped predictions?!
        # Or provide a checkbox so the user decides?
        pred = slot.predictor
        name = pred.name
        values = pred.domain.class_var.values
        newmetas.append(DiscreteVariable(name=name, values=values))
        newcolumns.append(slot.results.unmapped_predicted.reshape(-1, 1))
        newmetas += [ContinuousVariable(name=f"{name} ({value})")
                     for value in values]
        newcolumns.append(slot.results.unmapped_probabilities)

    @staticmethod
    def _add_regression_out_columns(slot, newmetas, newcolumns):
        newmetas.append(ContinuousVariable(name=slot.predictor.name))
        newcolumns.append(slot.results.unmapped_predicted.reshape((-1, 1)))

    def send_report(self):
        def merge_data_with_predictions():
            data_model = self.dataview.model()
            predictions_view = self.predictionsview
            predictions_model = predictions_view.model()

            # use ItemDelegate to style prediction values
            delegates = [predictions_view.itemDelegateForColumn(i)
                         for i in range(predictions_model.columnCount())]

            # iterate only over visible columns of data's QTableView
            iter_data_cols = list(filter(lambda x: not self.dataview.isColumnHidden(x),
                                         range(data_model.columnCount())))

            # print header
            yield [''] + \
                  [predictions_model.headerData(col, Qt.Horizontal, Qt.DisplayRole)
                   for col in range(predictions_model.columnCount())] + \
                  [data_model.headerData(col, Qt.Horizontal, Qt.DisplayRole)
                   for col in iter_data_cols]

            # print data & predictions
            for i in range(data_model.rowCount()):
                yield [data_model.headerData(i, Qt.Vertical, Qt.DisplayRole)] + \
                      [delegate.displayText(
                          predictions_model.data(predictions_model.index(i, j)),
                          QLocale())
                       for j, delegate in enumerate(delegates)] + \
                      [data_model.data(data_model.index(i, j),
                                       role=Qt.DisplayRole)
                       for j in iter_data_cols]

        if self.data:
            text = self._get_details().replace('\n', '<br>')
            if self.selected_classes:
                text += '<br>Showing probabilities for: '
                text += ', '. join([self.class_values[i]
                                    for i in self.selected_classes])
            self.report_paragraph('Info', text)
            self.report_table("Data & Predictions", merge_data_with_predictions(),
                              header_rows=1, header_columns=1)

            self.report_table("Scores", self.score_table.view)

    def resizeEvent(self, event):
        super().resizeEvent(event)
        self._update_splitter()

    def showEvent(self, event):
        super().showEvent(event)
        QTimer.singleShot(0, self._update_splitter)


class ItemDelegate(TableDataDelegate):
    def initStyleOption(self, option, index):
        super().initStyleOption(option, index)
        if self.parent().selectionModel().isSelected(index):
            option.state |= QStyle.State_Selected
        if self.parent().window().isActiveWindow():
            option.state |= QStyle.State_Active | QStyle.State_HasFocus


class DataItemDelegate(ItemDelegate):
    pass


class PredictionsItemDelegate(ItemDelegate):
    """
    A Item Delegate for custom formatting of predictions/probabilities
    """
    #: Roles supplied by the `PredictionsModel`
    DefaultRoles = (Qt.DisplayRole, )

    def __init__(
            self, class_values, colors, shown_probabilities=(),
            target_format=None, parent=None,
    ):
        super().__init__(parent)
        self.class_values = class_values  # will be None for continuous
        self.colors = [QColor(*c) for c in colors]
        self.target_format = target_format  # target format for cont. vars
        self.shown_probabilities = self.fmt = self.tooltip = None  # set below
        self.setFormat(shown_probabilities)

    def setFormat(self, shown_probabilities=()):
        self.shown_probabilities = shown_probabilities
        if self.class_values is None:
            # is continuous class
            self.fmt = f"{{value:{self.target_format[1:]}}}"
        else:
            self.fmt = " \N{RIGHTWARDS ARROW} ".join(
                [" : ".join(f"{{dist[{i}]:.2f}}" if i is not None else "-"
                            for i in shown_probabilities)]
                * bool(shown_probabilities)
                + ["{value!s}"])
        self.tooltip = ""
        if shown_probabilities:
            val = ', '.join(
                self.class_values[i] if i is not None else "-"
                for i in shown_probabilities if i is not None
            )
            self.tooltip = f"p({val})"

    def displayText(self, value, _):
        try:
            value, dist = value
        except ValueError:
            return ""
        else:
            return self.fmt.format(value=value, dist=dist)

    def helpEvent(self, event, view, option, index):
        if self.tooltip is not None:
            # ... but can be an empty string, so the current tooltip is removed
            QToolTip.showText(event.globalPos(), self.tooltip, view)
            return True
        else:
            return super().helpEvent(event, view, option, index)

    def initStyleOption(self, option, index):
        super().initStyleOption(option, index)
        if self.class_values is None:
            option.displayAlignment = \
                (option.displayAlignment & Qt.AlignVertical_Mask) | \
                Qt.AlignRight

    def sizeHint(self, option, index):
        # reimplemented
        sh = super().sizeHint(option, index)
        if option.widget is not None:
            style = option.widget.style()
        else:
            style = QApplication.style()
        margin = style.pixelMetric(
            QStyle.PM_FocusFrameHMargin, option, option.widget) + 1
        metrics = option.fontMetrics
        height = sh.height() + metrics.leading() + 2 * margin
        return QSize(sh.width(), height)

    def distribution(self, index):
        value = self.cachedData(index, Qt.DisplayRole)
        if isinstance(value, tuple) and len(value) == 2:
            _, dist = value
            return dist
        else:
            return None

    def paint(self, painter, option, index):
        dist = self.distribution(index)
        if dist is None or self.colors is None:
            super().paint(painter, option, index)
            return

        if option.widget is not None:
            style = option.widget.style()
        else:
            style = QApplication.style()

        self.initStyleOption(option, index)

        text = option.text
        metrics = option.fontMetrics

        margin = style.pixelMetric(
            QStyle.PM_FocusFrameHMargin, option, option.widget) + 1
        bottommargin = min(margin, 1)
        rect = option.rect.adjusted(margin, margin, -margin, -bottommargin)
        option.text = ""
        textrect = style.subElementRect(
            QStyle.SE_ItemViewItemText, option, option.widget)
        # Are the margins included in the subElementRect?? -> No!
        textrect = textrect.adjusted(margin, margin, -margin, -bottommargin)
        spacing = max(metrics.leading(), 1)

        distheight = rect.height() - metrics.height() - spacing
        distheight = min(max(distheight, 2), metrics.height())
        painter.save()
        painter.setClipRect(option.rect)
        painter.setFont(option.font)
        painter.setRenderHint(QPainter.Antialiasing)

        style.drawPrimitive(
            QStyle.PE_PanelItemViewRow, option, painter, option.widget)
        style.drawPrimitive(
            QStyle.PE_PanelItemViewItem, option, painter, option.widget)

        textrect = textrect.adjusted(0, 0, 0, -distheight - spacing)
        distrect = QRect(
            textrect.bottomLeft() + QPoint(0, spacing),
            QSize(rect.width(), distheight))
        painter.setPen(QPen(Qt.lightGray, 0.3))
        self.drawDistBar(painter, distrect, dist)
        painter.restore()
        if text:
            option.text = text
            self.drawViewItemText(style, painter, option, textrect)

    def drawDistBar(self, painter, rect, distribution):
        painter.save()
        painter.translate(rect.topLeft())
        for i in self.shown_probabilities:
            if i is None:
                continue
            dvalue = distribution[i]
            if not dvalue > 0:  # This also skips nans
                continue
            painter.setBrush(self.colors[i])
            width = rect.width() * dvalue
            painter.drawRoundedRect(QRectF(0, 0, width, rect.height()), 1, 2)
            painter.translate(width, 0.0)
        painter.restore()


class PredictionsModel(AbstractSortTableModel):
    list_sorted = pyqtSignal()

    def __init__(self, values=None, probs=None, headers=None, parent=None):
        super().__init__(parent)
        self._values = values
        self._probs = probs
        self.__probInd = None
        if values is not None:
            assert len(self._values) == len(self._probs) != 0
            sizes = {len(x) for c in (values, probs) for x in c}
            assert len(sizes) == 1
            self.__columnCount = len(values)
            self.__rowCount = sizes.pop()
            if headers is None:
                headers = [None] * self.__columnCount
        else:
            assert probs is None
            assert headers is None
            self.__columnCount = self.__rowCount = 0
        self._header = headers

    def rowCount(self, parent=QModelIndex()):
        return 0 if parent.isValid() else self.__rowCount

    def columnCount(self, parent=QModelIndex()):
        return 0 if parent.isValid() else self.__columnCount

    def data(self, index, role=Qt.DisplayRole):
        if role in (Qt.DisplayRole, Qt.EditRole):
            column = index.column()
            row = self.mapToSourceRows(index.row())
            return self._values[column][row], self._probs[column][row]
        return None

    def headerData(self, section, orientation, role=Qt.DisplayRole):
        if role == Qt.DisplayRole:
            if orientation == Qt.Vertical:
                return str(section + 1)
            elif self._header is not None and section < len(self._header):
                return self._header[section]
        return None

    def setProbInd(self, indices):
        self.__probInd = indices
        self.sort(self.sortColumn())

    def sortColumnData(self, column):
        values = self._values[column]
        probs = self._probs[column]
        # Let us assume that probs can be None, numpy array or list of arrays
        if probs is not None and len(probs) and len(probs[0]) \
                and self.__probInd is not None and len(self.__probInd):
            return probs[:, self.__probInd]
        else:
            return values

    def sort(self, column, order=Qt.AscendingOrder):
        super().sort(column, order)
        self.list_sorted.emit()


# PredictionsModel and DataModel have the same signal and sort method, but
# extracting it into a common base class would require diamond inheritance
# because they're derived from different classes. It's not worth it.
class DataModel(TableModel):
    list_sorted = pyqtSignal()

    def sort(self, column, order=Qt.AscendingOrder):
        super().sort(column, order)
        self.list_sorted.emit()


class SharedSelectionStore:
    """
    An object shared between multiple selection models

    The object assumes that the underlying models are AbstractSortTableModel.
    Internally, the object stores indices of unmapped, source rows (as int).

    The class implements method `select` with the same signature as
    QItemSelectionModel.select. Selection models that share this object
    must call this method. After changing the selection, this method will
    call `emit_selection_rows_changed` of all selection models, so they
    can emit the signal selectionChanged.
    """
    def __init__(self, model):
        # unmapped indices of selected rows
        self._rows: Set[int] = set()
        self.model: AbstractSortTableModel = model
        self._selection_models: List[SharedSelectionModel] = []

    @property
    def rows(self) -> Set[int]:
        """Indices of selected rows in the source model"""
        return self._rows

    def register(self, selection_model):
        """
        Add a selection mode to the list of models

        Args:
            selection_model (SharedSelectionModel): a new model
        """
        self._selection_models.append(selection_model)

    def unregister(self, selection_model):
        """
        Remove a selection mode to the list of models

        Args:
            selection_model (SharedSelectionModel): a model to remove
        """
        if selection_model in self._selection_models:
            self._selection_models.remove(selection_model)

    def select(self, selection: Union[QModelIndex, QItemSelection], flags: int):
        """
        (De)Select given rows

        Args:
            selection (QModelIndex or QItemSelection):
                rows to select; indices are mapped to rows in the view
            flags (QItemSelectionModel.SelectionFlags):
                flags that tell whether to Clear, Select, Deselect or Toggle
        """
        rows = set()
        if isinstance(selection, QModelIndex):
            if selection.model() is not None:
                rows = {selection.model().mapToSourceRows(selection.row())}
        else:
            indices = selection.indexes()
            if indices:
                map_to = indices[0].model().mapToSourceRows
                rows = set(map_to([index.row() for index in indices]))
        self.select_rows(rows, flags)

    def select_rows(self, rows: Set[int], flags):
        """
        (De)Select given rows

        Args:
            selection (set of int):
                rows to select; indices refer to unmapped rows in model, not view
            flags (QItemSelectionModel.SelectionFlags):
                flags that tell whether to Clear, Select, Deselect or Toggle
        """
        with self._emit_changed():
            if flags & QItemSelectionModel.Clear:
                self._rows.clear()
            if flags & QItemSelectionModel.Select:
                self._rows |= rows
            if flags & QItemSelectionModel.Deselect:
                self._rows -= rows
            if flags & QItemSelectionModel.Toggle:
                self._rows ^= rows

    def clear_selection(self):
        """Clear selection and emit changeSelection signal to all models"""
        with self._emit_changed():
            self._rows.clear()

    def reset(self):
        """Clear selection without emiting a signal,"""
        self._rows.clear()

    @contextmanager
    def _emit_changed(self):
        """
        A context manager that calls `emit_selection_rows_changed after
        changing a selection.
        """
        def map_from_source(rows):
            return self.model.mapFromSourceRows(list(rows))

        old_rows = self._rows.copy()
        try:
            yield
        finally:
            if self.model.rowCount() != 0:
                deselected = map_from_source(old_rows - self._rows)
                selected = map_from_source(self._rows - old_rows)
                if len(selected) != 0 or len(deselected) != 0:
                    for model in self._selection_models:
                        model.emit_selection_rows_changed(selected, deselected)


class SharedSelectionModel(QItemSelectionModel):
    """
    A selection model that shares the selection with its peers.

    It assumes that the underlying model is a AbstractTableModel.
    """
    def __init__(self, shared_store, model, parent):
        super().__init__(model, parent)
        self.store: SharedSelectionStore = shared_store
        self.store.register(self)

    def select(self, selection, flags):
        self.store.select(selection, flags)

    def selection_from_rows(self, rows: Sequence[int]) -> QItemSelection:
        """
        Return selection across all columns for given row indices (as ints)

        Args:
            rows (sequence of int): row indices, as shown in the view, not model

        Returns: QItemSelection
        """
        model = self.model()
        index = model.index
        last_col = model.columnCount() - 1
        sel = QItemSelection()
        for row in rows:
            sel.select(index(row, 0), index(row, last_col))
        return sel

    def emit_selection_rows_changed(
            self, selected: Sequence[int], deselected: Sequence[int]):
        """
        Given a sequence of indices of selected and deselected rows,
        emit a selectionChanged signal.

        Args:
            selected (Sequence[int]): indices of selected rows
            deselected (Sequence[int]): indices of deselected rows
        """
        self.selectionChanged.emit(
            self.selection_from_rows(selected),
            self.selection_from_rows(deselected))

    def selection(self):
        rows = self.model().mapFromSourceRows(list(self.store.rows))
        return self.selection_from_rows(rows)

    def hasSelection(self) -> bool:
        return bool(self.store.rows)

    def isColumnSelected(self, *_) -> bool:
        return len(self.store.rows) == self.model().rowCount()

    def isRowSelected(self, row, _parent=None) -> bool:
        mapped_row = self.model().mapToSourceRows(row)
        return mapped_row in self.store.rows

    rowIntersectsSelection = isRowSelected

    def isSelected(self, index) -> bool:
        return self.model().mapToSourceRows(index.row()) in self.store.rows

    def selectedColumns(self, row: int):
        if self.isColumnSelected():
            index = self.model().index
            return [index(row, col)
                    for col in range(self.model().columnCount())]
        else:
            return []

    def _selected_rows_arr(self):
        return numpy.fromiter(self.store.rows, int, len(self.store.rows))

    def selectedRows(self, col: int):
        index = self.model().index
        rows = self.model().mapFromSourceRows(self._selected_rows_arr())
        return [index(row, col) for row in rows]

    def selectedIndexes(self):
        index = self.model().index
        rows = self.model().mapFromSourceRows(self._selected_rows_arr())
        return [index(row, col)
                for col in range(self.model().columnCount())
                for row in rows]

    def clearSelection(self):
        self.store.clear_selection()

    def reset(self):
        self.store.reset()
        self.clearCurrentIndex()


class TableView(gui.HScrollStepMixin, QTableView):
    MaxSizeHintSamples = 1000

    def sizeHintForColumn(self, column):
        """
        Reimplemented from `QTableView.sizeHintForColumn`

        Note: This does not match the QTableView's implementation,
        in particular size hints from editor/index widgets are not taken
        into account.

        Parameters
        ----------
        column : int
        """
        # This is probably not needed in Qt5?
        if self.model() is None:
            return -1

        self.ensurePolished()
        model = self.model()
        vheader = self.verticalHeader()
        top = vheader.visualIndexAt(0)
        bottom = vheader.visualIndexAt(self.viewport().height())
        if bottom < 0:
            bottom = self.model().rowCount()

        options = self.viewOptions()
        options.widget = self

        width = 0
        sample_count = 0

        for row in range(top, bottom):
            if not vheader.isSectionHidden(vheader.logicalIndex(row)):
                index = model.index(row, column)
                delegate = self.itemDelegate(index)
                if not delegate:
                    continue
                size = delegate.sizeHint(options, index)
                width = max(size.width(), width)
                sample_count += 1

            if sample_count >= TableView.MaxSizeHintSamples:
                break

        return width + 1 if self.showGrid() else width


def tool_tip(value):
    value, dist = value
    if dist is not None:
        return "{!s} {!s}".format(value, dist)
    else:
        return str(value)


if __name__ == "__main__":  # pragma: no cover
    filename = "iris.tab"
    iris = Orange.data.Table(filename)
    iris2 = iris[:100]

    def pred_error(data, *args, **kwargs):
        raise ValueError

    pred_error.domain = iris.domain
    pred_error.name = "To err is human"

    if iris.domain.has_discrete_class:
        idom = iris.domain
        dom = Domain(
            idom.attributes,
            DiscreteVariable(idom.class_var.name, idom.class_var.values[1::-1])
        )
        iris2 = iris2.transform(dom)
        predictors_ = [
            Orange.classification.SVMLearner(probability=True)(iris2),
            Orange.classification.LogisticRegressionLearner()(iris),
            pred_error
        ]
    elif iris.domain.has_continuous_class:
        predictors_ = [
            Orange.regression.RidgeRegressionLearner(alpha=1.0)(iris),
            Orange.regression.LinearRegressionLearner()(iris),
            pred_error
        ]
    else:
        predictors_ = [pred_error]

    WidgetPreview(OWPredictions).run(
        set_data=iris2,
        insert_predictor=list(enumerate(predictors_)))

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
user