{
    "file_diffs": [
        {
            "old_file_content": "import numbers\n\nimport numpy\nfrom AnyQt.QtWidgets import QFormLayout\nfrom AnyQt.QtCore import Qt\n\nfrom Orange.data import Table, Domain, StringVariable, ContinuousVariable\nfrom Orange.data.util import get_unique_names\nfrom Orange.data.sql.table import SqlTable, AUTO_DL_LIMIT\nfrom Orange.preprocess import preprocess\nfrom Orange.projection import PCA\nfrom Orange.widgets import widget, gui, settings\nfrom Orange.widgets.utils.slidergraph import SliderGraph\nfrom Orange.widgets.utils.widgetpreview import WidgetPreview\nfrom Orange.widgets.widget import Input, Output\n\n\n# Maximum number of PCA components that we can set in the widget\nMAX_COMPONENTS = 100\nLINE_NAMES = [\"component variance\", \"cumulative variance\"]\n\n\nclass OWPCA(widget.OWWidget):\n    name = \"PCA\"\n    description = \"Principal component analysis with a scree-diagram.\"\n    icon = \"icons/PCA.svg\"\n    priority = 3050\n    keywords = [\"principal component analysis\", \"linear transformation\"]\n\n    class Inputs:\n        data = Input(\"Data\", Table)\n\n    class Outputs:\n        transformed_data = Output(\"Transformed Data\", Table, replaces=[\"Transformed data\"])\n        data = Output(\"Data\", Table, default=True)\n        components = Output(\"Components\", Table)\n        pca = Output(\"PCA\", PCA, dynamic=False)\n\n    ncomponents = settings.Setting(2)\n    variance_covered = settings.Setting(100)\n    auto_commit = settings.Setting(True)\n    normalize = settings.Setting(True)\n    maxp = settings.Setting(20)\n    axis_labels = settings.Setting(10)\n\n    graph_name = \"plot.plotItem\"\n\n    class Warning(widget.OWWidget.Warning):\n        trivial_components = widget.Msg(\n            \"All components of the PCA are trivial (explain 0 variance). \"\n            \"Input data is constant (or near constant).\")\n\n    class Error(widget.OWWidget.Error):\n        no_features = widget.Msg(\"At least 1 feature is required\")\n        no_instances = widget.Msg(\"At least 1 data instance is required\")\n\n    def __init__(self):\n        super().__init__()\n        self.data = None\n\n        self._pca = None\n        self._transformed = None\n        self._variance_ratio = None\n        self._cumulative = None\n        self._init_projector()\n\n        # Components Selection\n        box = gui.vBox(self.controlArea, \"Components Selection\")\n        form = QFormLayout()\n        box.layout().addLayout(form)\n\n        self.components_spin = gui.spin(\n            box, self, \"ncomponents\", 1, MAX_COMPONENTS,\n            callback=self._update_selection_component_spin,\n            keyboardTracking=False\n        )\n        self.components_spin.setSpecialValueText(\"All\")\n\n        self.variance_spin = gui.spin(\n            box, self, \"variance_covered\", 1, 100,\n            callback=self._update_selection_variance_spin,\n            keyboardTracking=False\n        )\n        self.variance_spin.setSuffix(\"%\")\n\n        form.addRow(\"Components:\", self.components_spin)\n        form.addRow(\"Explained variance:\", self.variance_spin)\n\n        # Options\n        self.options_box = gui.vBox(self.controlArea, \"Options\")\n        self.normalize_box = gui.checkBox(\n            self.options_box, self, \"normalize\",\n            \"Normalize variables\", callback=self._update_normalize\n        )\n\n        self.maxp_spin = gui.spin(\n            self.options_box, self, \"maxp\", 1, MAX_COMPONENTS,\n            label=\"Show only first\", callback=self._setup_plot,\n            keyboardTracking=False\n        )\n\n        self.controlArea.layout().addStretch()\n\n        gui.auto_apply(self.controlArea, self, \"auto_commit\")\n\n        self.plot = SliderGraph(\n            \"Principal Components\", \"Proportion of variance\",\n            self._on_cut_changed)\n\n        self.mainArea.layout().addWidget(self.plot)\n        self._update_normalize()\n\n    @Inputs.data\n    def set_data(self, data):\n        self.clear_messages()\n        self.clear()\n        self.information()\n        self.data = None\n        if isinstance(data, SqlTable):\n            if data.approx_len() < AUTO_DL_LIMIT:\n                data = Table(data)\n            else:\n                self.information(\"Data has been sampled\")\n                data_sample = data.sample_time(1, no_cache=True)\n                data_sample.download_data(2000, partial=True)\n                data = Table(data_sample)\n        if isinstance(data, Table):\n            if not data.domain.attributes:\n                self.Error.no_features()\n                self.clear_outputs()\n                return\n            if not data:\n                self.Error.no_instances()\n                self.clear_outputs()\n                return\n\n        self._init_projector()\n\n        self.data = data\n        self.fit()\n\n    def fit(self):\n        self.clear()\n        self.Warning.trivial_components.clear()\n        if self.data is None:\n            return\n\n        data = self.data\n\n        if self.normalize:\n            self._pca_projector.preprocessors = \\\n                self._pca_preprocessors + [preprocess.Normalize(center=False)]\n        else:\n            self._pca_projector.preprocessors = self._pca_preprocessors\n\n        if not isinstance(data, SqlTable):\n            pca = self._pca_projector(data)\n            variance_ratio = pca.explained_variance_ratio_\n            cumulative = numpy.cumsum(variance_ratio)\n\n            if numpy.isfinite(cumulative[-1]):\n                self.components_spin.setRange(0, len(cumulative))\n                self._pca = pca\n                self._variance_ratio = variance_ratio\n                self._cumulative = cumulative\n                self._setup_plot()\n            else:\n                self.Warning.trivial_components()\n\n            self.unconditional_commit()\n\n    def clear(self):\n        self._pca = None\n        self._transformed = None\n        self._variance_ratio = None\n        self._cumulative = None\n        self.plot.clear_plot()\n\n    def clear_outputs(self):\n        self.Outputs.transformed_data.send(None)\n        self.Outputs.data.send(None)\n        self.Outputs.components.send(None)\n        self.Outputs.pca.send(self._pca_projector)\n\n    def _setup_plot(self):\n        if self._pca is None:\n            self.plot.clear_plot()\n            return\n\n        explained_ratio = self._variance_ratio\n        explained = self._cumulative\n        cutpos = self._nselected_components()\n        p = min(len(self._variance_ratio), self.maxp)\n\n        self.plot.update(\n            numpy.arange(1, p+1), [explained_ratio[:p], explained[:p]],\n            [Qt.red, Qt.darkYellow], cutpoint_x=cutpos, names=LINE_NAMES)\n\n        self._update_axis()\n\n    def _on_cut_changed(self, components):\n        if components == self.ncomponents \\\n                or self.ncomponents == 0 \\\n                or self._pca is not None \\\n                and components == len(self._variance_ratio):\n            return\n\n        self.ncomponents = components\n        if self._pca is not None:\n            var = self._cumulative[components - 1]\n            if numpy.isfinite(var):\n                self.variance_covered = int(var * 100)\n\n        self._invalidate_selection()\n\n    def _update_selection_component_spin(self):\n        # cut changed by \"ncomponents\" spin.\n        if self._pca is None:\n            self._invalidate_selection()\n            return\n\n        if self.ncomponents == 0:\n            # Special \"All\" value\n            cut = len(self._variance_ratio)\n        else:\n            cut = self.ncomponents\n\n        var = self._cumulative[cut - 1]\n        if numpy.isfinite(var):\n            self.variance_covered = int(var * 100)\n\n        self.plot.set_cut_point(cut)\n        self._invalidate_selection()\n\n    def _update_selection_variance_spin(self):\n        # cut changed by \"max variance\" spin.\n        if self._pca is None:\n            return\n\n        cut = numpy.searchsorted(self._cumulative,\n                                 self.variance_covered / 100.0) + 1\n        cut = min(cut, len(self._cumulative))\n        self.ncomponents = cut\n        self.plot.set_cut_point(cut)\n        self._invalidate_selection()\n\n    def _update_normalize(self):\n        self.fit()\n        if self.data is None:\n            self._invalidate_selection()\n\n    def _init_projector(self):\n        self._pca_projector = PCA(n_components=MAX_COMPONENTS, random_state=0)\n        self._pca_projector.component = self.ncomponents\n        self._pca_preprocessors = PCA.preprocessors\n\n    def _nselected_components(self):\n        \"\"\"Return the number of selected components.\"\"\"\n        if self._pca is None:\n            return 0\n\n        if self.ncomponents == 0:\n            # Special \"All\" value\n            max_comp = len(self._variance_ratio)\n        else:\n            max_comp = self.ncomponents\n\n        var_max = self._cumulative[max_comp - 1]\n        if var_max != numpy.floor(self.variance_covered / 100.0):\n            cut = max_comp\n            assert numpy.isfinite(var_max)\n            self.variance_covered = int(var_max * 100)\n        else:\n            self.ncomponents = cut = numpy.searchsorted(\n                self._cumulative, self.variance_covered / 100.0) + 1\n        return cut\n\n    def _invalidate_selection(self):\n        self.commit()\n\n    def _update_axis(self):\n        p = min(len(self._variance_ratio), self.maxp)\n        axis = self.plot.getAxis(\"bottom\")\n        d = max((p-1)//(self.axis_labels-1), 1)\n        axis.setTicks([[(i, str(i)) for i in range(1, p + 1, d)]])\n\n    def commit(self):\n        transformed = data = components = None\n        if self._pca is not None:\n            if self._transformed is None:\n                # Compute the full transform (MAX_COMPONENTS components) once.\n                self._transformed = self._pca(self.data)\n            transformed = self._transformed\n\n            domain = Domain(\n                transformed.domain.attributes[:self.ncomponents],\n                self.data.domain.class_vars,\n                self.data.domain.metas\n            )\n            transformed = transformed.from_table(domain, transformed)\n            # prevent caching new features by defining compute_value\n            proposed = [a.name for a in self._pca.orig_domain.attributes]\n            meta_name = get_unique_names(proposed, 'components')\n            dom = Domain(\n                [ContinuousVariable(name, compute_value=lambda _: None)\n                 for name in proposed],\n                metas=[StringVariable(name=meta_name)])\n            metas = numpy.array([['PC{}'.format(i + 1)\n                                  for i in range(self.ncomponents)]],\n                                dtype=object).T\n            components = Table(dom, self._pca.components_[:self.ncomponents],\n                               metas=metas)\n            components.name = 'components'\n\n            data_dom = Domain(\n                self.data.domain.attributes,\n                self.data.domain.class_vars,\n                self.data.domain.metas + domain.attributes)\n            data = Table.from_numpy(\n                data_dom, self.data.X, self.data.Y,\n                numpy.hstack((self.data.metas, transformed.X)),\n                ids=self.data.ids)\n\n        self._pca_projector.component = self.ncomponents\n        self.Outputs.transformed_data.send(transformed)\n        self.Outputs.components.send(components)\n        self.Outputs.data.send(data)\n        self.Outputs.pca.send(self._pca_projector)\n\n    def send_report(self):\n        if self.data is None:\n            return\n        self.report_items((\n            (\"Normalize data\", str(self.normalize)),\n            (\"Selected components\", self.ncomponents),\n            (\"Explained variance\", \"{:.3f} %\".format(self.variance_covered))\n        ))\n        self.report_plot()\n\n    @classmethod\n    def migrate_settings(cls, settings, version):\n        if \"variance_covered\" in settings:\n            # Due to the error in gh-1896 the variance_covered was persisted\n            # as a NaN value, causing a TypeError in the widgets `__init__`.\n            vc = settings[\"variance_covered\"]\n            if isinstance(vc, numbers.Real):\n                if numpy.isfinite(vc):\n                    vc = int(vc)\n                else:\n                    vc = 100\n                settings[\"variance_covered\"] = vc\n        if settings.get(\"ncomponents\", 0) > MAX_COMPONENTS:\n            settings[\"ncomponents\"] = MAX_COMPONENTS\n\n        # Remove old `decomposition_idx` when SVD was still included\n        settings.pop(\"decomposition_idx\", None)\n\n        # Remove RemotePCA settings\n        settings.pop(\"batch_size\", None)\n        settings.pop(\"address\", None)\n        settings.pop(\"auto_update\", None)\n\n\nif __name__ == \"__main__\":  # pragma: no cover\n    WidgetPreview(OWPCA).run(Table(\"housing\"))\n",
            "new_file_content": "import numbers\n\nimport numpy\nfrom AnyQt.QtWidgets import QFormLayout\nfrom AnyQt.QtCore import Qt\n\nfrom Orange.data import Table, Domain, StringVariable, ContinuousVariable\nfrom Orange.data.util import get_unique_names\nfrom Orange.data.sql.table import SqlTable, AUTO_DL_LIMIT\nfrom Orange.preprocess import preprocess\nfrom Orange.projection import PCA\nfrom Orange.widgets import widget, gui, settings\nfrom Orange.widgets.utils.slidergraph import SliderGraph\nfrom Orange.widgets.utils.widgetpreview import WidgetPreview\nfrom Orange.widgets.widget import Input, Output\n\n\n# Maximum number of PCA components that we can set in the widget\nMAX_COMPONENTS = 100\nLINE_NAMES = [\"component variance\", \"cumulative variance\"]\n\n\nclass OWPCA(widget.OWWidget):\n    name = \"PCA\"\n    description = \"Principal component analysis with a scree-diagram.\"\n    icon = \"icons/PCA.svg\"\n    priority = 3050\n    keywords = [\"principal component analysis\", \"linear transformation\"]\n\n    class Inputs:\n        data = Input(\"Data\", Table)\n\n    class Outputs:\n        transformed_data = Output(\"Transformed Data\", Table, replaces=[\"Transformed data\"])\n        data = Output(\"Data\", Table, default=True)\n        components = Output(\"Components\", Table)\n        pca = Output(\"PCA\", PCA, dynamic=False)\n\n    ncomponents = settings.Setting(2)\n    variance_covered = settings.Setting(100)\n    auto_commit = settings.Setting(True)\n    normalize = settings.Setting(True)\n    maxp = settings.Setting(20)\n    axis_labels = settings.Setting(10)\n\n    graph_name = \"plot.plotItem\"\n\n    class Warning(widget.OWWidget.Warning):\n        trivial_components = widget.Msg(\n            \"All components of the PCA are trivial (explain 0 variance). \"\n            \"Input data is constant (or near constant).\")\n\n    class Error(widget.OWWidget.Error):\n        no_features = widget.Msg(\"At least 1 feature is required\")\n        no_instances = widget.Msg(\"At least 1 data instance is required\")\n\n    def __init__(self):\n        super().__init__()\n        self.data = None\n\n        self._pca = None\n        self._transformed = None\n        self._variance_ratio = None\n        self._cumulative = None\n        self._init_projector()\n\n        # Components Selection\n        box = gui.vBox(self.controlArea, \"Components Selection\")\n        form = QFormLayout()\n        box.layout().addLayout(form)\n\n        self.components_spin = gui.spin(\n            box, self, \"ncomponents\", 1, MAX_COMPONENTS,\n            callback=self._update_selection_component_spin,\n            keyboardTracking=False\n        )\n        self.components_spin.setSpecialValueText(\"All\")\n\n        self.variance_spin = gui.spin(\n            box, self, \"variance_covered\", 1, 100,\n            callback=self._update_selection_variance_spin,\n            keyboardTracking=False\n        )\n        self.variance_spin.setSuffix(\"%\")\n\n        form.addRow(\"Components:\", self.components_spin)\n        form.addRow(\"Explained variance:\", self.variance_spin)\n\n        # Options\n        self.options_box = gui.vBox(self.controlArea, \"Options\")\n        self.normalize_box = gui.checkBox(\n            self.options_box, self, \"normalize\",\n            \"Normalize variables\", callback=self._update_normalize\n        )\n\n        self.maxp_spin = gui.spin(\n            self.options_box, self, \"maxp\", 1, MAX_COMPONENTS,\n            label=\"Show only first\", callback=self._setup_plot,\n            keyboardTracking=False\n        )\n\n        self.controlArea.layout().addStretch()\n\n        gui.auto_apply(self.controlArea, self, \"auto_commit\")\n\n        self.plot = SliderGraph(\n            \"Principal Components\", \"Proportion of variance\",\n            self._on_cut_changed)\n\n        self.mainArea.layout().addWidget(self.plot)\n        self._update_normalize()\n\n    @Inputs.data\n    def set_data(self, data):\n        self.clear_messages()\n        self.clear()\n        self.information()\n        self.data = None\n        if not data:\n            self.clear_outputs()\n        if isinstance(data, SqlTable):\n            if data.approx_len() < AUTO_DL_LIMIT:\n                data = Table(data)\n            else:\n                self.information(\"Data has been sampled\")\n                data_sample = data.sample_time(1, no_cache=True)\n                data_sample.download_data(2000, partial=True)\n                data = Table(data_sample)\n        if isinstance(data, Table):\n            if not data.domain.attributes:\n                self.Error.no_features()\n                self.clear_outputs()\n                return\n            if not data:\n                self.Error.no_instances()\n                self.clear_outputs()\n                return\n\n        self._init_projector()\n\n        self.data = data\n        self.fit()\n\n    def fit(self):\n        self.clear()\n        self.Warning.trivial_components.clear()\n        if self.data is None:\n            return\n\n        data = self.data\n\n        if self.normalize:\n            self._pca_projector.preprocessors = \\\n                self._pca_preprocessors + [preprocess.Normalize(center=False)]\n        else:\n            self._pca_projector.preprocessors = self._pca_preprocessors\n\n        if not isinstance(data, SqlTable):\n            pca = self._pca_projector(data)\n            variance_ratio = pca.explained_variance_ratio_\n            cumulative = numpy.cumsum(variance_ratio)\n\n            if numpy.isfinite(cumulative[-1]):\n                self.components_spin.setRange(0, len(cumulative))\n                self._pca = pca\n                self._variance_ratio = variance_ratio\n                self._cumulative = cumulative\n                self._setup_plot()\n            else:\n                self.Warning.trivial_components()\n\n            self.unconditional_commit()\n\n    def clear(self):\n        self._pca = None\n        self._transformed = None\n        self._variance_ratio = None\n        self._cumulative = None\n        self.plot.clear_plot()\n\n    def clear_outputs(self):\n        self.Outputs.transformed_data.send(None)\n        self.Outputs.data.send(None)\n        self.Outputs.components.send(None)\n        self.Outputs.pca.send(self._pca_projector)\n\n    def _setup_plot(self):\n        if self._pca is None:\n            self.plot.clear_plot()\n            return\n\n        explained_ratio = self._variance_ratio\n        explained = self._cumulative\n        cutpos = self._nselected_components()\n        p = min(len(self._variance_ratio), self.maxp)\n\n        self.plot.update(\n            numpy.arange(1, p+1), [explained_ratio[:p], explained[:p]],\n            [Qt.red, Qt.darkYellow], cutpoint_x=cutpos, names=LINE_NAMES)\n\n        self._update_axis()\n\n    def _on_cut_changed(self, components):\n        if components == self.ncomponents \\\n                or self.ncomponents == 0 \\\n                or self._pca is not None \\\n                and components == len(self._variance_ratio):\n            return\n\n        self.ncomponents = components\n        if self._pca is not None:\n            var = self._cumulative[components - 1]\n            if numpy.isfinite(var):\n                self.variance_covered = int(var * 100)\n\n        self._invalidate_selection()\n\n    def _update_selection_component_spin(self):\n        # cut changed by \"ncomponents\" spin.\n        if self._pca is None:\n            self._invalidate_selection()\n            return\n\n        if self.ncomponents == 0:\n            # Special \"All\" value\n            cut = len(self._variance_ratio)\n        else:\n            cut = self.ncomponents\n\n        var = self._cumulative[cut - 1]\n        if numpy.isfinite(var):\n            self.variance_covered = int(var * 100)\n\n        self.plot.set_cut_point(cut)\n        self._invalidate_selection()\n\n    def _update_selection_variance_spin(self):\n        # cut changed by \"max variance\" spin.\n        if self._pca is None:\n            return\n\n        cut = numpy.searchsorted(self._cumulative,\n                                 self.variance_covered / 100.0) + 1\n        cut = min(cut, len(self._cumulative))\n        self.ncomponents = cut\n        self.plot.set_cut_point(cut)\n        self._invalidate_selection()\n\n    def _update_normalize(self):\n        self.fit()\n        if self.data is None:\n            self._invalidate_selection()\n\n    def _init_projector(self):\n        self._pca_projector = PCA(n_components=MAX_COMPONENTS, random_state=0)\n        self._pca_projector.component = self.ncomponents\n        self._pca_preprocessors = PCA.preprocessors\n\n    def _nselected_components(self):\n        \"\"\"Return the number of selected components.\"\"\"\n        if self._pca is None:\n            return 0\n\n        if self.ncomponents == 0:\n            # Special \"All\" value\n            max_comp = len(self._variance_ratio)\n        else:\n            max_comp = self.ncomponents\n\n        var_max = self._cumulative[max_comp - 1]\n        if var_max != numpy.floor(self.variance_covered / 100.0):\n            cut = max_comp\n            assert numpy.isfinite(var_max)\n            self.variance_covered = int(var_max * 100)\n        else:\n            self.ncomponents = cut = numpy.searchsorted(\n                self._cumulative, self.variance_covered / 100.0) + 1\n        return cut\n\n    def _invalidate_selection(self):\n        self.commit()\n\n    def _update_axis(self):\n        p = min(len(self._variance_ratio), self.maxp)\n        axis = self.plot.getAxis(\"bottom\")\n        d = max((p-1)//(self.axis_labels-1), 1)\n        axis.setTicks([[(i, str(i)) for i in range(1, p + 1, d)]])\n\n    def commit(self):\n        transformed = data = components = None\n        if self._pca is not None:\n            if self._transformed is None:\n                # Compute the full transform (MAX_COMPONENTS components) once.\n                self._transformed = self._pca(self.data)\n            transformed = self._transformed\n\n            domain = Domain(\n                transformed.domain.attributes[:self.ncomponents],\n                self.data.domain.class_vars,\n                self.data.domain.metas\n            )\n            transformed = transformed.from_table(domain, transformed)\n            # prevent caching new features by defining compute_value\n            proposed = [a.name for a in self._pca.orig_domain.attributes]\n            meta_name = get_unique_names(proposed, 'components')\n            dom = Domain(\n                [ContinuousVariable(name, compute_value=lambda _: None)\n                 for name in proposed],\n                metas=[StringVariable(name=meta_name)])\n            metas = numpy.array([['PC{}'.format(i + 1)\n                                  for i in range(self.ncomponents)]],\n                                dtype=object).T\n            components = Table(dom, self._pca.components_[:self.ncomponents],\n                               metas=metas)\n            components.name = 'components'\n\n            data_dom = Domain(\n                self.data.domain.attributes,\n                self.data.domain.class_vars,\n                self.data.domain.metas + domain.attributes)\n            data = Table.from_numpy(\n                data_dom, self.data.X, self.data.Y,\n                numpy.hstack((self.data.metas, transformed.X)),\n                ids=self.data.ids)\n\n        self._pca_projector.component = self.ncomponents\n        self.Outputs.transformed_data.send(transformed)\n        self.Outputs.components.send(components)\n        self.Outputs.data.send(data)\n        self.Outputs.pca.send(self._pca_projector)\n\n    def send_report(self):\n        if self.data is None:\n            return\n        self.report_items((\n            (\"Normalize data\", str(self.normalize)),\n            (\"Selected components\", self.ncomponents),\n            (\"Explained variance\", \"{:.3f} %\".format(self.variance_covered))\n        ))\n        self.report_plot()\n\n    @classmethod\n    def migrate_settings(cls, settings, version):\n        if \"variance_covered\" in settings:\n            # Due to the error in gh-1896 the variance_covered was persisted\n            # as a NaN value, causing a TypeError in the widgets `__init__`.\n            vc = settings[\"variance_covered\"]\n            if isinstance(vc, numbers.Real):\n                if numpy.isfinite(vc):\n                    vc = int(vc)\n                else:\n                    vc = 100\n                settings[\"variance_covered\"] = vc\n        if settings.get(\"ncomponents\", 0) > MAX_COMPONENTS:\n            settings[\"ncomponents\"] = MAX_COMPONENTS\n\n        # Remove old `decomposition_idx` when SVD was still included\n        settings.pop(\"decomposition_idx\", None)\n\n        # Remove RemotePCA settings\n        settings.pop(\"batch_size\", None)\n        settings.pop(\"address\", None)\n        settings.pop(\"auto_update\", None)\n\n\nif __name__ == \"__main__\":  # pragma: no cover\n    WidgetPreview(OWPCA).run(Table(\"housing\"))\n",
            "header": {
                "file": {
                    "path": "Orange/widgets/unsupervised/owpca.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "f03b52167",
                "new_commit_hash": "6aced939b",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/Orange/widgets/unsupervised/owpca.py"
            },
            "plus_file": {
                "path": "b/Orange/widgets/unsupervised/owpca.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 116,
                            "length": 6
                        },
                        "new_range": {
                            "start": 116,
                            "length": 8
                        },
                        "section": "class OWPCA(widget.OWWidget):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        self.clear()",
                                "type": "context"
                            },
                            {
                                "content": "        self.information()",
                                "type": "context"
                            },
                            {
                                "content": "        self.data = None",
                                "type": "context"
                            },
                            {
                                "content": "        if not data:",
                                "type": "added"
                            },
                            {
                                "content": "            self.clear_outputs()",
                                "type": "added"
                            },
                            {
                                "content": "        if isinstance(data, SqlTable):",
                                "type": "context"
                            },
                            {
                                "content": "            if data.approx_len() < AUTO_DL_LIMIT:",
                                "type": "context"
                            },
                            {
                                "content": "                data = Table(data)",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "Orange/widgets/unsupervised/owpca.py",
                            "type": "class",
                            "name": "OWPCA",
                            "content": "class OWPCA(widget.OWWidget):\n    name = \"PCA\"\n    description = \"Principal component analysis with a scree-diagram.\"\n    icon = \"icons/PCA.svg\"\n    priority = 3050\n    keywords = [\"principal component analysis\", \"linear transformation\"]\n\n    class Inputs:\n        data = Input(\"Data\", Table)\n\n    class Outputs:\n        transformed_data = Output(\"Transformed Data\", Table, replaces=[\"Transformed data\"])\n        data = Output(\"Data\", Table, default=True)\n        components = Output(\"Components\", Table)\n        pca = Output(\"PCA\", PCA, dynamic=False)\n\n    ncomponents = settings.Setting(2)\n    variance_covered = settings.Setting(100)\n    auto_commit = settings.Setting(True)\n    normalize = settings.Setting(True)\n    maxp = settings.Setting(20)\n    axis_labels = settings.Setting(10)\n\n    graph_name = \"plot.plotItem\"\n\n    class Warning(widget.OWWidget.Warning):\n        trivial_components = widget.Msg(\n            \"All components of the PCA are trivial (explain 0 variance). \"\n            \"Input data is constant (or near constant).\")\n\n    class Error(widget.OWWidget.Error):\n        no_features = widget.Msg(\"At least 1 feature is required\")\n        no_instances = widget.Msg(\"At least 1 data instance is required\")\n\n    def __init__(self):\n        super().__init__()\n        self.data = None\n\n        self._pca = None\n        self._transformed = None\n        self._variance_ratio = None\n        self._cumulative = None\n        self._init_projector()\n\n        # Components Selection\n        box = gui.vBox(self.controlArea, \"Components Selection\")\n        form = QFormLayout()\n        box.layout().addLayout(form)\n\n        self.components_spin = gui.spin(\n            box, self, \"ncomponents\", 1, MAX_COMPONENTS,\n            callback=self._update_selection_component_spin,\n            keyboardTracking=False\n        )\n        self.components_spin.setSpecialValueText(\"All\")\n\n        self.variance_spin = gui.spin(\n            box, self, \"variance_covered\", 1, 100,\n            callback=self._update_selection_variance_spin,\n            keyboardTracking=False\n        )\n        self.variance_spin.setSuffix(\"%\")\n\n        form.addRow(\"Components:\", self.components_spin)\n        form.addRow(\"Explained variance:\", self.variance_spin)\n\n        # Options\n        self.options_box = gui.vBox(self.controlArea, \"Options\")\n        self.normalize_box = gui.checkBox(\n            self.options_box, self, \"normalize\",\n            \"Normalize variables\", callback=self._update_normalize\n        )\n\n        self.maxp_spin = gui.spin(\n            self.options_box, self, \"maxp\", 1, MAX_COMPONENTS,\n            label=\"Show only first\", callback=self._setup_plot,\n            keyboardTracking=False\n        )\n\n        self.controlArea.layout().addStretch()\n\n        gui.auto_apply(self.controlArea, self, \"auto_commit\")\n\n        self.plot = SliderGraph(\n            \"Principal Components\", \"Proportion of variance\",\n            self._on_cut_changed)\n\n        self.mainArea.layout().addWidget(self.plot)\n        self._update_normalize()\n\n    @Inputs.data\n    def set_data(self, data):\n        self.clear_messages()\n        self.clear()\n        self.information()\n        self.data = None\n        if not data:\n            self.clear_outputs()\n        if isinstance(data, SqlTable):\n            if data.approx_len() < AUTO_DL_LIMIT:\n                data = Table(data)\n            else:\n                self.information(\"Data has been sampled\")\n                data_sample = data.sample_time(1, no_cache=True)\n                data_sample.download_data(2000, partial=True)\n                data = Table(data_sample)\n        if isinstance(data, Table):\n            if not data.domain.attributes:\n                self.Error.no_features()\n                self.clear_outputs()\n                return\n            if not data:\n                self.Error.no_instances()\n                self.clear_outputs()\n                return\n\n        self._init_projector()\n\n        self.data = data\n        self.fit()\n\n    def fit(self):\n        self.clear()\n        self.Warning.trivial_components.clear()\n        if self.data is None:\n            return\n\n        data = self.data\n\n        if self.normalize:\n            self._pca_projector.preprocessors = \\\n                self._pca_preprocessors + [preprocess.Normalize(center=False)]\n        else:\n            self._pca_projector.preprocessors = self._pca_preprocessors\n\n        if not isinstance(data, SqlTable):\n            pca = self._pca_projector(data)\n            variance_ratio = pca.explained_variance_ratio_\n            cumulative = numpy.cumsum(variance_ratio)\n\n            if numpy.isfinite(cumulative[-1]):\n                self.components_spin.setRange(0, len(cumulative))\n                self._pca = pca\n                self._variance_ratio = variance_ratio\n                self._cumulative = cumulative\n                self._setup_plot()\n            else:\n                self.Warning.trivial_components()\n\n            self.unconditional_commit()\n\n    def clear(self):\n        self._pca = None\n        self._transformed = None\n        self._variance_ratio = None\n        self._cumulative = None\n        self.plot.clear_plot()\n\n    def clear_outputs(self):\n        self.Outputs.transformed_data.send(None)\n        self.Outputs.data.send(None)\n        self.Outputs.components.send(None)\n        self.Outputs.pca.send(self._pca_projector)\n\n    def _setup_plot(self):\n        if self._pca is None:\n            self.plot.clear_plot()\n            return\n\n        explained_ratio = self._variance_ratio\n        explained = self._cumulative\n        cutpos = self._nselected_components()\n        p = min(len(self._variance_ratio), self.maxp)\n\n        self.plot.update(\n            numpy.arange(1, p+1), [explained_ratio[:p], explained[:p]],\n            [Qt.red, Qt.darkYellow], cutpoint_x=cutpos, names=LINE_NAMES)\n\n        self._update_axis()\n\n    def _on_cut_changed(self, components):\n        if components == self.ncomponents \\\n                or self.ncomponents == 0 \\\n                or self._pca is not None \\\n                and components == len(self._variance_ratio):\n            return\n\n        self.ncomponents = components\n        if self._pca is not None:\n            var = self._cumulative[components - 1]\n            if numpy.isfinite(var):\n                self.variance_covered = int(var * 100)\n\n        self._invalidate_selection()\n\n    def _update_selection_component_spin(self):\n        # cut changed by \"ncomponents\" spin.\n        if self._pca is None:\n            self._invalidate_selection()\n            return\n\n        if self.ncomponents == 0:\n            # Special \"All\" value\n            cut = len(self._variance_ratio)\n        else:\n            cut = self.ncomponents\n\n        var = self._cumulative[cut - 1]\n        if numpy.isfinite(var):\n            self.variance_covered = int(var * 100)\n\n        self.plot.set_cut_point(cut)\n        self._invalidate_selection()\n\n    def _update_selection_variance_spin(self):\n        # cut changed by \"max variance\" spin.\n        if self._pca is None:\n            return\n\n        cut = numpy.searchsorted(self._cumulative,\n                                 self.variance_covered / 100.0) + 1\n        cut = min(cut, len(self._cumulative))\n        self.ncomponents = cut\n        self.plot.set_cut_point(cut)\n        self._invalidate_selection()\n\n    def _update_normalize(self):\n        self.fit()\n        if self.data is None:\n            self._invalidate_selection()\n\n    def _init_projector(self):\n        self._pca_projector = PCA(n_components=MAX_COMPONENTS, random_state=0)\n        self._pca_projector.component = self.ncomponents\n        self._pca_preprocessors = PCA.preprocessors\n\n    def _nselected_components(self):\n        \"\"\"Return the number of selected components.\"\"\"\n        if self._pca is None:\n            return 0\n\n        if self.ncomponents == 0:\n            # Special \"All\" value\n            max_comp = len(self._variance_ratio)\n        else:\n            max_comp = self.ncomponents\n\n        var_max = self._cumulative[max_comp - 1]\n        if var_max != numpy.floor(self.variance_covered / 100.0):\n            cut = max_comp\n            assert numpy.isfinite(var_max)\n            self.variance_covered = int(var_max * 100)\n        else:\n            self.ncomponents = cut = numpy.searchsorted(\n                self._cumulative, self.variance_covered / 100.0) + 1\n        return cut\n\n    def _invalidate_selection(self):\n        self.commit()\n\n    def _update_axis(self):\n        p = min(len(self._variance_ratio), self.maxp)\n        axis = self.plot.getAxis(\"bottom\")\n        d = max((p-1)//(self.axis_labels-1), 1)\n        axis.setTicks([[(i, str(i)) for i in range(1, p + 1, d)]])\n\n    def commit(self):\n        transformed = data = components = None\n        if self._pca is not None:\n            if self._transformed is None:\n                # Compute the full transform (MAX_COMPONENTS components) once.\n                self._transformed = self._pca(self.data)\n            transformed = self._transformed\n\n            domain = Domain(\n                transformed.domain.attributes[:self.ncomponents],\n                self.data.domain.class_vars,\n                self.data.domain.metas\n            )\n            transformed = transformed.from_table(domain, transformed)\n            # prevent caching new features by defining compute_value\n            proposed = [a.name for a in self._pca.orig_domain.attributes]\n            meta_name = get_unique_names(proposed, 'components')\n            dom = Domain(\n                [ContinuousVariable(name, compute_value=lambda _: None)\n                 for name in proposed],\n                metas=[StringVariable(name=meta_name)])\n            metas = numpy.array([['PC{}'.format(i + 1)\n                                  for i in range(self.ncomponents)]],\n                                dtype=object).T\n            components = Table(dom, self._pca.components_[:self.ncomponents],\n                               metas=metas)\n            components.name = 'components'\n\n            data_dom = Domain(\n                self.data.domain.attributes,\n                self.data.domain.class_vars,\n                self.data.domain.metas + domain.attributes)\n            data = Table.from_numpy(\n                data_dom, self.data.X, self.data.Y,\n                numpy.hstack((self.data.metas, transformed.X)),\n                ids=self.data.ids)\n\n        self._pca_projector.component = self.ncomponents\n        self.Outputs.transformed_data.send(transformed)\n        self.Outputs.components.send(components)\n        self.Outputs.data.send(data)\n        self.Outputs.pca.send(self._pca_projector)\n\n    def send_report(self):\n        if self.data is None:\n            return\n        self.report_items((\n            (\"Normalize data\", str(self.normalize)),\n            (\"Selected components\", self.ncomponents),\n            (\"Explained variance\", \"{:.3f} %\".format(self.variance_covered))\n        ))\n        self.report_plot()\n\n    @classmethod\n    def migrate_settings(cls, settings, version):\n        if \"variance_covered\" in settings:\n            # Due to the error in gh-1896 the variance_covered was persisted\n            # as a NaN value, causing a TypeError in the widgets `__init__`.\n            vc = settings[\"variance_covered\"]\n            if isinstance(vc, numbers.Real):\n                if numpy.isfinite(vc):\n                    vc = int(vc)\n                else:\n                    vc = 100\n                settings[\"variance_covered\"] = vc\n        if settings.get(\"ncomponents\", 0) > MAX_COMPONENTS:\n            settings[\"ncomponents\"] = MAX_COMPONENTS\n\n        # Remove old `decomposition_idx` when SVD was still included\n        settings.pop(\"decomposition_idx\", None)\n\n        # Remove RemotePCA settings\n        settings.pop(\"batch_size\", None)\n        settings.pop(\"address\", None)\n        settings.pop(\"auto_update\", None)",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 23,
                            "end_lineno": 363,
                            "parent": null
                        },
                        {
                            "file_name": "Orange/widgets/unsupervised/owpca.py",
                            "type": "method",
                            "name": "OWPCA.set_data",
                            "content": "class OWPCA(widget.OWWidget):\n    name = \"PCA\"\n    description = \"Principal component analysis with a scree-diagram.\"\n    icon = \"icons/PCA.svg\"\n    priority = 3050\n    keywords = [\"principal component analysis\", \"linear transformation\"]\n\n    class Inputs:\n        data = Input(\"Data\", Table)\n\n    class Outputs:\n        transformed_data = Output(\"Transformed Data\", Table, replaces=[\"Transformed data\"])\n        data = Output(\"Data\", Table, default=True)\n        components = Output(\"Components\", Table)\n        pca = Output(\"PCA\", PCA, dynamic=False)\n\n    ncomponents = settings.Setting(2)\n    variance_covered = settings.Setting(100)\n    auto_commit = settings.Setting(True)\n    normalize = settings.Setting(True)\n    maxp = settings.Setting(20)\n    axis_labels = settings.Setting(10)\n\n    graph_name = \"plot.plotItem\"\n\n    class Warning(widget.OWWidget.Warning):\n        trivial_components = widget.Msg(\n            \"All components of the PCA are trivial (explain 0 variance). \"\n            \"Input data is constant (or near constant).\")\n\n    class Error(widget.OWWidget.Error):\n        no_features = widget.Msg(\"At least 1 feature is required\")\n        no_instances = widget.Msg(\"At least 1 data instance is required\")\n\n    def __init__(self):\n        super().__init__()\n        self.data = None\n\n        self._pca = None\n        self._transformed = None\n        self._variance_ratio = None\n        self._cumulative = None\n        self._init_projector()\n\n        # Components Selection\n        box = gui.vBox(self.controlArea, \"Components Selection\")\n        form = QFormLayout()\n        box.layout().addLayout(form)\n\n        self.components_spin = gui.spin(\n            box, self, \"ncomponents\", 1, MAX_COMPONENTS,\n            callback=self._update_selection_component_spin,\n            keyboardTracking=False\n        )\n        self.components_spin.setSpecialValueText(\"All\")\n\n        self.variance_spin = gui.spin(\n            box, self, \"variance_covered\", 1, 100,\n            callback=self._update_selection_variance_spin,\n            keyboardTracking=False\n        )\n        self.variance_spin.setSuffix(\"%\")\n\n        form.addRow(\"Components:\", self.components_spin)\n        form.addRow(\"Explained variance:\", self.variance_spin)\n\n        # Options\n        self.options_box = gui.vBox(self.controlArea, \"Options\")\n        self.normalize_box = gui.checkBox(\n            self.options_box, self, \"normalize\",\n            \"Normalize variables\", callback=self._update_normalize\n        )\n\n        self.maxp_spin = gui.spin(\n            self.options_box, self, \"maxp\", 1, MAX_COMPONENTS,\n            label=\"Show only first\", callback=self._setup_plot,\n            keyboardTracking=False\n        )\n\n        self.controlArea.layout().addStretch()\n\n        gui.auto_apply(self.controlArea, self, \"auto_commit\")\n\n        self.plot = SliderGraph(\n            \"Principal Components\", \"Proportion of variance\",\n            self._on_cut_changed)\n\n        self.mainArea.layout().addWidget(self.plot)\n        self._update_normalize()\n\n    @Inputs.data\n    def set_data(self, data):\n        self.clear_messages()\n        self.clear()\n        self.information()\n        self.data = None\n        if not data:\n            self.clear_outputs()\n        if isinstance(data, SqlTable):\n            if data.approx_len() < AUTO_DL_LIMIT:\n                data = Table(data)\n            else:\n                self.information(\"Data has been sampled\")\n                data_sample = data.sample_time(1, no_cache=True)\n                data_sample.download_data(2000, partial=True)\n                data = Table(data_sample)\n        if isinstance(data, Table):\n            if not data.domain.attributes:\n                self.Error.no_features()\n                self.clear_outputs()\n                return\n            if not data:\n                self.Error.no_instances()\n                self.clear_outputs()\n                return\n\n        self._init_projector()\n\n        self.data = data\n        self.fit()\n\n    def fit(self):\n        self.clear()\n        self.Warning.trivial_components.clear()\n        if self.data is None:\n            return\n\n        data = self.data\n\n        if self.normalize:\n            self._pca_projector.preprocessors = \\\n                self._pca_preprocessors + [preprocess.Normalize(center=False)]\n        else:\n            self._pca_projector.preprocessors = self._pca_preprocessors\n\n        if not isinstance(data, SqlTable):\n            pca = self._pca_projector(data)\n            variance_ratio = pca.explained_variance_ratio_\n            cumulative = numpy.cumsum(variance_ratio)\n\n            if numpy.isfinite(cumulative[-1]):\n                self.components_spin.setRange(0, len(cumulative))\n                self._pca = pca\n                self._variance_ratio = variance_ratio\n                self._cumulative = cumulative\n                self._setup_plot()\n            else:\n                self.Warning.trivial_components()\n\n            self.unconditional_commit()\n\n    def clear(self):\n        self._pca = None\n        self._transformed = None\n        self._variance_ratio = None\n        self._cumulative = None\n        self.plot.clear_plot()\n\n    def clear_outputs(self):\n        self.Outputs.transformed_data.send(None)\n        self.Outputs.data.send(None)\n        self.Outputs.components.send(None)\n        self.Outputs.pca.send(self._pca_projector)\n\n    def _setup_plot(self):\n        if self._pca is None:\n            self.plot.clear_plot()\n            return\n\n        explained_ratio = self._variance_ratio\n        explained = self._cumulative\n        cutpos = self._nselected_components()\n        p = min(len(self._variance_ratio), self.maxp)\n\n        self.plot.update(\n            numpy.arange(1, p+1), [explained_ratio[:p], explained[:p]],\n            [Qt.red, Qt.darkYellow], cutpoint_x=cutpos, names=LINE_NAMES)\n\n        self._update_axis()\n\n    def _on_cut_changed(self, components):\n        if components == self.ncomponents \\\n                or self.ncomponents == 0 \\\n                or self._pca is not None \\\n                and components == len(self._variance_ratio):\n            return\n\n        self.ncomponents = components\n        if self._pca is not None:\n            var = self._cumulative[components - 1]\n            if numpy.isfinite(var):\n                self.variance_covered = int(var * 100)\n\n        self._invalidate_selection()\n\n    def _update_selection_component_spin(self):\n        # cut changed by \"ncomponents\" spin.\n        if self._pca is None:\n            self._invalidate_selection()\n            return\n\n        if self.ncomponents == 0:\n            # Special \"All\" value\n            cut = len(self._variance_ratio)\n        else:\n            cut = self.ncomponents\n\n        var = self._cumulative[cut - 1]\n        if numpy.isfinite(var):\n            self.variance_covered = int(var * 100)\n\n        self.plot.set_cut_point(cut)\n        self._invalidate_selection()\n\n    def _update_selection_variance_spin(self):\n        # cut changed by \"max variance\" spin.\n        if self._pca is None:\n            return\n\n        cut = numpy.searchsorted(self._cumulative,\n                                 self.variance_covered / 100.0) + 1\n        cut = min(cut, len(self._cumulative))\n        self.ncomponents = cut\n        self.plot.set_cut_point(cut)\n        self._invalidate_selection()\n\n    def _update_normalize(self):\n        self.fit()\n        if self.data is None:\n            self._invalidate_selection()\n\n    def _init_projector(self):\n        self._pca_projector = PCA(n_components=MAX_COMPONENTS, random_state=0)\n        self._pca_projector.component = self.ncomponents\n        self._pca_preprocessors = PCA.preprocessors\n\n    def _nselected_components(self):\n        \"\"\"Return the number of selected components.\"\"\"\n        if self._pca is None:\n            return 0\n\n        if self.ncomponents == 0:\n            # Special \"All\" value\n            max_comp = len(self._variance_ratio)\n        else:\n            max_comp = self.ncomponents\n\n        var_max = self._cumulative[max_comp - 1]\n        if var_max != numpy.floor(self.variance_covered / 100.0):\n            cut = max_comp\n            assert numpy.isfinite(var_max)\n            self.variance_covered = int(var_max * 100)\n        else:\n            self.ncomponents = cut = numpy.searchsorted(\n                self._cumulative, self.variance_covered / 100.0) + 1\n        return cut\n\n    def _invalidate_selection(self):\n        self.commit()\n\n    def _update_axis(self):\n        p = min(len(self._variance_ratio), self.maxp)\n        axis = self.plot.getAxis(\"bottom\")\n        d = max((p-1)//(self.axis_labels-1), 1)\n        axis.setTicks([[(i, str(i)) for i in range(1, p + 1, d)]])\n\n    def commit(self):\n        transformed = data = components = None\n        if self._pca is not None:\n            if self._transformed is None:\n                # Compute the full transform (MAX_COMPONENTS components) once.\n                self._transformed = self._pca(self.data)\n            transformed = self._transformed\n\n            domain = Domain(\n                transformed.domain.attributes[:self.ncomponents],\n                self.data.domain.class_vars,\n                self.data.domain.metas\n            )\n            transformed = transformed.from_table(domain, transformed)\n            # prevent caching new features by defining compute_value\n            proposed = [a.name for a in self._pca.orig_domain.attributes]\n            meta_name = get_unique_names(proposed, 'components')\n            dom = Domain(\n                [ContinuousVariable(name, compute_value=lambda _: None)\n                 for name in proposed],\n                metas=[StringVariable(name=meta_name)])\n            metas = numpy.array([['PC{}'.format(i + 1)\n                                  for i in range(self.ncomponents)]],\n                                dtype=object).T\n            components = Table(dom, self._pca.components_[:self.ncomponents],\n                               metas=metas)\n            components.name = 'components'\n\n            data_dom = Domain(\n                self.data.domain.attributes,\n                self.data.domain.class_vars,\n                self.data.domain.metas + domain.attributes)\n            data = Table.from_numpy(\n                data_dom, self.data.X, self.data.Y,\n                numpy.hstack((self.data.metas, transformed.X)),\n                ids=self.data.ids)\n\n        self._pca_projector.component = self.ncomponents\n        self.Outputs.transformed_data.send(transformed)\n        self.Outputs.components.send(components)\n        self.Outputs.data.send(data)\n        self.Outputs.pca.send(self._pca_projector)\n\n    def send_report(self):\n        if self.data is None:\n            return\n        self.report_items((\n            (\"Normalize data\", str(self.normalize)),\n            (\"Selected components\", self.ncomponents),\n            (\"Explained variance\", \"{:.3f} %\".format(self.variance_covered))\n        ))\n        self.report_plot()\n\n    @classmethod\n    def migrate_settings(cls, settings, version):\n        if \"variance_covered\" in settings:\n            # Due to the error in gh-1896 the variance_covered was persisted\n            # as a NaN value, causing a TypeError in the widgets `__init__`.\n            vc = settings[\"variance_covered\"]\n            if isinstance(vc, numbers.Real):\n                if numpy.isfinite(vc):\n                    vc = int(vc)\n                else:\n                    vc = 100\n                settings[\"variance_covered\"] = vc\n        if settings.get(\"ncomponents\", 0) > MAX_COMPONENTS:\n            settings[\"ncomponents\"] = MAX_COMPONENTS\n\n        # Remove old `decomposition_idx` when SVD was still included\n        settings.pop(\"decomposition_idx\", None)\n\n        # Remove RemotePCA settings\n        settings.pop(\"batch_size\", None)\n        settings.pop(\"address\", None)\n        settings.pop(\"auto_update\", None)",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 114,
                            "end_lineno": 142,
                            "parent": {
                                "file_name": "Orange/widgets/unsupervised/owpca.py",
                                "type": "class",
                                "name": "OWPCA",
                                "content": "class OWPCA(widget.OWWidget):\n    name = \"PCA\"\n    description = \"Principal component analysis with a scree-diagram.\"\n    icon = \"icons/PCA.svg\"\n    priority = 3050\n    keywords = [\"principal component analysis\", \"linear transformation\"]\n\n    class Inputs:\n        data = Input(\"Data\", Table)\n\n    class Outputs:\n        transformed_data = Output(\"Transformed Data\", Table, replaces=[\"Transformed data\"])\n        data = Output(\"Data\", Table, default=True)\n        components = Output(\"Components\", Table)\n        pca = Output(\"PCA\", PCA, dynamic=False)\n\n    ncomponents = settings.Setting(2)\n    variance_covered = settings.Setting(100)\n    auto_commit = settings.Setting(True)\n    normalize = settings.Setting(True)\n    maxp = settings.Setting(20)\n    axis_labels = settings.Setting(10)\n\n    graph_name = \"plot.plotItem\"\n\n    class Warning(widget.OWWidget.Warning):\n        trivial_components = widget.Msg(\n            \"All components of the PCA are trivial (explain 0 variance). \"\n            \"Input data is constant (or near constant).\")\n\n    class Error(widget.OWWidget.Error):\n        no_features = widget.Msg(\"At least 1 feature is required\")\n        no_instances = widget.Msg(\"At least 1 data instance is required\")\n\n    def __init__(self):\n        super().__init__()\n        self.data = None\n\n        self._pca = None\n        self._transformed = None\n        self._variance_ratio = None\n        self._cumulative = None\n        self._init_projector()\n\n        # Components Selection\n        box = gui.vBox(self.controlArea, \"Components Selection\")\n        form = QFormLayout()\n        box.layout().addLayout(form)\n\n        self.components_spin = gui.spin(\n            box, self, \"ncomponents\", 1, MAX_COMPONENTS,\n            callback=self._update_selection_component_spin,\n            keyboardTracking=False\n        )\n        self.components_spin.setSpecialValueText(\"All\")\n\n        self.variance_spin = gui.spin(\n            box, self, \"variance_covered\", 1, 100,\n            callback=self._update_selection_variance_spin,\n            keyboardTracking=False\n        )\n        self.variance_spin.setSuffix(\"%\")\n\n        form.addRow(\"Components:\", self.components_spin)\n        form.addRow(\"Explained variance:\", self.variance_spin)\n\n        # Options\n        self.options_box = gui.vBox(self.controlArea, \"Options\")\n        self.normalize_box = gui.checkBox(\n            self.options_box, self, \"normalize\",\n            \"Normalize variables\", callback=self._update_normalize\n        )\n\n        self.maxp_spin = gui.spin(\n            self.options_box, self, \"maxp\", 1, MAX_COMPONENTS,\n            label=\"Show only first\", callback=self._setup_plot,\n            keyboardTracking=False\n        )\n\n        self.controlArea.layout().addStretch()\n\n        gui.auto_apply(self.controlArea, self, \"auto_commit\")\n\n        self.plot = SliderGraph(\n            \"Principal Components\", \"Proportion of variance\",\n            self._on_cut_changed)\n\n        self.mainArea.layout().addWidget(self.plot)\n        self._update_normalize()\n\n    @Inputs.data\n    def set_data(self, data):\n        self.clear_messages()\n        self.clear()\n        self.information()\n        self.data = None\n        if not data:\n            self.clear_outputs()\n        if isinstance(data, SqlTable):\n            if data.approx_len() < AUTO_DL_LIMIT:\n                data = Table(data)\n            else:\n                self.information(\"Data has been sampled\")\n                data_sample = data.sample_time(1, no_cache=True)\n                data_sample.download_data(2000, partial=True)\n                data = Table(data_sample)\n        if isinstance(data, Table):\n            if not data.domain.attributes:\n                self.Error.no_features()\n                self.clear_outputs()\n                return\n            if not data:\n                self.Error.no_instances()\n                self.clear_outputs()\n                return\n\n        self._init_projector()\n\n        self.data = data\n        self.fit()\n\n    def fit(self):\n        self.clear()\n        self.Warning.trivial_components.clear()\n        if self.data is None:\n            return\n\n        data = self.data\n\n        if self.normalize:\n            self._pca_projector.preprocessors = \\\n                self._pca_preprocessors + [preprocess.Normalize(center=False)]\n        else:\n            self._pca_projector.preprocessors = self._pca_preprocessors\n\n        if not isinstance(data, SqlTable):\n            pca = self._pca_projector(data)\n            variance_ratio = pca.explained_variance_ratio_\n            cumulative = numpy.cumsum(variance_ratio)\n\n            if numpy.isfinite(cumulative[-1]):\n                self.components_spin.setRange(0, len(cumulative))\n                self._pca = pca\n                self._variance_ratio = variance_ratio\n                self._cumulative = cumulative\n                self._setup_plot()\n            else:\n                self.Warning.trivial_components()\n\n            self.unconditional_commit()\n\n    def clear(self):\n        self._pca = None\n        self._transformed = None\n        self._variance_ratio = None\n        self._cumulative = None\n        self.plot.clear_plot()\n\n    def clear_outputs(self):\n        self.Outputs.transformed_data.send(None)\n        self.Outputs.data.send(None)\n        self.Outputs.components.send(None)\n        self.Outputs.pca.send(self._pca_projector)\n\n    def _setup_plot(self):\n        if self._pca is None:\n            self.plot.clear_plot()\n            return\n\n        explained_ratio = self._variance_ratio\n        explained = self._cumulative\n        cutpos = self._nselected_components()\n        p = min(len(self._variance_ratio), self.maxp)\n\n        self.plot.update(\n            numpy.arange(1, p+1), [explained_ratio[:p], explained[:p]],\n            [Qt.red, Qt.darkYellow], cutpoint_x=cutpos, names=LINE_NAMES)\n\n        self._update_axis()\n\n    def _on_cut_changed(self, components):\n        if components == self.ncomponents \\\n                or self.ncomponents == 0 \\\n                or self._pca is not None \\\n                and components == len(self._variance_ratio):\n            return\n\n        self.ncomponents = components\n        if self._pca is not None:\n            var = self._cumulative[components - 1]\n            if numpy.isfinite(var):\n                self.variance_covered = int(var * 100)\n\n        self._invalidate_selection()\n\n    def _update_selection_component_spin(self):\n        # cut changed by \"ncomponents\" spin.\n        if self._pca is None:\n            self._invalidate_selection()\n            return\n\n        if self.ncomponents == 0:\n            # Special \"All\" value\n            cut = len(self._variance_ratio)\n        else:\n            cut = self.ncomponents\n\n        var = self._cumulative[cut - 1]\n        if numpy.isfinite(var):\n            self.variance_covered = int(var * 100)\n\n        self.plot.set_cut_point(cut)\n        self._invalidate_selection()\n\n    def _update_selection_variance_spin(self):\n        # cut changed by \"max variance\" spin.\n        if self._pca is None:\n            return\n\n        cut = numpy.searchsorted(self._cumulative,\n                                 self.variance_covered / 100.0) + 1\n        cut = min(cut, len(self._cumulative))\n        self.ncomponents = cut\n        self.plot.set_cut_point(cut)\n        self._invalidate_selection()\n\n    def _update_normalize(self):\n        self.fit()\n        if self.data is None:\n            self._invalidate_selection()\n\n    def _init_projector(self):\n        self._pca_projector = PCA(n_components=MAX_COMPONENTS, random_state=0)\n        self._pca_projector.component = self.ncomponents\n        self._pca_preprocessors = PCA.preprocessors\n\n    def _nselected_components(self):\n        \"\"\"Return the number of selected components.\"\"\"\n        if self._pca is None:\n            return 0\n\n        if self.ncomponents == 0:\n            # Special \"All\" value\n            max_comp = len(self._variance_ratio)\n        else:\n            max_comp = self.ncomponents\n\n        var_max = self._cumulative[max_comp - 1]\n        if var_max != numpy.floor(self.variance_covered / 100.0):\n            cut = max_comp\n            assert numpy.isfinite(var_max)\n            self.variance_covered = int(var_max * 100)\n        else:\n            self.ncomponents = cut = numpy.searchsorted(\n                self._cumulative, self.variance_covered / 100.0) + 1\n        return cut\n\n    def _invalidate_selection(self):\n        self.commit()\n\n    def _update_axis(self):\n        p = min(len(self._variance_ratio), self.maxp)\n        axis = self.plot.getAxis(\"bottom\")\n        d = max((p-1)//(self.axis_labels-1), 1)\n        axis.setTicks([[(i, str(i)) for i in range(1, p + 1, d)]])\n\n    def commit(self):\n        transformed = data = components = None\n        if self._pca is not None:\n            if self._transformed is None:\n                # Compute the full transform (MAX_COMPONENTS components) once.\n                self._transformed = self._pca(self.data)\n            transformed = self._transformed\n\n            domain = Domain(\n                transformed.domain.attributes[:self.ncomponents],\n                self.data.domain.class_vars,\n                self.data.domain.metas\n            )\n            transformed = transformed.from_table(domain, transformed)\n            # prevent caching new features by defining compute_value\n            proposed = [a.name for a in self._pca.orig_domain.attributes]\n            meta_name = get_unique_names(proposed, 'components')\n            dom = Domain(\n                [ContinuousVariable(name, compute_value=lambda _: None)\n                 for name in proposed],\n                metas=[StringVariable(name=meta_name)])\n            metas = numpy.array([['PC{}'.format(i + 1)\n                                  for i in range(self.ncomponents)]],\n                                dtype=object).T\n            components = Table(dom, self._pca.components_[:self.ncomponents],\n                               metas=metas)\n            components.name = 'components'\n\n            data_dom = Domain(\n                self.data.domain.attributes,\n                self.data.domain.class_vars,\n                self.data.domain.metas + domain.attributes)\n            data = Table.from_numpy(\n                data_dom, self.data.X, self.data.Y,\n                numpy.hstack((self.data.metas, transformed.X)),\n                ids=self.data.ids)\n\n        self._pca_projector.component = self.ncomponents\n        self.Outputs.transformed_data.send(transformed)\n        self.Outputs.components.send(components)\n        self.Outputs.data.send(data)\n        self.Outputs.pca.send(self._pca_projector)\n\n    def send_report(self):\n        if self.data is None:\n            return\n        self.report_items((\n            (\"Normalize data\", str(self.normalize)),\n            (\"Selected components\", self.ncomponents),\n            (\"Explained variance\", \"{:.3f} %\".format(self.variance_covered))\n        ))\n        self.report_plot()\n\n    @classmethod\n    def migrate_settings(cls, settings, version):\n        if \"variance_covered\" in settings:\n            # Due to the error in gh-1896 the variance_covered was persisted\n            # as a NaN value, causing a TypeError in the widgets `__init__`.\n            vc = settings[\"variance_covered\"]\n            if isinstance(vc, numbers.Real):\n                if numpy.isfinite(vc):\n                    vc = int(vc)\n                else:\n                    vc = 100\n                settings[\"variance_covered\"] = vc\n        if settings.get(\"ncomponents\", 0) > MAX_COMPONENTS:\n            settings[\"ncomponents\"] = MAX_COMPONENTS\n\n        # Remove old `decomposition_idx` when SVD was still included\n        settings.pop(\"decomposition_idx\", None)\n\n        # Remove RemotePCA settings\n        settings.pop(\"batch_size\", None)\n        settings.pop(\"address\", None)\n        settings.pop(\"auto_update\", None)",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 23,
                                "end_lineno": 363,
                                "parent": null
                            }
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "# Test methods with long descriptive names can omit docstrings\n# pylint: disable=missing-docstring, protected-access\nimport unittest\nfrom unittest.mock import patch, Mock\n\nimport numpy as np\n\nfrom Orange.data import Table, Domain, ContinuousVariable, TimeVariable\nfrom Orange.preprocess import preprocess\nfrom Orange.preprocess.preprocess import Normalize\nfrom Orange.widgets.tests.base import WidgetTest\nfrom Orange.widgets.tests.utils import table_dense_sparse, possible_duplicate_table\nfrom Orange.widgets.unsupervised.owpca import OWPCA\nfrom Orange.tests import test_filename\nfrom sklearn.utils import check_random_state\nfrom sklearn.utils.extmath import svd_flip\n\n\nclass TestOWPCA(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(OWPCA)  # type: OWPCA\n        self.iris = Table(\"iris\")  # type: Table\n\n    def test_set_variance100(self):\n        self.widget.set_data(self.iris)\n        self.widget.variance_covered = 100\n        self.widget._update_selection_variance_spin()\n\n    def test_constant_data(self):\n        data = self.iris[::5]\n        data.X[:, :] = 1.0\n        # Ignore the warning: the test checks whether the widget shows\n        # Warning.trivial_components when this happens\n        with np.errstate(invalid=\"ignore\"):\n            self.send_signal(self.widget.Inputs.data, data)\n        self.assertTrue(self.widget.Warning.trivial_components.is_shown())\n        self.assertIsNone(self.get_output(self.widget.Outputs.transformed_data))\n        self.assertIsNone(self.get_output(self.widget.Outputs.components))\n\n    def test_empty_data(self):\n        \"\"\" Check widget for dataset with no rows and for dataset with no attributes \"\"\"\n        self.send_signal(self.widget.Inputs.data, self.iris[:0])\n        self.assertTrue(self.widget.Error.no_instances.is_shown())\n\n        domain = Domain([], None, self.iris.domain.variables)\n        new_data = Table.from_table(domain, self.iris)\n        self.send_signal(self.widget.Inputs.data, new_data)\n        self.assertTrue(self.widget.Error.no_features.is_shown())\n        self.assertFalse(self.widget.Error.no_instances.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(self.widget.Error.no_features.is_shown())\n\n    def test_limit_components(self):\n        X = np.random.RandomState(0).rand(101, 101)\n        data = Table.from_numpy(None, X)\n        self.widget.ncomponents = 100\n        self.send_signal(self.widget.Inputs.data, data)\n        tran = self.get_output(self.widget.Outputs.transformed_data)\n        self.assertEqual(len(tran.domain.attributes), 100)\n        self.widget.ncomponents = 101  # should not be accesible\n        with self.assertRaises(IndexError):\n            self.send_signal(self.widget.Inputs.data, data)\n\n    def test_migrate_settings_limits_components(self):\n        settings = dict(ncomponents=10)\n        OWPCA.migrate_settings(settings, 0)\n        self.assertEqual(settings['ncomponents'], 10)\n        settings = dict(ncomponents=101)\n        OWPCA.migrate_settings(settings, 0)\n        self.assertEqual(settings['ncomponents'], 100)\n\n    def test_migrate_settings_changes_variance_covered_to_int(self):\n        settings = dict(variance_covered=17.5)\n        OWPCA.migrate_settings(settings, 0)\n        self.assertEqual(settings[\"variance_covered\"], 17)\n\n        settings = dict(variance_covered=float('nan'))\n        OWPCA.migrate_settings(settings, 0)\n        self.assertEqual(settings[\"variance_covered\"], 100)\n\n    def test_variance_shown(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.widget.maxp = 2\n        self.widget._setup_plot()\n        var2 = self.widget.variance_covered\n        self.widget.ncomponents = 3\n        self.widget._update_selection_component_spin()\n        var3 = self.widget.variance_covered\n        self.assertGreater(var3, var2)\n\n    def test_unique_domain_components(self):\n        table = possible_duplicate_table('components')\n        self.send_signal(self.widget.Inputs.data, table)\n        out = self.get_output(self.widget.Outputs.components)\n        self.assertEqual(out.domain.metas[0].name, 'components (1)')\n\n    def test_sparse_data(self):\n        \"\"\"Check that PCA returns the same results for both dense and sparse data.\"\"\"\n        dense_data, sparse_data = self.iris, self.iris.to_sparse()\n\n        def _compute_projection(data):\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            result = self.get_output(self.widget.Outputs.transformed_data)\n            self.send_signal(self.widget.Inputs.data, None)\n            return result\n\n        # Disable normalization\n        self.widget.controls.normalize.setChecked(False)\n        dense_pca = _compute_projection(dense_data)\n        sparse_pca = _compute_projection(sparse_data)\n        np.testing.assert_almost_equal(dense_pca.X, sparse_pca.X)\n\n        # Enable normalization\n        self.widget.controls.normalize.setChecked(True)\n        dense_pca = _compute_projection(dense_data)\n        sparse_pca = _compute_projection(sparse_data)\n        np.testing.assert_almost_equal(dense_pca.X, sparse_pca.X)\n\n    def test_all_components_continuous(self):\n        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))\n        # GH-2329 only occurred on TimeVariables when normalize=False\n        self.assertTrue(any(isinstance(a, TimeVariable)\n                            for a in data.domain.attributes))\n\n        self.widget.normalize = False\n        self.widget._update_normalize()     # pylint: disable=protected-access\n        self.widget.set_data(data)\n\n        components = self.get_output(self.widget.Outputs.components)\n        self.assertTrue(all(type(a) is ContinuousVariable   # pylint: disable=unidiomatic-typecheck\n                            for a in components.domain.attributes),\n                        \"Some variables aren't of type ContinuousVariable\")\n\n    @table_dense_sparse\n    def test_normalize_data(self, prepare_table):\n        \"\"\"Check that normalization is called at the proper times.\"\"\"\n        data = prepare_table(self.iris)\n\n        # Enable checkbox\n        self.widget.controls.normalize.setChecked(True)\n        self.assertTrue(self.widget.controls.normalize.isChecked())\n        with patch.object(preprocess, \"Normalize\", wraps=Normalize) as normalize:\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            self.assertTrue(self.widget.controls.normalize.isEnabled())\n            normalize.assert_called_once()\n\n        # Disable checkbox\n        self.widget.controls.normalize.setChecked(False)\n        self.assertFalse(self.widget.controls.normalize.isChecked())\n        with patch.object(preprocess, \"Normalize\", wraps=Normalize) as normalize:\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            self.assertTrue(self.widget.controls.normalize.isEnabled())\n            normalize.assert_not_called()\n\n    @table_dense_sparse\n    def test_normalization_variance(self, prepare_table):\n        data = prepare_table(self.iris)\n        self.widget.ncomponents = 2\n\n        # Enable normalization\n        self.widget.controls.normalize.setChecked(True)\n        self.assertTrue(self.widget.normalize)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.wait_until_stop_blocking()\n        variance_normalized = self.widget.variance_covered\n\n        # Disable normalization\n        self.widget.controls.normalize.setChecked(False)\n        self.assertFalse(self.widget.normalize)\n        self.wait_until_stop_blocking()\n        variance_unnormalized = self.widget.variance_covered\n\n        # normalized data will have lower covered variance\n        self.assertLess(variance_normalized, variance_unnormalized)\n\n    @table_dense_sparse\n    def test_normalized_gives_correct_result(self, prepare_table):\n        \"\"\"Make sure that normalization through widget gives correct result.\"\"\"\n        # Randomly set some values to zero\n        random_state = check_random_state(42)\n        mask = random_state.beta(1, 2, size=self.iris.X.shape) > 0.5\n        self.iris.X[mask] = 0\n\n        data = prepare_table(self.iris)\n\n        # Enable normalization and run data through widget\n        self.widget.controls.normalize.setChecked(True)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.wait_until_stop_blocking()\n        widget_result = self.get_output(self.widget.Outputs.transformed_data)\n\n        # Compute the correct embedding\n        x = self.iris.X\n        x = (x - x.mean(0)) / x.std(0)\n        U, S, Va = np.linalg.svd(x)\n        U, S, Va = U[:, :2], S[:2], Va[:2]\n        U, Va = svd_flip(U, Va)\n        pca_embedding = U * S\n\n        np.testing.assert_almost_equal(widget_result.X, pca_embedding)\n\n    def test_do_not_mask_features(self):\n        # the widget used to replace cached variables when creating the\n        # components output (until 20170726)\n        data = Table(\"iris.tab\")\n        self.widget.set_data(data)\n        ndata = Table(\"iris.tab\")\n        self.assertEqual(data.domain[0], ndata.domain[0])\n\n    def test_on_cut_changed(self):\n        widget = self.widget\n        widget.ncomponents = 2\n        invalidate = widget._invalidate_selection = Mock()\n        widget._on_cut_changed(2)\n        invalidate.assert_not_called()\n        widget._on_cut_changed(3)\n        invalidate.assert_called()\n\n        widget.ncomponents = 0  # Take all components\n        invalidate.reset_mock()\n        widget._on_cut_changed(1)\n        invalidate.assert_not_called()\n        self.assertEqual(widget.ncomponents, 0)\n\n    def test_output_data(self):\n        widget = self.widget\n        widget.ncomponents = 2\n        domain = Domain(self.iris.domain.attributes[:3],\n                        self.iris.domain.class_var,\n                        self.iris.domain.attributes[3:])\n        iris = self.iris.transform(domain)\n        self.send_signal(widget.Inputs.data, iris)\n        output = self.get_output(widget.Outputs.data)\n        outdom = output.domain\n        self.assertEqual(domain.attributes, outdom.attributes)\n        self.assertEqual(domain.class_var, outdom.class_var)\n        self.assertEqual(domain.metas, outdom.metas[:1])\n        self.assertEqual(len(outdom.metas), 3)\n        np.testing.assert_equal(iris.X, output.X)\n        np.testing.assert_equal(iris.Y, output.Y)\n        np.testing.assert_equal(iris.metas[:, 0], output.metas[:, 0])\n\n        trans = self.get_output(widget.Outputs.transformed_data)\n        self.assertEqual(trans.domain.attributes, outdom.metas[1:])\n        np.testing.assert_equal(trans.X, output.metas[:, 1:])\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n",
            "new_file_content": "# Test methods with long descriptive names can omit docstrings\n# pylint: disable=missing-docstring, protected-access\nimport unittest\nfrom unittest.mock import patch, Mock\n\nimport numpy as np\n\nfrom Orange.data import Table, Domain, ContinuousVariable, TimeVariable\nfrom Orange.preprocess import preprocess\nfrom Orange.preprocess.preprocess import Normalize\nfrom Orange.widgets.tests.base import WidgetTest\nfrom Orange.widgets.tests.utils import table_dense_sparse, possible_duplicate_table\nfrom Orange.widgets.unsupervised.owpca import OWPCA\nfrom Orange.tests import test_filename\nfrom sklearn.utils import check_random_state\nfrom sklearn.utils.extmath import svd_flip\n\n\nclass TestOWPCA(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(OWPCA)  # type: OWPCA\n        self.iris = Table(\"iris\")  # type: Table\n\n    def test_set_variance100(self):\n        self.widget.set_data(self.iris)\n        self.widget.variance_covered = 100\n        self.widget._update_selection_variance_spin()\n\n    def test_constant_data(self):\n        data = self.iris[::5]\n        data.X[:, :] = 1.0\n        # Ignore the warning: the test checks whether the widget shows\n        # Warning.trivial_components when this happens\n        with np.errstate(invalid=\"ignore\"):\n            self.send_signal(self.widget.Inputs.data, data)\n        self.assertTrue(self.widget.Warning.trivial_components.is_shown())\n        self.assertIsNone(self.get_output(self.widget.Outputs.transformed_data))\n        self.assertIsNone(self.get_output(self.widget.Outputs.components))\n\n    def test_empty_data(self):\n        \"\"\" Check widget for dataset with no rows and for dataset with no attributes \"\"\"\n        self.send_signal(self.widget.Inputs.data, self.iris[:0])\n        self.assertTrue(self.widget.Error.no_instances.is_shown())\n\n        domain = Domain([], None, self.iris.domain.variables)\n        new_data = Table.from_table(domain, self.iris)\n        self.send_signal(self.widget.Inputs.data, new_data)\n        self.assertTrue(self.widget.Error.no_features.is_shown())\n        self.assertFalse(self.widget.Error.no_instances.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(self.widget.Error.no_features.is_shown())\n\n    def test_limit_components(self):\n        X = np.random.RandomState(0).rand(101, 101)\n        data = Table.from_numpy(None, X)\n        self.widget.ncomponents = 100\n        self.send_signal(self.widget.Inputs.data, data)\n        tran = self.get_output(self.widget.Outputs.transformed_data)\n        self.assertEqual(len(tran.domain.attributes), 100)\n        self.widget.ncomponents = 101  # should not be accesible\n        with self.assertRaises(IndexError):\n            self.send_signal(self.widget.Inputs.data, data)\n\n    def test_migrate_settings_limits_components(self):\n        settings = dict(ncomponents=10)\n        OWPCA.migrate_settings(settings, 0)\n        self.assertEqual(settings['ncomponents'], 10)\n        settings = dict(ncomponents=101)\n        OWPCA.migrate_settings(settings, 0)\n        self.assertEqual(settings['ncomponents'], 100)\n\n    def test_migrate_settings_changes_variance_covered_to_int(self):\n        settings = dict(variance_covered=17.5)\n        OWPCA.migrate_settings(settings, 0)\n        self.assertEqual(settings[\"variance_covered\"], 17)\n\n        settings = dict(variance_covered=float('nan'))\n        OWPCA.migrate_settings(settings, 0)\n        self.assertEqual(settings[\"variance_covered\"], 100)\n\n    def test_variance_shown(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.widget.maxp = 2\n        self.widget._setup_plot()\n        var2 = self.widget.variance_covered\n        self.widget.ncomponents = 3\n        self.widget._update_selection_component_spin()\n        var3 = self.widget.variance_covered\n        self.assertGreater(var3, var2)\n\n    def test_unique_domain_components(self):\n        table = possible_duplicate_table('components')\n        self.send_signal(self.widget.Inputs.data, table)\n        out = self.get_output(self.widget.Outputs.components)\n        self.assertEqual(out.domain.metas[0].name, 'components (1)')\n\n    def test_sparse_data(self):\n        \"\"\"Check that PCA returns the same results for both dense and sparse data.\"\"\"\n        dense_data, sparse_data = self.iris, self.iris.to_sparse()\n\n        def _compute_projection(data):\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            result = self.get_output(self.widget.Outputs.transformed_data)\n            self.send_signal(self.widget.Inputs.data, None)\n            return result\n\n        # Disable normalization\n        self.widget.controls.normalize.setChecked(False)\n        dense_pca = _compute_projection(dense_data)\n        sparse_pca = _compute_projection(sparse_data)\n        np.testing.assert_almost_equal(dense_pca.X, sparse_pca.X)\n\n        # Enable normalization\n        self.widget.controls.normalize.setChecked(True)\n        dense_pca = _compute_projection(dense_data)\n        sparse_pca = _compute_projection(sparse_data)\n        np.testing.assert_almost_equal(dense_pca.X, sparse_pca.X)\n\n    def test_all_components_continuous(self):\n        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))\n        # GH-2329 only occurred on TimeVariables when normalize=False\n        self.assertTrue(any(isinstance(a, TimeVariable)\n                            for a in data.domain.attributes))\n\n        self.widget.normalize = False\n        self.widget._update_normalize()     # pylint: disable=protected-access\n        self.widget.set_data(data)\n\n        components = self.get_output(self.widget.Outputs.components)\n        self.assertTrue(all(type(a) is ContinuousVariable   # pylint: disable=unidiomatic-typecheck\n                            for a in components.domain.attributes),\n                        \"Some variables aren't of type ContinuousVariable\")\n\n    @table_dense_sparse\n    def test_normalize_data(self, prepare_table):\n        \"\"\"Check that normalization is called at the proper times.\"\"\"\n        data = prepare_table(self.iris)\n\n        # Enable checkbox\n        self.widget.controls.normalize.setChecked(True)\n        self.assertTrue(self.widget.controls.normalize.isChecked())\n        with patch.object(preprocess, \"Normalize\", wraps=Normalize) as normalize:\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            self.assertTrue(self.widget.controls.normalize.isEnabled())\n            normalize.assert_called_once()\n\n        # Disable checkbox\n        self.widget.controls.normalize.setChecked(False)\n        self.assertFalse(self.widget.controls.normalize.isChecked())\n        with patch.object(preprocess, \"Normalize\", wraps=Normalize) as normalize:\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            self.assertTrue(self.widget.controls.normalize.isEnabled())\n            normalize.assert_not_called()\n\n    @table_dense_sparse\n    def test_normalization_variance(self, prepare_table):\n        data = prepare_table(self.iris)\n        self.widget.ncomponents = 2\n\n        # Enable normalization\n        self.widget.controls.normalize.setChecked(True)\n        self.assertTrue(self.widget.normalize)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.wait_until_stop_blocking()\n        variance_normalized = self.widget.variance_covered\n\n        # Disable normalization\n        self.widget.controls.normalize.setChecked(False)\n        self.assertFalse(self.widget.normalize)\n        self.wait_until_stop_blocking()\n        variance_unnormalized = self.widget.variance_covered\n\n        # normalized data will have lower covered variance\n        self.assertLess(variance_normalized, variance_unnormalized)\n\n    @table_dense_sparse\n    def test_normalized_gives_correct_result(self, prepare_table):\n        \"\"\"Make sure that normalization through widget gives correct result.\"\"\"\n        # Randomly set some values to zero\n        random_state = check_random_state(42)\n        mask = random_state.beta(1, 2, size=self.iris.X.shape) > 0.5\n        self.iris.X[mask] = 0\n\n        data = prepare_table(self.iris)\n\n        # Enable normalization and run data through widget\n        self.widget.controls.normalize.setChecked(True)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.wait_until_stop_blocking()\n        widget_result = self.get_output(self.widget.Outputs.transformed_data)\n\n        # Compute the correct embedding\n        x = self.iris.X\n        x = (x - x.mean(0)) / x.std(0)\n        U, S, Va = np.linalg.svd(x)\n        U, S, Va = U[:, :2], S[:2], Va[:2]\n        U, Va = svd_flip(U, Va)\n        pca_embedding = U * S\n\n        np.testing.assert_almost_equal(widget_result.X, pca_embedding)\n\n    def test_do_not_mask_features(self):\n        # the widget used to replace cached variables when creating the\n        # components output (until 20170726)\n        data = Table(\"iris.tab\")\n        self.widget.set_data(data)\n        ndata = Table(\"iris.tab\")\n        self.assertEqual(data.domain[0], ndata.domain[0])\n\n    def test_on_cut_changed(self):\n        widget = self.widget\n        widget.ncomponents = 2\n        invalidate = widget._invalidate_selection = Mock()\n        widget._on_cut_changed(2)\n        invalidate.assert_not_called()\n        widget._on_cut_changed(3)\n        invalidate.assert_called()\n\n        widget.ncomponents = 0  # Take all components\n        invalidate.reset_mock()\n        widget._on_cut_changed(1)\n        invalidate.assert_not_called()\n        self.assertEqual(widget.ncomponents, 0)\n\n    def test_output_data(self):\n        widget = self.widget\n        widget.ncomponents = 2\n        domain = Domain(self.iris.domain.attributes[:3],\n                        self.iris.domain.class_var,\n                        self.iris.domain.attributes[3:])\n        iris = self.iris.transform(domain)\n        self.send_signal(widget.Inputs.data, iris)\n        output = self.get_output(widget.Outputs.data)\n        outdom = output.domain\n        self.assertEqual(domain.attributes, outdom.attributes)\n        self.assertEqual(domain.class_var, outdom.class_var)\n        self.assertEqual(domain.metas, outdom.metas[:1])\n        self.assertEqual(len(outdom.metas), 3)\n        np.testing.assert_equal(iris.X, output.X)\n        np.testing.assert_equal(iris.Y, output.Y)\n        np.testing.assert_equal(iris.metas[:, 0], output.metas[:, 0])\n\n        trans = self.get_output(widget.Outputs.transformed_data)\n        self.assertEqual(trans.domain.attributes, outdom.metas[1:])\n        np.testing.assert_equal(trans.X, output.metas[:, 1:])\n\n        self.send_signal(widget.Inputs.data, None)\n        output = self.get_output(widget.Outputs.data)\n        self.assertIsNone(output)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n",
            "header": {
                "file": {
                    "path": "Orange/widgets/unsupervised/tests/test_owpca.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "01afae171",
                "new_commit_hash": "55ced0798",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/Orange/widgets/unsupervised/tests/test_owpca.py"
            },
            "plus_file": {
                "path": "b/Orange/widgets/unsupervised/tests/test_owpca.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 248,
                            "length": 6
                        },
                        "new_range": {
                            "start": 248,
                            "length": 10
                        },
                        "section": "class TestOWPCA(WidgetTest):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        self.assertEqual(trans.domain.attributes, outdom.metas[1:])",
                                "type": "context"
                            },
                            {
                                "content": "        np.testing.assert_equal(trans.X, output.metas[:, 1:])",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "        self.send_signal(widget.Inputs.data, None)",
                                "type": "added"
                            },
                            {
                                "content": "        output = self.get_output(widget.Outputs.data)",
                                "type": "added"
                            },
                            {
                                "content": "        self.assertIsNone(output)",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "if __name__ == \"__main__\":",
                                "type": "context"
                            },
                            {
                                "content": "    unittest.main()",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "Orange/widgets/unsupervised/tests/test_owpca.py",
                            "type": "class",
                            "name": "TestOWPCA",
                            "content": "class TestOWPCA(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(OWPCA)  # type: OWPCA\n        self.iris = Table(\"iris\")  # type: Table\n\n    def test_set_variance100(self):\n        self.widget.set_data(self.iris)\n        self.widget.variance_covered = 100\n        self.widget._update_selection_variance_spin()\n\n    def test_constant_data(self):\n        data = self.iris[::5]\n        data.X[:, :] = 1.0\n        # Ignore the warning: the test checks whether the widget shows\n        # Warning.trivial_components when this happens\n        with np.errstate(invalid=\"ignore\"):\n            self.send_signal(self.widget.Inputs.data, data)\n        self.assertTrue(self.widget.Warning.trivial_components.is_shown())\n        self.assertIsNone(self.get_output(self.widget.Outputs.transformed_data))\n        self.assertIsNone(self.get_output(self.widget.Outputs.components))\n\n    def test_empty_data(self):\n        \"\"\" Check widget for dataset with no rows and for dataset with no attributes \"\"\"\n        self.send_signal(self.widget.Inputs.data, self.iris[:0])\n        self.assertTrue(self.widget.Error.no_instances.is_shown())\n\n        domain = Domain([], None, self.iris.domain.variables)\n        new_data = Table.from_table(domain, self.iris)\n        self.send_signal(self.widget.Inputs.data, new_data)\n        self.assertTrue(self.widget.Error.no_features.is_shown())\n        self.assertFalse(self.widget.Error.no_instances.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(self.widget.Error.no_features.is_shown())\n\n    def test_limit_components(self):\n        X = np.random.RandomState(0).rand(101, 101)\n        data = Table.from_numpy(None, X)\n        self.widget.ncomponents = 100\n        self.send_signal(self.widget.Inputs.data, data)\n        tran = self.get_output(self.widget.Outputs.transformed_data)\n        self.assertEqual(len(tran.domain.attributes), 100)\n        self.widget.ncomponents = 101  # should not be accesible\n        with self.assertRaises(IndexError):\n            self.send_signal(self.widget.Inputs.data, data)\n\n    def test_migrate_settings_limits_components(self):\n        settings = dict(ncomponents=10)\n        OWPCA.migrate_settings(settings, 0)\n        self.assertEqual(settings['ncomponents'], 10)\n        settings = dict(ncomponents=101)\n        OWPCA.migrate_settings(settings, 0)\n        self.assertEqual(settings['ncomponents'], 100)\n\n    def test_migrate_settings_changes_variance_covered_to_int(self):\n        settings = dict(variance_covered=17.5)\n        OWPCA.migrate_settings(settings, 0)\n        self.assertEqual(settings[\"variance_covered\"], 17)\n\n        settings = dict(variance_covered=float('nan'))\n        OWPCA.migrate_settings(settings, 0)\n        self.assertEqual(settings[\"variance_covered\"], 100)\n\n    def test_variance_shown(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.widget.maxp = 2\n        self.widget._setup_plot()\n        var2 = self.widget.variance_covered\n        self.widget.ncomponents = 3\n        self.widget._update_selection_component_spin()\n        var3 = self.widget.variance_covered\n        self.assertGreater(var3, var2)\n\n    def test_unique_domain_components(self):\n        table = possible_duplicate_table('components')\n        self.send_signal(self.widget.Inputs.data, table)\n        out = self.get_output(self.widget.Outputs.components)\n        self.assertEqual(out.domain.metas[0].name, 'components (1)')\n\n    def test_sparse_data(self):\n        \"\"\"Check that PCA returns the same results for both dense and sparse data.\"\"\"\n        dense_data, sparse_data = self.iris, self.iris.to_sparse()\n\n        def _compute_projection(data):\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            result = self.get_output(self.widget.Outputs.transformed_data)\n            self.send_signal(self.widget.Inputs.data, None)\n            return result\n\n        # Disable normalization\n        self.widget.controls.normalize.setChecked(False)\n        dense_pca = _compute_projection(dense_data)\n        sparse_pca = _compute_projection(sparse_data)\n        np.testing.assert_almost_equal(dense_pca.X, sparse_pca.X)\n\n        # Enable normalization\n        self.widget.controls.normalize.setChecked(True)\n        dense_pca = _compute_projection(dense_data)\n        sparse_pca = _compute_projection(sparse_data)\n        np.testing.assert_almost_equal(dense_pca.X, sparse_pca.X)\n\n    def test_all_components_continuous(self):\n        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))\n        # GH-2329 only occurred on TimeVariables when normalize=False\n        self.assertTrue(any(isinstance(a, TimeVariable)\n                            for a in data.domain.attributes))\n\n        self.widget.normalize = False\n        self.widget._update_normalize()     # pylint: disable=protected-access\n        self.widget.set_data(data)\n\n        components = self.get_output(self.widget.Outputs.components)\n        self.assertTrue(all(type(a) is ContinuousVariable   # pylint: disable=unidiomatic-typecheck\n                            for a in components.domain.attributes),\n                        \"Some variables aren't of type ContinuousVariable\")\n\n    @table_dense_sparse\n    def test_normalize_data(self, prepare_table):\n        \"\"\"Check that normalization is called at the proper times.\"\"\"\n        data = prepare_table(self.iris)\n\n        # Enable checkbox\n        self.widget.controls.normalize.setChecked(True)\n        self.assertTrue(self.widget.controls.normalize.isChecked())\n        with patch.object(preprocess, \"Normalize\", wraps=Normalize) as normalize:\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            self.assertTrue(self.widget.controls.normalize.isEnabled())\n            normalize.assert_called_once()\n\n        # Disable checkbox\n        self.widget.controls.normalize.setChecked(False)\n        self.assertFalse(self.widget.controls.normalize.isChecked())\n        with patch.object(preprocess, \"Normalize\", wraps=Normalize) as normalize:\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            self.assertTrue(self.widget.controls.normalize.isEnabled())\n            normalize.assert_not_called()\n\n    @table_dense_sparse\n    def test_normalization_variance(self, prepare_table):\n        data = prepare_table(self.iris)\n        self.widget.ncomponents = 2\n\n        # Enable normalization\n        self.widget.controls.normalize.setChecked(True)\n        self.assertTrue(self.widget.normalize)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.wait_until_stop_blocking()\n        variance_normalized = self.widget.variance_covered\n\n        # Disable normalization\n        self.widget.controls.normalize.setChecked(False)\n        self.assertFalse(self.widget.normalize)\n        self.wait_until_stop_blocking()\n        variance_unnormalized = self.widget.variance_covered\n\n        # normalized data will have lower covered variance\n        self.assertLess(variance_normalized, variance_unnormalized)\n\n    @table_dense_sparse\n    def test_normalized_gives_correct_result(self, prepare_table):\n        \"\"\"Make sure that normalization through widget gives correct result.\"\"\"\n        # Randomly set some values to zero\n        random_state = check_random_state(42)\n        mask = random_state.beta(1, 2, size=self.iris.X.shape) > 0.5\n        self.iris.X[mask] = 0\n\n        data = prepare_table(self.iris)\n\n        # Enable normalization and run data through widget\n        self.widget.controls.normalize.setChecked(True)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.wait_until_stop_blocking()\n        widget_result = self.get_output(self.widget.Outputs.transformed_data)\n\n        # Compute the correct embedding\n        x = self.iris.X\n        x = (x - x.mean(0)) / x.std(0)\n        U, S, Va = np.linalg.svd(x)\n        U, S, Va = U[:, :2], S[:2], Va[:2]\n        U, Va = svd_flip(U, Va)\n        pca_embedding = U * S\n\n        np.testing.assert_almost_equal(widget_result.X, pca_embedding)\n\n    def test_do_not_mask_features(self):\n        # the widget used to replace cached variables when creating the\n        # components output (until 20170726)\n        data = Table(\"iris.tab\")\n        self.widget.set_data(data)\n        ndata = Table(\"iris.tab\")\n        self.assertEqual(data.domain[0], ndata.domain[0])\n\n    def test_on_cut_changed(self):\n        widget = self.widget\n        widget.ncomponents = 2\n        invalidate = widget._invalidate_selection = Mock()\n        widget._on_cut_changed(2)\n        invalidate.assert_not_called()\n        widget._on_cut_changed(3)\n        invalidate.assert_called()\n\n        widget.ncomponents = 0  # Take all components\n        invalidate.reset_mock()\n        widget._on_cut_changed(1)\n        invalidate.assert_not_called()\n        self.assertEqual(widget.ncomponents, 0)\n\n    def test_output_data(self):\n        widget = self.widget\n        widget.ncomponents = 2\n        domain = Domain(self.iris.domain.attributes[:3],\n                        self.iris.domain.class_var,\n                        self.iris.domain.attributes[3:])\n        iris = self.iris.transform(domain)\n        self.send_signal(widget.Inputs.data, iris)\n        output = self.get_output(widget.Outputs.data)\n        outdom = output.domain\n        self.assertEqual(domain.attributes, outdom.attributes)\n        self.assertEqual(domain.class_var, outdom.class_var)\n        self.assertEqual(domain.metas, outdom.metas[:1])\n        self.assertEqual(len(outdom.metas), 3)\n        np.testing.assert_equal(iris.X, output.X)\n        np.testing.assert_equal(iris.Y, output.Y)\n        np.testing.assert_equal(iris.metas[:, 0], output.metas[:, 0])\n\n        trans = self.get_output(widget.Outputs.transformed_data)\n        self.assertEqual(trans.domain.attributes, outdom.metas[1:])\n        np.testing.assert_equal(trans.X, output.metas[:, 1:])\n\n        self.send_signal(widget.Inputs.data, None)\n        output = self.get_output(widget.Outputs.data)\n        self.assertIsNone(output)",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 19,
                            "end_lineno": 253,
                            "parent": null
                        },
                        {
                            "file_name": "Orange/widgets/unsupervised/tests/test_owpca.py",
                            "type": "method",
                            "name": "TestOWPCA.test_output_data",
                            "content": "class TestOWPCA(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(OWPCA)  # type: OWPCA\n        self.iris = Table(\"iris\")  # type: Table\n\n    def test_set_variance100(self):\n        self.widget.set_data(self.iris)\n        self.widget.variance_covered = 100\n        self.widget._update_selection_variance_spin()\n\n    def test_constant_data(self):\n        data = self.iris[::5]\n        data.X[:, :] = 1.0\n        # Ignore the warning: the test checks whether the widget shows\n        # Warning.trivial_components when this happens\n        with np.errstate(invalid=\"ignore\"):\n            self.send_signal(self.widget.Inputs.data, data)\n        self.assertTrue(self.widget.Warning.trivial_components.is_shown())\n        self.assertIsNone(self.get_output(self.widget.Outputs.transformed_data))\n        self.assertIsNone(self.get_output(self.widget.Outputs.components))\n\n    def test_empty_data(self):\n        \"\"\" Check widget for dataset with no rows and for dataset with no attributes \"\"\"\n        self.send_signal(self.widget.Inputs.data, self.iris[:0])\n        self.assertTrue(self.widget.Error.no_instances.is_shown())\n\n        domain = Domain([], None, self.iris.domain.variables)\n        new_data = Table.from_table(domain, self.iris)\n        self.send_signal(self.widget.Inputs.data, new_data)\n        self.assertTrue(self.widget.Error.no_features.is_shown())\n        self.assertFalse(self.widget.Error.no_instances.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(self.widget.Error.no_features.is_shown())\n\n    def test_limit_components(self):\n        X = np.random.RandomState(0).rand(101, 101)\n        data = Table.from_numpy(None, X)\n        self.widget.ncomponents = 100\n        self.send_signal(self.widget.Inputs.data, data)\n        tran = self.get_output(self.widget.Outputs.transformed_data)\n        self.assertEqual(len(tran.domain.attributes), 100)\n        self.widget.ncomponents = 101  # should not be accesible\n        with self.assertRaises(IndexError):\n            self.send_signal(self.widget.Inputs.data, data)\n\n    def test_migrate_settings_limits_components(self):\n        settings = dict(ncomponents=10)\n        OWPCA.migrate_settings(settings, 0)\n        self.assertEqual(settings['ncomponents'], 10)\n        settings = dict(ncomponents=101)\n        OWPCA.migrate_settings(settings, 0)\n        self.assertEqual(settings['ncomponents'], 100)\n\n    def test_migrate_settings_changes_variance_covered_to_int(self):\n        settings = dict(variance_covered=17.5)\n        OWPCA.migrate_settings(settings, 0)\n        self.assertEqual(settings[\"variance_covered\"], 17)\n\n        settings = dict(variance_covered=float('nan'))\n        OWPCA.migrate_settings(settings, 0)\n        self.assertEqual(settings[\"variance_covered\"], 100)\n\n    def test_variance_shown(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.widget.maxp = 2\n        self.widget._setup_plot()\n        var2 = self.widget.variance_covered\n        self.widget.ncomponents = 3\n        self.widget._update_selection_component_spin()\n        var3 = self.widget.variance_covered\n        self.assertGreater(var3, var2)\n\n    def test_unique_domain_components(self):\n        table = possible_duplicate_table('components')\n        self.send_signal(self.widget.Inputs.data, table)\n        out = self.get_output(self.widget.Outputs.components)\n        self.assertEqual(out.domain.metas[0].name, 'components (1)')\n\n    def test_sparse_data(self):\n        \"\"\"Check that PCA returns the same results for both dense and sparse data.\"\"\"\n        dense_data, sparse_data = self.iris, self.iris.to_sparse()\n\n        def _compute_projection(data):\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            result = self.get_output(self.widget.Outputs.transformed_data)\n            self.send_signal(self.widget.Inputs.data, None)\n            return result\n\n        # Disable normalization\n        self.widget.controls.normalize.setChecked(False)\n        dense_pca = _compute_projection(dense_data)\n        sparse_pca = _compute_projection(sparse_data)\n        np.testing.assert_almost_equal(dense_pca.X, sparse_pca.X)\n\n        # Enable normalization\n        self.widget.controls.normalize.setChecked(True)\n        dense_pca = _compute_projection(dense_data)\n        sparse_pca = _compute_projection(sparse_data)\n        np.testing.assert_almost_equal(dense_pca.X, sparse_pca.X)\n\n    def test_all_components_continuous(self):\n        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))\n        # GH-2329 only occurred on TimeVariables when normalize=False\n        self.assertTrue(any(isinstance(a, TimeVariable)\n                            for a in data.domain.attributes))\n\n        self.widget.normalize = False\n        self.widget._update_normalize()     # pylint: disable=protected-access\n        self.widget.set_data(data)\n\n        components = self.get_output(self.widget.Outputs.components)\n        self.assertTrue(all(type(a) is ContinuousVariable   # pylint: disable=unidiomatic-typecheck\n                            for a in components.domain.attributes),\n                        \"Some variables aren't of type ContinuousVariable\")\n\n    @table_dense_sparse\n    def test_normalize_data(self, prepare_table):\n        \"\"\"Check that normalization is called at the proper times.\"\"\"\n        data = prepare_table(self.iris)\n\n        # Enable checkbox\n        self.widget.controls.normalize.setChecked(True)\n        self.assertTrue(self.widget.controls.normalize.isChecked())\n        with patch.object(preprocess, \"Normalize\", wraps=Normalize) as normalize:\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            self.assertTrue(self.widget.controls.normalize.isEnabled())\n            normalize.assert_called_once()\n\n        # Disable checkbox\n        self.widget.controls.normalize.setChecked(False)\n        self.assertFalse(self.widget.controls.normalize.isChecked())\n        with patch.object(preprocess, \"Normalize\", wraps=Normalize) as normalize:\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            self.assertTrue(self.widget.controls.normalize.isEnabled())\n            normalize.assert_not_called()\n\n    @table_dense_sparse\n    def test_normalization_variance(self, prepare_table):\n        data = prepare_table(self.iris)\n        self.widget.ncomponents = 2\n\n        # Enable normalization\n        self.widget.controls.normalize.setChecked(True)\n        self.assertTrue(self.widget.normalize)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.wait_until_stop_blocking()\n        variance_normalized = self.widget.variance_covered\n\n        # Disable normalization\n        self.widget.controls.normalize.setChecked(False)\n        self.assertFalse(self.widget.normalize)\n        self.wait_until_stop_blocking()\n        variance_unnormalized = self.widget.variance_covered\n\n        # normalized data will have lower covered variance\n        self.assertLess(variance_normalized, variance_unnormalized)\n\n    @table_dense_sparse\n    def test_normalized_gives_correct_result(self, prepare_table):\n        \"\"\"Make sure that normalization through widget gives correct result.\"\"\"\n        # Randomly set some values to zero\n        random_state = check_random_state(42)\n        mask = random_state.beta(1, 2, size=self.iris.X.shape) > 0.5\n        self.iris.X[mask] = 0\n\n        data = prepare_table(self.iris)\n\n        # Enable normalization and run data through widget\n        self.widget.controls.normalize.setChecked(True)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.wait_until_stop_blocking()\n        widget_result = self.get_output(self.widget.Outputs.transformed_data)\n\n        # Compute the correct embedding\n        x = self.iris.X\n        x = (x - x.mean(0)) / x.std(0)\n        U, S, Va = np.linalg.svd(x)\n        U, S, Va = U[:, :2], S[:2], Va[:2]\n        U, Va = svd_flip(U, Va)\n        pca_embedding = U * S\n\n        np.testing.assert_almost_equal(widget_result.X, pca_embedding)\n\n    def test_do_not_mask_features(self):\n        # the widget used to replace cached variables when creating the\n        # components output (until 20170726)\n        data = Table(\"iris.tab\")\n        self.widget.set_data(data)\n        ndata = Table(\"iris.tab\")\n        self.assertEqual(data.domain[0], ndata.domain[0])\n\n    def test_on_cut_changed(self):\n        widget = self.widget\n        widget.ncomponents = 2\n        invalidate = widget._invalidate_selection = Mock()\n        widget._on_cut_changed(2)\n        invalidate.assert_not_called()\n        widget._on_cut_changed(3)\n        invalidate.assert_called()\n\n        widget.ncomponents = 0  # Take all components\n        invalidate.reset_mock()\n        widget._on_cut_changed(1)\n        invalidate.assert_not_called()\n        self.assertEqual(widget.ncomponents, 0)\n\n    def test_output_data(self):\n        widget = self.widget\n        widget.ncomponents = 2\n        domain = Domain(self.iris.domain.attributes[:3],\n                        self.iris.domain.class_var,\n                        self.iris.domain.attributes[3:])\n        iris = self.iris.transform(domain)\n        self.send_signal(widget.Inputs.data, iris)\n        output = self.get_output(widget.Outputs.data)\n        outdom = output.domain\n        self.assertEqual(domain.attributes, outdom.attributes)\n        self.assertEqual(domain.class_var, outdom.class_var)\n        self.assertEqual(domain.metas, outdom.metas[:1])\n        self.assertEqual(len(outdom.metas), 3)\n        np.testing.assert_equal(iris.X, output.X)\n        np.testing.assert_equal(iris.Y, output.Y)\n        np.testing.assert_equal(iris.metas[:, 0], output.metas[:, 0])\n\n        trans = self.get_output(widget.Outputs.transformed_data)\n        self.assertEqual(trans.domain.attributes, outdom.metas[1:])\n        np.testing.assert_equal(trans.X, output.metas[:, 1:])\n\n        self.send_signal(widget.Inputs.data, None)\n        output = self.get_output(widget.Outputs.data)\n        self.assertIsNone(output)",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 229,
                            "end_lineno": 253,
                            "parent": {
                                "file_name": "Orange/widgets/unsupervised/tests/test_owpca.py",
                                "type": "class",
                                "name": "TestOWPCA",
                                "content": "class TestOWPCA(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(OWPCA)  # type: OWPCA\n        self.iris = Table(\"iris\")  # type: Table\n\n    def test_set_variance100(self):\n        self.widget.set_data(self.iris)\n        self.widget.variance_covered = 100\n        self.widget._update_selection_variance_spin()\n\n    def test_constant_data(self):\n        data = self.iris[::5]\n        data.X[:, :] = 1.0\n        # Ignore the warning: the test checks whether the widget shows\n        # Warning.trivial_components when this happens\n        with np.errstate(invalid=\"ignore\"):\n            self.send_signal(self.widget.Inputs.data, data)\n        self.assertTrue(self.widget.Warning.trivial_components.is_shown())\n        self.assertIsNone(self.get_output(self.widget.Outputs.transformed_data))\n        self.assertIsNone(self.get_output(self.widget.Outputs.components))\n\n    def test_empty_data(self):\n        \"\"\" Check widget for dataset with no rows and for dataset with no attributes \"\"\"\n        self.send_signal(self.widget.Inputs.data, self.iris[:0])\n        self.assertTrue(self.widget.Error.no_instances.is_shown())\n\n        domain = Domain([], None, self.iris.domain.variables)\n        new_data = Table.from_table(domain, self.iris)\n        self.send_signal(self.widget.Inputs.data, new_data)\n        self.assertTrue(self.widget.Error.no_features.is_shown())\n        self.assertFalse(self.widget.Error.no_instances.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(self.widget.Error.no_features.is_shown())\n\n    def test_limit_components(self):\n        X = np.random.RandomState(0).rand(101, 101)\n        data = Table.from_numpy(None, X)\n        self.widget.ncomponents = 100\n        self.send_signal(self.widget.Inputs.data, data)\n        tran = self.get_output(self.widget.Outputs.transformed_data)\n        self.assertEqual(len(tran.domain.attributes), 100)\n        self.widget.ncomponents = 101  # should not be accesible\n        with self.assertRaises(IndexError):\n            self.send_signal(self.widget.Inputs.data, data)\n\n    def test_migrate_settings_limits_components(self):\n        settings = dict(ncomponents=10)\n        OWPCA.migrate_settings(settings, 0)\n        self.assertEqual(settings['ncomponents'], 10)\n        settings = dict(ncomponents=101)\n        OWPCA.migrate_settings(settings, 0)\n        self.assertEqual(settings['ncomponents'], 100)\n\n    def test_migrate_settings_changes_variance_covered_to_int(self):\n        settings = dict(variance_covered=17.5)\n        OWPCA.migrate_settings(settings, 0)\n        self.assertEqual(settings[\"variance_covered\"], 17)\n\n        settings = dict(variance_covered=float('nan'))\n        OWPCA.migrate_settings(settings, 0)\n        self.assertEqual(settings[\"variance_covered\"], 100)\n\n    def test_variance_shown(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.widget.maxp = 2\n        self.widget._setup_plot()\n        var2 = self.widget.variance_covered\n        self.widget.ncomponents = 3\n        self.widget._update_selection_component_spin()\n        var3 = self.widget.variance_covered\n        self.assertGreater(var3, var2)\n\n    def test_unique_domain_components(self):\n        table = possible_duplicate_table('components')\n        self.send_signal(self.widget.Inputs.data, table)\n        out = self.get_output(self.widget.Outputs.components)\n        self.assertEqual(out.domain.metas[0].name, 'components (1)')\n\n    def test_sparse_data(self):\n        \"\"\"Check that PCA returns the same results for both dense and sparse data.\"\"\"\n        dense_data, sparse_data = self.iris, self.iris.to_sparse()\n\n        def _compute_projection(data):\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            result = self.get_output(self.widget.Outputs.transformed_data)\n            self.send_signal(self.widget.Inputs.data, None)\n            return result\n\n        # Disable normalization\n        self.widget.controls.normalize.setChecked(False)\n        dense_pca = _compute_projection(dense_data)\n        sparse_pca = _compute_projection(sparse_data)\n        np.testing.assert_almost_equal(dense_pca.X, sparse_pca.X)\n\n        # Enable normalization\n        self.widget.controls.normalize.setChecked(True)\n        dense_pca = _compute_projection(dense_data)\n        sparse_pca = _compute_projection(sparse_data)\n        np.testing.assert_almost_equal(dense_pca.X, sparse_pca.X)\n\n    def test_all_components_continuous(self):\n        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))\n        # GH-2329 only occurred on TimeVariables when normalize=False\n        self.assertTrue(any(isinstance(a, TimeVariable)\n                            for a in data.domain.attributes))\n\n        self.widget.normalize = False\n        self.widget._update_normalize()     # pylint: disable=protected-access\n        self.widget.set_data(data)\n\n        components = self.get_output(self.widget.Outputs.components)\n        self.assertTrue(all(type(a) is ContinuousVariable   # pylint: disable=unidiomatic-typecheck\n                            for a in components.domain.attributes),\n                        \"Some variables aren't of type ContinuousVariable\")\n\n    @table_dense_sparse\n    def test_normalize_data(self, prepare_table):\n        \"\"\"Check that normalization is called at the proper times.\"\"\"\n        data = prepare_table(self.iris)\n\n        # Enable checkbox\n        self.widget.controls.normalize.setChecked(True)\n        self.assertTrue(self.widget.controls.normalize.isChecked())\n        with patch.object(preprocess, \"Normalize\", wraps=Normalize) as normalize:\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            self.assertTrue(self.widget.controls.normalize.isEnabled())\n            normalize.assert_called_once()\n\n        # Disable checkbox\n        self.widget.controls.normalize.setChecked(False)\n        self.assertFalse(self.widget.controls.normalize.isChecked())\n        with patch.object(preprocess, \"Normalize\", wraps=Normalize) as normalize:\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            self.assertTrue(self.widget.controls.normalize.isEnabled())\n            normalize.assert_not_called()\n\n    @table_dense_sparse\n    def test_normalization_variance(self, prepare_table):\n        data = prepare_table(self.iris)\n        self.widget.ncomponents = 2\n\n        # Enable normalization\n        self.widget.controls.normalize.setChecked(True)\n        self.assertTrue(self.widget.normalize)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.wait_until_stop_blocking()\n        variance_normalized = self.widget.variance_covered\n\n        # Disable normalization\n        self.widget.controls.normalize.setChecked(False)\n        self.assertFalse(self.widget.normalize)\n        self.wait_until_stop_blocking()\n        variance_unnormalized = self.widget.variance_covered\n\n        # normalized data will have lower covered variance\n        self.assertLess(variance_normalized, variance_unnormalized)\n\n    @table_dense_sparse\n    def test_normalized_gives_correct_result(self, prepare_table):\n        \"\"\"Make sure that normalization through widget gives correct result.\"\"\"\n        # Randomly set some values to zero\n        random_state = check_random_state(42)\n        mask = random_state.beta(1, 2, size=self.iris.X.shape) > 0.5\n        self.iris.X[mask] = 0\n\n        data = prepare_table(self.iris)\n\n        # Enable normalization and run data through widget\n        self.widget.controls.normalize.setChecked(True)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.wait_until_stop_blocking()\n        widget_result = self.get_output(self.widget.Outputs.transformed_data)\n\n        # Compute the correct embedding\n        x = self.iris.X\n        x = (x - x.mean(0)) / x.std(0)\n        U, S, Va = np.linalg.svd(x)\n        U, S, Va = U[:, :2], S[:2], Va[:2]\n        U, Va = svd_flip(U, Va)\n        pca_embedding = U * S\n\n        np.testing.assert_almost_equal(widget_result.X, pca_embedding)\n\n    def test_do_not_mask_features(self):\n        # the widget used to replace cached variables when creating the\n        # components output (until 20170726)\n        data = Table(\"iris.tab\")\n        self.widget.set_data(data)\n        ndata = Table(\"iris.tab\")\n        self.assertEqual(data.domain[0], ndata.domain[0])\n\n    def test_on_cut_changed(self):\n        widget = self.widget\n        widget.ncomponents = 2\n        invalidate = widget._invalidate_selection = Mock()\n        widget._on_cut_changed(2)\n        invalidate.assert_not_called()\n        widget._on_cut_changed(3)\n        invalidate.assert_called()\n\n        widget.ncomponents = 0  # Take all components\n        invalidate.reset_mock()\n        widget._on_cut_changed(1)\n        invalidate.assert_not_called()\n        self.assertEqual(widget.ncomponents, 0)\n\n    def test_output_data(self):\n        widget = self.widget\n        widget.ncomponents = 2\n        domain = Domain(self.iris.domain.attributes[:3],\n                        self.iris.domain.class_var,\n                        self.iris.domain.attributes[3:])\n        iris = self.iris.transform(domain)\n        self.send_signal(widget.Inputs.data, iris)\n        output = self.get_output(widget.Outputs.data)\n        outdom = output.domain\n        self.assertEqual(domain.attributes, outdom.attributes)\n        self.assertEqual(domain.class_var, outdom.class_var)\n        self.assertEqual(domain.metas, outdom.metas[:1])\n        self.assertEqual(len(outdom.metas), 3)\n        np.testing.assert_equal(iris.X, output.X)\n        np.testing.assert_equal(iris.Y, output.Y)\n        np.testing.assert_equal(iris.metas[:, 0], output.metas[:, 0])\n\n        trans = self.get_output(widget.Outputs.transformed_data)\n        self.assertEqual(trans.domain.attributes, outdom.metas[1:])\n        np.testing.assert_equal(trans.X, output.metas[:, 1:])\n\n        self.send_signal(widget.Inputs.data, None)\n        output = self.get_output(widget.Outputs.data)\n        self.assertIsNone(output)",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 19,
                                "end_lineno": 253,
                                "parent": null
                            }
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "1d1299dd5768eddabb67c94397415b2e9196962c^",
    "new_commit_hash": "1d1299dd5768eddabb67c94397415b2e9196962c",
    "commit_message": "Merge pull request #4900 from aturanjanin/owpca\n\nPCA: clear output when input data is removed",
    "commit_date": "2020-09-04T10:47:22+02:00",
    "metadata": {}
}
{
    "repo_name": "orange3",
    "new_commit_hash": "1d1299dd5768eddabb67c94397415b2e9196962c",
    "test_file_codes": [
        "# Test methods with long descriptive names can omit docstrings\n# pylint: disable=missing-docstring, protected-access\nimport unittest\nfrom unittest.mock import patch, Mock\n\nimport numpy as np\n\nfrom Orange.data import Table, Domain, ContinuousVariable, TimeVariable\nfrom Orange.preprocess import preprocess\nfrom Orange.preprocess.preprocess import Normalize\nfrom Orange.widgets.tests.base import WidgetTest\nfrom Orange.widgets.tests.utils import table_dense_sparse, possible_duplicate_table\nfrom Orange.widgets.unsupervised.owpca import OWPCA\nfrom Orange.tests import test_filename\nfrom sklearn.utils import check_random_state\nfrom sklearn.utils.extmath import svd_flip\n\n\nclass TestOWPCA(WidgetTest):\n    def setUp(self):\n        self.widget = self.create_widget(OWPCA)  # type: OWPCA\n        self.iris = Table(\"iris\")  # type: Table\n\n    def test_set_variance100(self):\n        self.widget.set_data(self.iris)\n        self.widget.variance_covered = 100\n        self.widget._update_selection_variance_spin()\n\n    def test_constant_data(self):\n        data = self.iris[::5]\n        data.X[:, :] = 1.0\n        # Ignore the warning: the test checks whether the widget shows\n        # Warning.trivial_components when this happens\n        with np.errstate(invalid=\"ignore\"):\n            self.send_signal(self.widget.Inputs.data, data)\n        self.assertTrue(self.widget.Warning.trivial_components.is_shown())\n        self.assertIsNone(self.get_output(self.widget.Outputs.transformed_data))\n        self.assertIsNone(self.get_output(self.widget.Outputs.components))\n\n    def test_empty_data(self):\n        \"\"\" Check widget for dataset with no rows and for dataset with no attributes \"\"\"\n        self.send_signal(self.widget.Inputs.data, self.iris[:0])\n        self.assertTrue(self.widget.Error.no_instances.is_shown())\n\n        domain = Domain([], None, self.iris.domain.variables)\n        new_data = Table.from_table(domain, self.iris)\n        self.send_signal(self.widget.Inputs.data, new_data)\n        self.assertTrue(self.widget.Error.no_features.is_shown())\n        self.assertFalse(self.widget.Error.no_instances.is_shown())\n\n        self.send_signal(self.widget.Inputs.data, None)\n        self.assertFalse(self.widget.Error.no_features.is_shown())\n\n    def test_limit_components(self):\n        X = np.random.RandomState(0).rand(101, 101)\n        data = Table.from_numpy(None, X)\n        self.widget.ncomponents = 100\n        self.send_signal(self.widget.Inputs.data, data)\n        tran = self.get_output(self.widget.Outputs.transformed_data)\n        self.assertEqual(len(tran.domain.attributes), 100)\n        self.widget.ncomponents = 101  # should not be accesible\n        with self.assertRaises(IndexError):\n            self.send_signal(self.widget.Inputs.data, data)\n\n    def test_migrate_settings_limits_components(self):\n        settings = dict(ncomponents=10)\n        OWPCA.migrate_settings(settings, 0)\n        self.assertEqual(settings['ncomponents'], 10)\n        settings = dict(ncomponents=101)\n        OWPCA.migrate_settings(settings, 0)\n        self.assertEqual(settings['ncomponents'], 100)\n\n    def test_migrate_settings_changes_variance_covered_to_int(self):\n        settings = dict(variance_covered=17.5)\n        OWPCA.migrate_settings(settings, 0)\n        self.assertEqual(settings[\"variance_covered\"], 17)\n\n        settings = dict(variance_covered=float('nan'))\n        OWPCA.migrate_settings(settings, 0)\n        self.assertEqual(settings[\"variance_covered\"], 100)\n\n    def test_variance_shown(self):\n        self.send_signal(self.widget.Inputs.data, self.iris)\n        self.widget.maxp = 2\n        self.widget._setup_plot()\n        var2 = self.widget.variance_covered\n        self.widget.ncomponents = 3\n        self.widget._update_selection_component_spin()\n        var3 = self.widget.variance_covered\n        self.assertGreater(var3, var2)\n\n    def test_unique_domain_components(self):\n        table = possible_duplicate_table('components')\n        self.send_signal(self.widget.Inputs.data, table)\n        out = self.get_output(self.widget.Outputs.components)\n        self.assertEqual(out.domain.metas[0].name, 'components (1)')\n\n    def test_sparse_data(self):\n        \"\"\"Check that PCA returns the same results for both dense and sparse data.\"\"\"\n        dense_data, sparse_data = self.iris, self.iris.to_sparse()\n\n        def _compute_projection(data):\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            result = self.get_output(self.widget.Outputs.transformed_data)\n            self.send_signal(self.widget.Inputs.data, None)\n            return result\n\n        # Disable normalization\n        self.widget.controls.normalize.setChecked(False)\n        dense_pca = _compute_projection(dense_data)\n        sparse_pca = _compute_projection(sparse_data)\n        np.testing.assert_almost_equal(dense_pca.X, sparse_pca.X)\n\n        # Enable normalization\n        self.widget.controls.normalize.setChecked(True)\n        dense_pca = _compute_projection(dense_data)\n        sparse_pca = _compute_projection(sparse_data)\n        np.testing.assert_almost_equal(dense_pca.X, sparse_pca.X)\n\n    def test_all_components_continuous(self):\n        data = Table(test_filename(\"datasets/cyber-security-breaches.tab\"))\n        # GH-2329 only occurred on TimeVariables when normalize=False\n        self.assertTrue(any(isinstance(a, TimeVariable)\n                            for a in data.domain.attributes))\n\n        self.widget.normalize = False\n        self.widget._update_normalize()     # pylint: disable=protected-access\n        self.widget.set_data(data)\n\n        components = self.get_output(self.widget.Outputs.components)\n        self.assertTrue(all(type(a) is ContinuousVariable   # pylint: disable=unidiomatic-typecheck\n                            for a in components.domain.attributes),\n                        \"Some variables aren't of type ContinuousVariable\")\n\n    @table_dense_sparse\n    def test_normalize_data(self, prepare_table):\n        \"\"\"Check that normalization is called at the proper times.\"\"\"\n        data = prepare_table(self.iris)\n\n        # Enable checkbox\n        self.widget.controls.normalize.setChecked(True)\n        self.assertTrue(self.widget.controls.normalize.isChecked())\n        with patch.object(preprocess, \"Normalize\", wraps=Normalize) as normalize:\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            self.assertTrue(self.widget.controls.normalize.isEnabled())\n            normalize.assert_called_once()\n\n        # Disable checkbox\n        self.widget.controls.normalize.setChecked(False)\n        self.assertFalse(self.widget.controls.normalize.isChecked())\n        with patch.object(preprocess, \"Normalize\", wraps=Normalize) as normalize:\n            self.send_signal(self.widget.Inputs.data, data)\n            self.wait_until_stop_blocking()\n            self.assertTrue(self.widget.controls.normalize.isEnabled())\n            normalize.assert_not_called()\n\n    @table_dense_sparse\n    def test_normalization_variance(self, prepare_table):\n        data = prepare_table(self.iris)\n        self.widget.ncomponents = 2\n\n        # Enable normalization\n        self.widget.controls.normalize.setChecked(True)\n        self.assertTrue(self.widget.normalize)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.wait_until_stop_blocking()\n        variance_normalized = self.widget.variance_covered\n\n        # Disable normalization\n        self.widget.controls.normalize.setChecked(False)\n        self.assertFalse(self.widget.normalize)\n        self.wait_until_stop_blocking()\n        variance_unnormalized = self.widget.variance_covered\n\n        # normalized data will have lower covered variance\n        self.assertLess(variance_normalized, variance_unnormalized)\n\n    @table_dense_sparse\n    def test_normalized_gives_correct_result(self, prepare_table):\n        \"\"\"Make sure that normalization through widget gives correct result.\"\"\"\n        # Randomly set some values to zero\n        random_state = check_random_state(42)\n        mask = random_state.beta(1, 2, size=self.iris.X.shape) > 0.5\n        self.iris.X[mask] = 0\n\n        data = prepare_table(self.iris)\n\n        # Enable normalization and run data through widget\n        self.widget.controls.normalize.setChecked(True)\n        self.send_signal(self.widget.Inputs.data, data)\n        self.wait_until_stop_blocking()\n        widget_result = self.get_output(self.widget.Outputs.transformed_data)\n\n        # Compute the correct embedding\n        x = self.iris.X\n        x = (x - x.mean(0)) / x.std(0)\n        U, S, Va = np.linalg.svd(x)\n        U, S, Va = U[:, :2], S[:2], Va[:2]\n        U, Va = svd_flip(U, Va)\n        pca_embedding = U * S\n\n        np.testing.assert_almost_equal(widget_result.X, pca_embedding)\n\n    def test_do_not_mask_features(self):\n        # the widget used to replace cached variables when creating the\n        # components output (until 20170726)\n        data = Table(\"iris.tab\")\n        self.widget.set_data(data)\n        ndata = Table(\"iris.tab\")\n        self.assertEqual(data.domain[0], ndata.domain[0])\n\n    def test_on_cut_changed(self):\n        widget = self.widget\n        widget.ncomponents = 2\n        invalidate = widget._invalidate_selection = Mock()\n        widget._on_cut_changed(2)\n        invalidate.assert_not_called()\n        widget._on_cut_changed(3)\n        invalidate.assert_called()\n\n        widget.ncomponents = 0  # Take all components\n        invalidate.reset_mock()\n        widget._on_cut_changed(1)\n        invalidate.assert_not_called()\n        self.assertEqual(widget.ncomponents, 0)\n\n    def test_output_data(self):\n        widget = self.widget\n        widget.ncomponents = 2\n        domain = Domain(self.iris.domain.attributes[:3],\n                        self.iris.domain.class_var,\n                        self.iris.domain.attributes[3:])\n        iris = self.iris.transform(domain)\n        self.send_signal(widget.Inputs.data, iris)\n        output = self.get_output(widget.Outputs.data)\n        outdom = output.domain\n        self.assertEqual(domain.attributes, outdom.attributes)\n        self.assertEqual(domain.class_var, outdom.class_var)\n        self.assertEqual(domain.metas, outdom.metas[:1])\n        self.assertEqual(len(outdom.metas), 3)\n        np.testing.assert_equal(iris.X, output.X)\n        np.testing.assert_equal(iris.Y, output.Y)\n        np.testing.assert_equal(iris.metas[:, 0], output.metas[:, 0])\n\n        trans = self.get_output(widget.Outputs.transformed_data)\n        self.assertEqual(trans.domain.attributes, outdom.metas[1:])\n        np.testing.assert_equal(trans.X, output.metas[:, 1:])\n\n        self.send_signal(widget.Inputs.data, None)\n        output = self.get_output(widget.Outputs.data)\n        self.assertIsNone(output)\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\nrunning build_src\nbuild_src\nbuilding extension \"Orange.classification._simple_tree\" sources\nbuilding extension \"Orange.classification._tree_scorers\" sources\nbuilding extension \"Orange.data._valuecount\" sources\nbuilding extension \"Orange.data._contingency\" sources\nbuilding extension \"Orange.data._io\" sources\nbuilding extension \"Orange.data._variable\" sources\nbuilding extension \"Orange.distance._distance\" sources\nbuilding extension \"Orange.preprocess._discretize\" sources\nbuilding extension \"Orange.preprocess._relieff\" sources\nbuilding extension \"Orange.projection._som\" sources\nbuilding extension \"Orange.widgets.utils._grid_density\" sources\nbuild_src: building npy-pkg config files\ncustomize UnixCCompiler\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating /tmp/tmpa9qjnt23/tmp\ncreating /tmp/tmpa9qjnt23/tmp/tmpa9qjnt23\ncompile options: '-MMD -MF /tmp/tmpa9qjnt23/file.c.d -c'\ncc: /tmp/tmpa9qjnt23/file.c\ncustomize UnixCCompiler using build_ext\ncustomize UnixCCompiler\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating /tmp/tmpjgjy3v9b/tmp\ncreating /tmp/tmpjgjy3v9b/tmp/tmpjgjy3v9b\ncompile options: '-MMD -MF /tmp/tmpjgjy3v9b/file.c.d -c'\ncc: /tmp/tmpjgjy3v9b/file.c\ncustomize UnixCCompiler using build_ext\nbuilding 'Orange.classification._simple_tree' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating build/temp.linux-x86_64-3.7/Orange\ncreating build/temp.linux-x86_64-3.7/Orange/classification\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/classification/_simple_tree.c\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 Orange/classification/_simple_tree.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.classification._tree_scorers' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/classification/_tree_scorers.c\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 Orange/classification/_tree_scorers.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.data._valuecount' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating build/temp.linux-x86_64-3.7/Orange/data\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/data/_valuecount.c\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 Orange/data/_valuecount.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.data._contingency' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/data/_contingency.c\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 Orange/data/_contingency.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.data._io' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/data/_io.c\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 Orange/data/_io.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.data._variable' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/data/_variable.c\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 Orange/data/_variable.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.distance._distance' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating build/temp.linux-x86_64-3.7/Orange/distance\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/distance/_distance.c\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 Orange/distance/_distance.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.preprocess._discretize' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating build/temp.linux-x86_64-3.7/Orange/preprocess\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/preprocess/_discretize.c\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 Orange/preprocess/_discretize.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.preprocess._relieff' extension\ncompiling C++ sources\nC compiler: c++ -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\nc++: Orange/preprocess/_relieff.cpp\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 Orange/preprocess/_relieff.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.projection._som' extension\ncompiling C sources\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating build/temp.linux-x86_64-3.7/Orange/projection\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\ncc: Orange/projection/_som.c\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 Orange/projection/_som.cpython-37m-x86_64-linux-gnu.so\nbuilding 'Orange.widgets.utils._grid_density' extension\ncompiling C++ sources\nC compiler: c++ -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating build/temp.linux-x86_64-3.7/Orange/widgets\ncreating build/temp.linux-x86_64-3.7/Orange/widgets/utils\ncompile options: '-I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include -I/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/include -I/home/gcpuser/.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/include/python3.7m -c'\nc++: Orange/widgets/utils/_grid_density.cpp\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 Orange/widgets/utils/_grid_density.cpython-37m-x86_64-linux-gnu.so\nrunning develop\nrunning build_scripts\nrunning egg_info\nrunning build_src\nbuild_src\nbuilding extension \"Orange.classification._simple_tree\" sources\nbuilding extension \"Orange.classification._tree_scorers\" sources\nbuilding extension \"Orange.data._valuecount\" sources\nbuilding extension \"Orange.data._contingency\" sources\nbuilding extension \"Orange.data._io\" sources\nbuilding extension \"Orange.data._variable\" sources\nbuilding extension \"Orange.distance._distance\" sources\nbuilding extension \"Orange.preprocess._discretize\" sources\nbuilding extension \"Orange.preprocess._relieff\" sources\nbuilding extension \"Orange.projection._som\" sources\nbuilding extension \"Orange.widgets.utils._grid_density\" sources\nbuild_src: building npy-pkg config files\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'\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'\nadding license file 'LICENSE'\nwriting manifest file 'Orange3.egg-info/SOURCES.txt'\nrunning build_ext\ncustomize UnixCCompiler\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating /tmp/tmpjj90jbwf/tmp\ncreating /tmp/tmpjj90jbwf/tmp/tmpjj90jbwf\ncompile options: '-MMD -MF /tmp/tmpjj90jbwf/file.c.d -c'\ncc: /tmp/tmpjj90jbwf/file.c\ncustomize UnixCCompiler using build_ext\ncustomize UnixCCompiler\nC compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/tools/deps/include -I/tools/deps/include/ncursesw -fPIC\n\ncreating /tmp/tmpohtigme1/tmp\ncreating /tmp/tmpohtigme1/tmp/tmpohtigme1\ncompile options: '-MMD -MF /tmp/tmpohtigme1/file.c.d -c'\ncc: /tmp/tmpohtigme1/file.c\ncustomize UnixCCompiler using build_ext\nCreating /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/Orange3.egg-link (link to .)\nAdding Orange3 3.27.0.dev0+1d1299d to easy-install.pth file\nInstalling orange-canvas script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/bin\n\nInstalled /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c\nProcessing dependencies for Orange3==3.27.0.dev0+1d1299d\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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/bin\nInstalling pip3 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/bin\nInstalling pip3.10 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/bin\nInstalling f2py3 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/bin\nInstalling f2py3.7 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages\nSearching for httpx==0.14.3\nBest match: httpx 0.14.3\nAdding httpx 0.14.3 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages\nSearching for chardet==3.0.4\nBest match: chardet 3.0.4\nAdding chardet 3.0.4 to easy-install.pth file\nInstalling chardetect script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/bin\nInstalling pyrcc5 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/bin\nInstalling pyuic5 script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/bin\nInstalling pyftmerge script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/bin\nInstalling pyftsubset script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/bin\nInstalling ttx script to /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/bin\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages\nSearching for httpcore==0.10.2\nBest match: httpcore 0.10.2\nAdding httpcore 0.10.2 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages\nSearching for h11==0.9.0\nBest match: h11 0.9.0\nAdding h11 0.9.0 to easy-install.pth file\n\nUsing /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages\nFinished processing dependencies for Orange3==3.27.0.dev0+1d1299d\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 80ms\nPrepared 18 packages in 25ms\nInstalled 18 packages in 594ms\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 53 packages in 294ms\nInstalled 47 packages in 652ms\n + baycomp==1.0.3\n + bottleneck==1.4.0\n + certifi==2024.12.14\n + cffi==1.15.1\n + chardet==3.0.4\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.9.0\n + httpcore==0.10.2\n + httpx==0.14.3\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 39 packages in 56ms\nInstalled 13 packages in 51ms\n + anyqt==0.2.0\n + attrs==24.2.0\n + cattrs==23.1.2\n + commonmark==0.9.1\n + dictdiffer==0.9.0\n + docutils==0.20.1\n + orange-canvas-core==0.1.35\n + orange-widget-base==4.22.0\n + platformdirs==4.0.0\n + pyqtgraph==0.12.4\n + qasync==0.25.0\n + requests-cache==1.1.1\n + url-normalize==1.4.3\nResolved 2 packages in 4ms\nInstalled 2 packages in 20ms\n + psycopg2==2.9.9\n + pymssql==2.3.2\nAudited 1 package in 62ms\nAudited 2 packages in 4ms\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      |         ^~~~~~~~~~~~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/classification/_tree_scorers.c:567:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.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:3115:27: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 3115 |   __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:4619:27: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 4619 |   __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:5396:43: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 5396 |           for (__pyx_t_22 = 0; __pyx_t_22 < __pyx_t_21; __pyx_t_22+=1) {\n      |                                           ^\nOrange/classification/_tree_scorers.c:5517:39: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 5517 |           __pyx_t_27 = ((__pyx_v_left >= __pyx_v_min_leaf) != 0);\n      |                                       ^~\nOrange/classification/_tree_scorers.c:5523:53: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 5523 |           __pyx_t_27 = (((__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:7051:41: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 7051 |         for (__pyx_t_11 = 0; __pyx_t_11 < __pyx_t_10; __pyx_t_11+=1) {\n      |                                         ^\nOrange/classification/_tree_scorers.c: In function ‘__pyx_pf_6Orange_14classification_13_tree_scorers_16compute_predictions_csc’:\nOrange/classification/_tree_scorers.c:7741:41: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]\n 7741 |         for (__pyx_t_11 = 0; __pyx_t_11 < __pyx_t_10; __pyx_t_11+=1) {\n      |                                         ^\nOrange/classification/_tree_scorers.c:7801:143: warning: comparison of integer expressions of different signedness: ‘__pyx_t_5numpy_int32_t’ {aka ‘int’} and ‘unsigned int’ [-Wsign-compare]\n 7801 |               __pyx_t_17 = (((*((__pyx_t_5numpy_int32_t *) ( /* dim=0 */ (__pyx_v_indices.data + __pyx_t_18 * __pyx_v_indices.strides[0]) ))) != __pyx_v_i) != 0);\n      |                                                                                                                                               ^~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/data/_valuecount.c:567:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/data/_contingency.c:598:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.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:3881:24: warning: ‘__pyx_v_decs’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 3881 |           __pyx_v_decs = (__pyx_v_decs / 10.0);\n      |           ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~\nOrange/data/_io.c:4070:24: warning: ‘__pyx_v_value’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4070 |           __pyx_t_10 = PyFloat_FromDouble(__pyx_v_value); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 221, __pyx_L5_error)\n      |                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nOrange/data/_io.c:480:40: warning: ‘__pyx_v_attr_index’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n  480 |   #define PyInt_FromLong               PyLong_FromLong\n      |                                        ^~~~~~~~~~~~~~~\nOrange/data/_io.c:1301:7: note: ‘__pyx_v_attr_index’ was declared here\n 1301 |   int __pyx_v_attr_index;\n      |       ^~~~~~~~~~~~~~~~~~\nOrange/data/_io.c:2935:25: warning: ‘__pyx_v_atomp’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 2935 |           __pyx_v_atomp = (__pyx_v_atomp + 1);\n      |           ~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~\nIn file included from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/distance/_distance.c:598:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/preprocess/_discretize.c:567:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/preprocess/_relieff.cpp:627:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1830,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,\n                 from /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4,\n                 from Orange/projection/_som.c:598:\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.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:3655:7: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 3655 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:3654:7: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 3654 |   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:4547:223: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4547 |           *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_int16_t *, __pyx_pybuffernd_winners.rcbuffer->pybuffer.buf, __pyx_t_38, __pyx_pybuffernd_winners.diminfo[0].strides, __pyx_t_39, __pyx_pybuffernd_winners.diminfo[1].strides) = __pyx_v_win_y;\n      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~\nOrange/projection/_som.c:4114:7: note: ‘__pyx_v_win_y’ was declared here\n 4114 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:4536:223: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4536 |           *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_int16_t *, __pyx_pybuffernd_winners.rcbuffer->pybuffer.buf, __pyx_t_36, __pyx_pybuffernd_winners.diminfo[0].strides, __pyx_t_37, __pyx_pybuffernd_winners.diminfo[1].strides) = __pyx_v_win_x;\n      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~\nOrange/projection/_som.c:4113:7: note: ‘__pyx_v_win_x’ was declared here\n 4113 |   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:4746:7: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4746 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:4745:7: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 4745 |   int __pyx_v_win_x;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c: In function ‘__pyx_pw_6Orange_10projection_4_som_3update’:\nOrange/projection/_som.c:3141:7: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 3141 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c: In function ‘__pyx_pw_6Orange_10projection_4_som_1get_winners’:\nOrange/projection/_som.c:2964:223: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 2964 |           *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_int16_t *, __pyx_pybuffernd_winners.rcbuffer->pybuffer.buf, __pyx_t_27, __pyx_pybuffernd_winners.diminfo[0].strides, __pyx_t_28, __pyx_pybuffernd_winners.diminfo[1].strides) = __pyx_v_win_y;\n      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~\nOrange/projection/_som.c:2665:7: note: ‘__pyx_v_win_y’ was declared here\n 2665 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:2953:223: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 2953 |           *__Pyx_BufPtrStrided2d(__pyx_t_5numpy_int16_t *, __pyx_pybuffernd_winners.rcbuffer->pybuffer.buf, __pyx_t_25, __pyx_pybuffernd_winners.diminfo[0].strides, __pyx_t_26, __pyx_pybuffernd_winners.diminfo[1].strides) = __pyx_v_win_x;\n      |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~\nOrange/projection/_som.c:2664:7: note: ‘__pyx_v_win_x’ was declared here\n 2664 |   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:5515:7: warning: ‘__pyx_v_win_y’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 5515 |   int __pyx_v_win_y;\n      |       ^~~~~~~~~~~~~\nOrange/projection/_som.c:5514:7: warning: ‘__pyx_v_win_x’ may be used uninitialized in this function [-Wmaybe-uninitialized]\n 5514 |   int __pyx_v_win_x;\n      |       ^~~~~~~~~~~~~\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.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,\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.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_1d1299dd5768eddabb67c94397415b2e9196962c/.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,\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/.venv/lib/python3.7/site-packages/setuptools/command/egg_info.py:628: SetuptoolsDeprecationWarning: Custom 'build_py' does not implement 'get_data_files_without_manifest'.\nPlease extend command classes from setuptools instead of distutils.\n  SetuptoolsDeprecationWarning\n",
    "new_commit_res_code": 1,
    "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_1d1299dd5768eddabb67c94397415b2e9196962c\ncollected 23 items\n\nr2e_tests/test_1.py sssE...................                              [100%]\n\n==================================== ERRORS ====================================\n_______________________ ERROR at setup of test_filename ________________________\nfile /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/Orange/tests/__init__.py, line 65\n  def test_filename(path):\nE       fixture 'path' not found\n>       available fixtures: cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory\n>       use 'pytest --fixtures [testpath]' for help on them.\n\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/Orange/tests/__init__.py:65\n==================================== PASSES ====================================\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::TestOWPCA::test_all_components_continuous\nPASSED r2e_tests/test_1.py::TestOWPCA::test_constant_data\nPASSED r2e_tests/test_1.py::TestOWPCA::test_do_not_mask_features\nPASSED r2e_tests/test_1.py::TestOWPCA::test_empty_data\nPASSED r2e_tests/test_1.py::TestOWPCA::test_image_export\nPASSED r2e_tests/test_1.py::TestOWPCA::test_limit_components\nPASSED r2e_tests/test_1.py::TestOWPCA::test_migrate_settings_changes_variance_covered_to_int\nPASSED r2e_tests/test_1.py::TestOWPCA::test_migrate_settings_limits_components\nPASSED r2e_tests/test_1.py::TestOWPCA::test_minimum_size\nPASSED r2e_tests/test_1.py::TestOWPCA::test_msg_base_class\nPASSED r2e_tests/test_1.py::TestOWPCA::test_normalization_variance\nPASSED r2e_tests/test_1.py::TestOWPCA::test_normalize_data\nPASSED r2e_tests/test_1.py::TestOWPCA::test_normalized_gives_correct_result\nPASSED r2e_tests/test_1.py::TestOWPCA::test_on_cut_changed\nPASSED r2e_tests/test_1.py::TestOWPCA::test_output_data\nPASSED r2e_tests/test_1.py::TestOWPCA::test_set_variance100\nPASSED r2e_tests/test_1.py::TestOWPCA::test_sparse_data\nPASSED r2e_tests/test_1.py::TestOWPCA::test_unique_domain_components\nPASSED r2e_tests/test_1.py::TestOWPCA::test_variance_shown\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\nERROR r2e_tests/test_1.py::test_filename\n==================== 19 passed, 3 skipped, 1 error in 5.92s ====================\nWidgets left: 0    Max widgets: 2552 \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_1d1299dd5768eddabb67c94397415b2e9196962c\ncollected 23 items\n\nr2e_tests/test_1.py sssE..............F....                              [100%]\n\n==================================== ERRORS ====================================\n_______________________ ERROR at setup of test_filename ________________________\nfile /home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/Orange/tests/__init__.py, line 65\n  def test_filename(path):\nE       fixture 'path' not found\n>       available fixtures: cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory\n>       use 'pytest --fixtures [testpath]' for help on them.\n\n/home/gcpuser/buckets/local_repoeval_bucket/repos/orange3_1d1299dd5768eddabb67c94397415b2e9196962c/Orange/tests/__init__.py:65\n=================================== FAILURES ===================================\n__________________________ TestOWPCA.test_output_data __________________________\n\nself = <r2e_tests.test_1.TestOWPCA testMethod=test_output_data>\n\n    def test_output_data(self):\n        widget = self.widget\n        widget.ncomponents = 2\n        domain = Domain(self.iris.domain.attributes[:3],\n                        self.iris.domain.class_var,\n                        self.iris.domain.attributes[3:])\n        iris = self.iris.transform(domain)\n        self.send_signal(widget.Inputs.data, iris)\n        output = self.get_output(widget.Outputs.data)\n        outdom = output.domain\n        self.assertEqual(domain.attributes, outdom.attributes)\n        self.assertEqual(domain.class_var, outdom.class_var)\n        self.assertEqual(domain.metas, outdom.metas[:1])\n        self.assertEqual(len(outdom.metas), 3)\n        np.testing.assert_equal(iris.X, output.X)\n        np.testing.assert_equal(iris.Y, output.Y)\n        np.testing.assert_equal(iris.metas[:, 0], output.metas[:, 0])\n    \n        trans = self.get_output(widget.Outputs.transformed_data)\n        self.assertEqual(trans.domain.attributes, outdom.metas[1:])\n        np.testing.assert_equal(trans.X, output.metas[:, 1:])\n    \n        self.send_signal(widget.Inputs.data, None)\n        output = self.get_output(widget.Outputs.data)\n>       self.assertIsNone(output)\nE       AssertionError: [[5.1, 3.5, 1.4 | Iris-setosa] {0.2, -1.85953, 0.449173},\nE        [4.9, 3.0, 1.4 | Iris-setosa] {0.2, -1.60267, -0.697065},\nE        [4.7, 3.2, 1.3 | Iris-setosa] {0.2, -1.95885, -0.387732},\nE        [4.6, 3.1, 1.5 | Iris-setosa] {0.2, -1.87492, -0.639969},\nE        [5.0, 3.6, 1.4 | Iris-setosa] {0.2, -2.018, 0.605866},\nE        ...\nE       ] is not None\n\nr2e_tests/test_1.py:253: AssertionError\n==================================== PASSES ====================================\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::TestOWPCA::test_all_components_continuous\nPASSED r2e_tests/test_1.py::TestOWPCA::test_constant_data\nPASSED r2e_tests/test_1.py::TestOWPCA::test_do_not_mask_features\nPASSED r2e_tests/test_1.py::TestOWPCA::test_empty_data\nPASSED r2e_tests/test_1.py::TestOWPCA::test_image_export\nPASSED r2e_tests/test_1.py::TestOWPCA::test_limit_components\nPASSED r2e_tests/test_1.py::TestOWPCA::test_migrate_settings_changes_variance_covered_to_int\nPASSED r2e_tests/test_1.py::TestOWPCA::test_migrate_settings_limits_components\nPASSED r2e_tests/test_1.py::TestOWPCA::test_minimum_size\nPASSED r2e_tests/test_1.py::TestOWPCA::test_msg_base_class\nPASSED r2e_tests/test_1.py::TestOWPCA::test_normalization_variance\nPASSED r2e_tests/test_1.py::TestOWPCA::test_normalize_data\nPASSED r2e_tests/test_1.py::TestOWPCA::test_normalized_gives_correct_result\nPASSED r2e_tests/test_1.py::TestOWPCA::test_on_cut_changed\nPASSED r2e_tests/test_1.py::TestOWPCA::test_set_variance100\nPASSED r2e_tests/test_1.py::TestOWPCA::test_sparse_data\nPASSED r2e_tests/test_1.py::TestOWPCA::test_unique_domain_components\nPASSED r2e_tests/test_1.py::TestOWPCA::test_variance_shown\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\nERROR r2e_tests/test_1.py::test_filename\nFAILED r2e_tests/test_1.py::TestOWPCA::test_output_data - AssertionError: [[5...\n=============== 1 failed, 18 passed, 3 skipped, 1 error in 5.00s ===============\nWidgets left: 0    Max widgets: 2552 \n\n",
    "old_commit_res_stderr": ""
}
Orange/widgets/unsupervised/owpca.py Orange/widgets/unsupervised/tests/test_owpca.py
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** PCA Widget Does Not Clear Output When Input Data Is Removed

**Description:**
When the input data is removed by setting it to `None`, the PCA widget is expected to clear its outputs. However, the output data remains populated instead of being cleared, causing unexpected behavior.

**Reproduction Steps:**
In a test scenario, after setting the input data to `None`, the output should also be `None`. For example:

```python
widget.set_input_data(None)
output = widget.get_output_data()
```

**Expected Behavior:**
After removing the input data, the output data should be cleared, and `output` should be `None`.

**Actual Behavior:**
The output data remains populated with the previous data instead of being cleared, resulting in `output` not being `None`.

[/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/unsupervised/owpca.py
import numbers

import numpy
from AnyQt.QtWidgets import QFormLayout
from AnyQt.QtCore import Qt

from Orange.data import Table, Domain, StringVariable, ContinuousVariable
from Orange.data.util import get_unique_names
from Orange.data.sql.table import SqlTable, AUTO_DL_LIMIT
from Orange.preprocess import preprocess
from Orange.projection import PCA
from Orange.widgets import widget, gui, settings
from Orange.widgets.utils.slidergraph import SliderGraph
from Orange.widgets.utils.widgetpreview import WidgetPreview
from Orange.widgets.widget import Input, Output


# Maximum number of PCA components that we can set in the widget
MAX_COMPONENTS = 100
LINE_NAMES = ["component variance", "cumulative variance"]


class OWPCA(widget.OWWidget):
    name = "PCA"
    description = "Principal component analysis with a scree-diagram."
    icon = "icons/PCA.svg"
    priority = 3050
    keywords = ["principal component analysis", "linear transformation"]

    class Inputs:
        data = Input("Data", Table)

    class Outputs:
        transformed_data = Output("Transformed Data", Table, replaces=["Transformed data"])
        data = Output("Data", Table, default=True)
        components = Output("Components", Table)
        pca = Output("PCA", PCA, dynamic=False)

    ncomponents = settings.Setting(2)
    variance_covered = settings.Setting(100)
    auto_commit = settings.Setting(True)
    normalize = settings.Setting(True)
    maxp = settings.Setting(20)
    axis_labels = settings.Setting(10)

    graph_name = "plot.plotItem"

    class Warning(widget.OWWidget.Warning):
        trivial_components = widget.Msg(
            "All components of the PCA are trivial (explain 0 variance). "
            "Input data is constant (or near constant).")

    class Error(widget.OWWidget.Error):
        no_features = widget.Msg("At least 1 feature is required")
        no_instances = widget.Msg("At least 1 data instance is required")

    def __init__(self):
        super().__init__()
        self.data = None

        self._pca = None
        self._transformed = None
        self._variance_ratio = None
        self._cumulative = None
        self._init_projector()

        # Components Selection
        box = gui.vBox(self.controlArea, "Components Selection")
        form = QFormLayout()
        box.layout().addLayout(form)

        self.components_spin = gui.spin(
            box, self, "ncomponents", 1, MAX_COMPONENTS,
            callback=self._update_selection_component_spin,
            keyboardTracking=False
        )
        self.components_spin.setSpecialValueText("All")

        self.variance_spin = gui.spin(
            box, self, "variance_covered", 1, 100,
            callback=self._update_selection_variance_spin,
            keyboardTracking=False
        )
        self.variance_spin.setSuffix("%")

        form.addRow("Components:", self.components_spin)
        form.addRow("Explained variance:", self.variance_spin)

        # Options
        self.options_box = gui.vBox(self.controlArea, "Options")
        self.normalize_box = gui.checkBox(
            self.options_box, self, "normalize",
            "Normalize variables", callback=self._update_normalize
        )

        self.maxp_spin = gui.spin(
            self.options_box, self, "maxp", 1, MAX_COMPONENTS,
            label="Show only first", callback=self._setup_plot,
            keyboardTracking=False
        )

        self.controlArea.layout().addStretch()

        gui.auto_apply(self.controlArea, self, "auto_commit")

        self.plot = SliderGraph(
            "Principal Components", "Proportion of variance",
            self._on_cut_changed)

        self.mainArea.layout().addWidget(self.plot)
        self._update_normalize()

    @Inputs.data
    def set_data(self, data):
        self.clear_messages()
        self.clear()
        self.information()
        self.data = None
        if isinstance(data, SqlTable):
            if data.approx_len() < AUTO_DL_LIMIT:
                data = Table(data)
            else:
                self.information("Data has been sampled")
                data_sample = data.sample_time(1, no_cache=True)
                data_sample.download_data(2000, partial=True)
                data = Table(data_sample)
        if isinstance(data, Table):
            if not data.domain.attributes:
                self.Error.no_features()
                self.clear_outputs()
                return
            if not data:
                self.Error.no_instances()
                self.clear_outputs()
                return

        self._init_projector()

        self.data = data
        self.fit()

    def fit(self):
        self.clear()
        self.Warning.trivial_components.clear()
        if self.data is None:
            return

        data = self.data

        if self.normalize:
            self._pca_projector.preprocessors = \
                self._pca_preprocessors + [preprocess.Normalize(center=False)]
        else:
            self._pca_projector.preprocessors = self._pca_preprocessors

        if not isinstance(data, SqlTable):
            pca = self._pca_projector(data)
            variance_ratio = pca.explained_variance_ratio_
            cumulative = numpy.cumsum(variance_ratio)

            if numpy.isfinite(cumulative[-1]):
                self.components_spin.setRange(0, len(cumulative))
                self._pca = pca
                self._variance_ratio = variance_ratio
                self._cumulative = cumulative
                self._setup_plot()
            else:
                self.Warning.trivial_components()

            self.unconditional_commit()

    def clear(self):
        self._pca = None
        self._transformed = None
        self._variance_ratio = None
        self._cumulative = None
        self.plot.clear_plot()

    def clear_outputs(self):
        self.Outputs.transformed_data.send(None)
        self.Outputs.data.send(None)
        self.Outputs.components.send(None)
        self.Outputs.pca.send(self._pca_projector)

    def _setup_plot(self):
        if self._pca is None:
            self.plot.clear_plot()
            return

        explained_ratio = self._variance_ratio
        explained = self._cumulative
        cutpos = self._nselected_components()
        p = min(len(self._variance_ratio), self.maxp)

        self.plot.update(
            numpy.arange(1, p+1), [explained_ratio[:p], explained[:p]],
            [Qt.red, Qt.darkYellow], cutpoint_x=cutpos, names=LINE_NAMES)

        self._update_axis()

    def _on_cut_changed(self, components):
        if components == self.ncomponents \
                or self.ncomponents == 0 \
                or self._pca is not None \
                and components == len(self._variance_ratio):
            return

        self.ncomponents = components
        if self._pca is not None:
            var = self._cumulative[components - 1]
            if numpy.isfinite(var):
                self.variance_covered = int(var * 100)

        self._invalidate_selection()

    def _update_selection_component_spin(self):
        # cut changed by "ncomponents" spin.
        if self._pca is None:
            self._invalidate_selection()
            return

        if self.ncomponents == 0:
            # Special "All" value
            cut = len(self._variance_ratio)
        else:
            cut = self.ncomponents

        var = self._cumulative[cut - 1]
        if numpy.isfinite(var):
            self.variance_covered = int(var * 100)

        self.plot.set_cut_point(cut)
        self._invalidate_selection()

    def _update_selection_variance_spin(self):
        # cut changed by "max variance" spin.
        if self._pca is None:
            return

        cut = numpy.searchsorted(self._cumulative,
                                 self.variance_covered / 100.0) + 1
        cut = min(cut, len(self._cumulative))
        self.ncomponents = cut
        self.plot.set_cut_point(cut)
        self._invalidate_selection()

    def _update_normalize(self):
        self.fit()
        if self.data is None:
            self._invalidate_selection()

    def _init_projector(self):
        self._pca_projector = PCA(n_components=MAX_COMPONENTS, random_state=0)
        self._pca_projector.component = self.ncomponents
        self._pca_preprocessors = PCA.preprocessors

    def _nselected_components(self):
        """Return the number of selected components."""
        if self._pca is None:
            return 0

        if self.ncomponents == 0:
            # Special "All" value
            max_comp = len(self._variance_ratio)
        else:
            max_comp = self.ncomponents

        var_max = self._cumulative[max_comp - 1]
        if var_max != numpy.floor(self.variance_covered / 100.0):
            cut = max_comp
            assert numpy.isfinite(var_max)
            self.variance_covered = int(var_max * 100)
        else:
            self.ncomponents = cut = numpy.searchsorted(
                self._cumulative, self.variance_covered / 100.0) + 1
        return cut

    def _invalidate_selection(self):
        self.commit()

    def _update_axis(self):
        p = min(len(self._variance_ratio), self.maxp)
        axis = self.plot.getAxis("bottom")
        d = max((p-1)//(self.axis_labels-1), 1)
        axis.setTicks([[(i, str(i)) for i in range(1, p + 1, d)]])

    def commit(self):
        transformed = data = components = None
        if self._pca is not None:
            if self._transformed is None:
                # Compute the full transform (MAX_COMPONENTS components) once.
                self._transformed = self._pca(self.data)
            transformed = self._transformed

            domain = Domain(
                transformed.domain.attributes[:self.ncomponents],
                self.data.domain.class_vars,
                self.data.domain.metas
            )
            transformed = transformed.from_table(domain, transformed)
            # prevent caching new features by defining compute_value
            proposed = [a.name for a in self._pca.orig_domain.attributes]
            meta_name = get_unique_names(proposed, 'components')
            dom = Domain(
                [ContinuousVariable(name, compute_value=lambda _: None)
                 for name in proposed],
                metas=[StringVariable(name=meta_name)])
            metas = numpy.array([['PC{}'.format(i + 1)
                                  for i in range(self.ncomponents)]],
                                dtype=object).T
            components = Table(dom, self._pca.components_[:self.ncomponents],
                               metas=metas)
            components.name = 'components'

            data_dom = Domain(
                self.data.domain.attributes,
                self.data.domain.class_vars,
                self.data.domain.metas + domain.attributes)
            data = Table.from_numpy(
                data_dom, self.data.X, self.data.Y,
                numpy.hstack((self.data.metas, transformed.X)),
                ids=self.data.ids)

        self._pca_projector.component = self.ncomponents
        self.Outputs.transformed_data.send(transformed)
        self.Outputs.components.send(components)
        self.Outputs.data.send(data)
        self.Outputs.pca.send(self._pca_projector)

    def send_report(self):
        if self.data is None:
            return
        self.report_items((
            ("Normalize data", str(self.normalize)),
            ("Selected components", self.ncomponents),
            ("Explained variance", "{:.3f} %".format(self.variance_covered))
        ))
        self.report_plot()

    @classmethod
    def migrate_settings(cls, settings, version):
        if "variance_covered" in settings:
            # Due to the error in gh-1896 the variance_covered was persisted
            # as a NaN value, causing a TypeError in the widgets `__init__`.
            vc = settings["variance_covered"]
            if isinstance(vc, numbers.Real):
                if numpy.isfinite(vc):
                    vc = int(vc)
                else:
                    vc = 100
                settings["variance_covered"] = vc
        if settings.get("ncomponents", 0) > MAX_COMPONENTS:
            settings["ncomponents"] = MAX_COMPONENTS

        # Remove old `decomposition_idx` when SVD was still included
        settings.pop("decomposition_idx", None)

        # Remove RemotePCA settings
        settings.pop("batch_size", None)
        settings.pop("address", None)
        settings.pop("auto_update", None)


if __name__ == "__main__":  # pragma: no cover
    WidgetPreview(OWPCA).run(Table("housing"))

```
--- 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:** PCA Widget Does Not Clear Output When Input Data Is Removed

**Description:**
When the input data is removed by setting it to `None`, the PCA widget is expected to clear its outputs. However, the output data remains populated instead of being cleared, causing unexpected behavior.

**Reproduction Steps:**
In a test scenario, after setting the input data to `None`, the output should also be `None`. For example:

```python
widget.set_input_data(None)
output = widget.get_output_data()
```

**Expected Behavior:**
After removing the input data, the output data should be cleared, and `output` should be `None`.

**Actual Behavior:**
The output data remains populated with the previous data instead of being cleared, resulting in `output` not being `None`.

[/ISSUE]
{
    "TestOWPCA.test_all_components_continuous": "PASSED",
    "TestOWPCA.test_constant_data": "PASSED",
    "TestOWPCA.test_do_not_mask_features": "PASSED",
    "TestOWPCA.test_empty_data": "PASSED",
    "TestOWPCA.test_image_export": "PASSED",
    "TestOWPCA.test_limit_components": "PASSED",
    "TestOWPCA.test_migrate_settings_changes_variance_covered_to_int": "PASSED",
    "TestOWPCA.test_migrate_settings_limits_components": "PASSED",
    "TestOWPCA.test_minimum_size": "PASSED",
    "TestOWPCA.test_msg_base_class": "PASSED",
    "TestOWPCA.test_normalization_variance": "PASSED",
    "TestOWPCA.test_normalize_data": "PASSED",
    "TestOWPCA.test_normalized_gives_correct_result": "PASSED",
    "TestOWPCA.test_on_cut_changed": "PASSED",
    "TestOWPCA.test_output_data": "PASSED",
    "TestOWPCA.test_set_variance100": "PASSED",
    "TestOWPCA.test_sparse_data": "PASSED",
    "TestOWPCA.test_unique_domain_components": "PASSED",
    "TestOWPCA.test_variance_shown": "PASSED",
    "test_filename": "ERROR"
}
diff --git a/Orange/widgets/unsupervised/owpca.py b/Orange/widgets/unsupervised/owpca.py
index f03b52167..6aced939b 100644
--- a/Orange/widgets/unsupervised/owpca.py
+++ b/Orange/widgets/unsupervised/owpca.py
@@ -116,6 +116,8 @@ class OWPCA(widget.OWWidget):
         self.clear()
         self.information()
         self.data = None
+        if not data:
+            self.clear_outputs()
         if isinstance(data, SqlTable):
             if data.approx_len() < AUTO_DL_LIMIT:
                 data = Table(data)

{"Orange/widgets/unsupervised/owpca.py": "import numbers\n\nimport numpy\nfrom AnyQt.QtWidgets import QFormLayout\nfrom AnyQt.QtCore import Qt\n\nfrom Orange.data import Table, Domain, StringVariable, ContinuousVariable\nfrom Orange.data.util import get_unique_names\nfrom Orange.data.sql.table import SqlTable, AUTO_DL_LIMIT\nfrom Orange.preprocess import preprocess\nfrom Orange.projection import PCA\nfrom Orange.widgets import widget, gui, settings\nfrom Orange.widgets.utils.slidergraph import SliderGraph\nfrom Orange.widgets.utils.widgetpreview import WidgetPreview\nfrom Orange.widgets.widget import Input, Output\n\n\n# Maximum number of PCA components that we can set in the widget\nMAX_COMPONENTS = 100\nLINE_NAMES = [\"component variance\", \"cumulative variance\"]\n\n\nclass OWPCA(widget.OWWidget):\n    name = \"PCA\"\n    description = \"Principal component analysis with a scree-diagram.\"\n    icon = \"icons/PCA.svg\"\n    priority = 3050\n    keywords = [\"principal component analysis\", \"linear transformation\"]\n\n    class Inputs:\n        data = Input(\"Data\", Table)\n\n    class Outputs:\n        transformed_data = Output(\"Transformed Data\", Table, replaces=[\"Transformed data\"])\n        data = Output(\"Data\", Table, default=True)\n        components = Output(\"Components\", Table)\n        pca = Output(\"PCA\", PCA, dynamic=False)\n\n    ncomponents = settings.Setting(2)\n    variance_covered = settings.Setting(100)\n    auto_commit = settings.Setting(True)\n    normalize = settings.Setting(True)\n    maxp = settings.Setting(20)\n    axis_labels = settings.Setting(10)\n\n    graph_name = \"plot.plotItem\"\n\n    class Warning(widget.OWWidget.Warning):\n        trivial_components = widget.Msg(\n            \"All components of the PCA are trivial (explain 0 variance). \"\n            \"Input data is constant (or near constant).\")\n\n    class Error(widget.OWWidget.Error):\n        no_features = widget.Msg(\"At least 1 feature is required\")\n        no_instances = widget.Msg(\"At least 1 data instance is required\")\n\n    def __init__(self):\n        super().__init__()\n        self.data = None\n\n        self._pca = None\n        self._transformed = None\n        self._variance_ratio = None\n        self._cumulative = None\n        self._init_projector()\n\n        # Components Selection\n        box = gui.vBox(self.controlArea, \"Components Selection\")\n        form = QFormLayout()\n        box.layout().addLayout(form)\n\n        self.components_spin = gui.spin(\n            box, self, \"ncomponents\", 1, MAX_COMPONENTS,\n            callback=self._update_selection_component_spin,\n            keyboardTracking=False\n        )\n        self.components_spin.setSpecialValueText(\"All\")\n\n        self.variance_spin = gui.spin(\n            box, self, \"variance_covered\", 1, 100,\n            callback=self._update_selection_variance_spin,\n            keyboardTracking=False\n        )\n        self.variance_spin.setSuffix(\"%\")\n\n        form.addRow(\"Components:\", self.components_spin)\n        form.addRow(\"Explained variance:\", self.variance_spin)\n\n        # Options\n        self.options_box = gui.vBox(self.controlArea, \"Options\")\n        self.normalize_box = gui.checkBox(\n            self.options_box, self, \"normalize\",\n            \"Normalize variables\", callback=self._update_normalize\n        )\n\n        self.maxp_spin = gui.spin(\n            self.options_box, self, \"maxp\", 1, MAX_COMPONENTS,\n            label=\"Show only first\", callback=self._setup_plot,\n            keyboardTracking=False\n        )\n\n        self.controlArea.layout().addStretch()\n\n        gui.auto_apply(self.controlArea, self, \"auto_commit\")\n\n        self.plot = SliderGraph(\n            \"Principal Components\", \"Proportion of variance\",\n            self._on_cut_changed)\n\n        self.mainArea.layout().addWidget(self.plot)\n        self._update_normalize()\n\n    @Inputs.data\n    def set_data(self, data):\n        self.clear_messages()\n        self.clear()\n        self.information()\n        self.data = None\n        if isinstance(data, SqlTable):\n            if data.approx_len() < AUTO_DL_LIMIT:\n                data = Table(data)\n            else:\n                self.information(\"Data has been sampled\")\n                data_sample = data.sample_time(1, no_cache=True)\n                data_sample.download_data(2000, partial=True)\n                data = Table(data_sample)\n        if isinstance(data, Table):\n            if not data.domain.attributes:\n                self.Error.no_features()\n                self.clear_outputs()\n                return\n            if not data:\n                self.Error.no_instances()\n                self.clear_outputs()\n                return\n\n        self._init_projector()\n\n        self.data = data\n        self.fit()\n\n    def fit(self):\n        self.clear()\n        self.Warning.trivial_components.clear()\n        if self.data is None:\n            return\n\n        data = self.data\n\n        if self.normalize:\n            self._pca_projector.preprocessors = \\\n                self._pca_preprocessors + [preprocess.Normalize(center=False)]\n        else:\n            self._pca_projector.preprocessors = self._pca_preprocessors\n\n        if not isinstance(data, SqlTable):\n            pca = self._pca_projector(data)\n            variance_ratio = pca.explained_variance_ratio_\n            cumulative = numpy.cumsum(variance_ratio)\n\n            if numpy.isfinite(cumulative[-1]):\n                self.components_spin.setRange(0, len(cumulative))\n                self._pca = pca\n                self._variance_ratio = variance_ratio\n                self._cumulative = cumulative\n                self._setup_plot()\n            else:\n                self.Warning.trivial_components()\n\n            self.unconditional_commit()\n\n    def clear(self):\n        self._pca = None\n        self._transformed = None\n        self._variance_ratio = None\n        self._cumulative = None\n        self.plot.clear_plot()\n\n    def clear_outputs(self):\n        self.Outputs.transformed_data.send(None)\n        self.Outputs.data.send(None)\n        self.Outputs.components.send(None)\n        self.Outputs.pca.send(self._pca_projector)\n\n    def _setup_plot(self):\n        if self._pca is None:\n            self.plot.clear_plot()\n            return\n\n        explained_ratio = self._variance_ratio\n        explained = self._cumulative\n        cutpos = self._nselected_components()\n        p = min(len(self._variance_ratio), self.maxp)\n\n        self.plot.update(\n            numpy.arange(1, p+1), [explained_ratio[:p], explained[:p]],\n            [Qt.red, Qt.darkYellow], cutpoint_x=cutpos, names=LINE_NAMES)\n\n        self._update_axis()\n\n    def _on_cut_changed(self, components):\n        if components == self.ncomponents \\\n                or self.ncomponents == 0 \\\n                or self._pca is not None \\\n                and components == len(self._variance_ratio):\n            return\n\n        self.ncomponents = components\n        if self._pca is not None:\n            var = self._cumulative[components - 1]\n            if numpy.isfinite(var):\n                self.variance_covered = int(var * 100)\n\n        self._invalidate_selection()\n\n    def _update_selection_component_spin(self):\n        # cut changed by \"ncomponents\" spin.\n        if self._pca is None:\n            self._invalidate_selection()\n            return\n\n        if self.ncomponents == 0:\n            # Special \"All\" value\n            cut = len(self._variance_ratio)\n        else:\n            cut = self.ncomponents\n\n        var = self._cumulative[cut - 1]\n        if numpy.isfinite(var):\n            self.variance_covered = int(var * 100)\n\n        self.plot.set_cut_point(cut)\n        self._invalidate_selection()\n\n    def _update_selection_variance_spin(self):\n        # cut changed by \"max variance\" spin.\n        if self._pca is None:\n            return\n\n        cut = numpy.searchsorted(self._cumulative,\n                                 self.variance_covered / 100.0) + 1\n        cut = min(cut, len(self._cumulative))\n        self.ncomponents = cut\n        self.plot.set_cut_point(cut)\n        self._invalidate_selection()\n\n    def _update_normalize(self):\n        self.fit()\n        if self.data is None:\n            self._invalidate_selection()\n\n    def _init_projector(self):\n        self._pca_projector = PCA(n_components=MAX_COMPONENTS, random_state=0)\n        self._pca_projector.component = self.ncomponents\n        self._pca_preprocessors = PCA.preprocessors\n\n    def _nselected_components(self):\n        \"\"\"Return the number of selected components.\"\"\"\n        if self._pca is None:\n            return 0\n\n        if self.ncomponents == 0:\n            # Special \"All\" value\n            max_comp = len(self._variance_ratio)\n        else:\n            max_comp = self.ncomponents\n\n        var_max = self._cumulative[max_comp - 1]\n        if var_max != numpy.floor(self.variance_covered / 100.0):\n            cut = max_comp\n            assert numpy.isfinite(var_max)\n            self.variance_covered = int(var_max * 100)\n        else:\n            self.ncomponents = cut = numpy.searchsorted(\n                self._cumulative, self.variance_covered / 100.0) + 1\n        return cut\n\n    def _invalidate_selection(self):\n        self.commit()\n\n    def _update_axis(self):\n        p = min(len(self._variance_ratio), self.maxp)\n        axis = self.plot.getAxis(\"bottom\")\n        d = max((p-1)//(self.axis_labels-1), 1)\n        axis.setTicks([[(i, str(i)) for i in range(1, p + 1, d)]])\n\n    def commit(self):\n        transformed = data = components = None\n        if self._pca is not None:\n            if self._transformed is None:\n                # Compute the full transform (MAX_COMPONENTS components) once.\n                self._transformed = self._pca(self.data)\n            transformed = self._transformed\n\n            domain = Domain(\n                transformed.domain.attributes[:self.ncomponents],\n                self.data.domain.class_vars,\n                self.data.domain.metas\n            )\n            transformed = transformed.from_table(domain, transformed)\n            # prevent caching new features by defining compute_value\n            proposed = [a.name for a in self._pca.orig_domain.attributes]\n            meta_name = get_unique_names(proposed, 'components')\n            dom = Domain(\n                [ContinuousVariable(name, compute_value=lambda _: None)\n                 for name in proposed],\n                metas=[StringVariable(name=meta_name)])\n            metas = numpy.array([['PC{}'.format(i + 1)\n                                  for i in range(self.ncomponents)]],\n                                dtype=object).T\n            components = Table(dom, self._pca.components_[:self.ncomponents],\n                               metas=metas)\n            components.name = 'components'\n\n            data_dom = Domain(\n                self.data.domain.attributes,\n                self.data.domain.class_vars,\n                self.data.domain.metas + domain.attributes)\n            data = Table.from_numpy(\n                data_dom, self.data.X, self.data.Y,\n                numpy.hstack((self.data.metas, transformed.X)),\n                ids=self.data.ids)\n\n        self._pca_projector.component = self.ncomponents\n        self.Outputs.transformed_data.send(transformed)\n        self.Outputs.components.send(components)\n        self.Outputs.data.send(data)\n        self.Outputs.pca.send(self._pca_projector)\n\n    def send_report(self):\n        if self.data is None:\n            return\n        self.report_items((\n            (\"Normalize data\", str(self.normalize)),\n            (\"Selected components\", self.ncomponents),\n            (\"Explained variance\", \"{:.3f} %\".format(self.variance_covered))\n        ))\n        self.report_plot()\n\n    @classmethod\n    def migrate_settings(cls, settings, version):\n        if \"variance_covered\" in settings:\n            # Due to the error in gh-1896 the variance_covered was persisted\n            # as a NaN value, causing a TypeError in the widgets `__init__`.\n            vc = settings[\"variance_covered\"]\n            if isinstance(vc, numbers.Real):\n                if numpy.isfinite(vc):\n                    vc = int(vc)\n                else:\n                    vc = 100\n                settings[\"variance_covered\"] = vc\n        if settings.get(\"ncomponents\", 0) > MAX_COMPONENTS:\n            settings[\"ncomponents\"] = MAX_COMPONENTS\n\n        # Remove old `decomposition_idx` when SVD was still included\n        settings.pop(\"decomposition_idx\", None)\n\n        # Remove RemotePCA settings\n        settings.pop(\"batch_size\", None)\n        settings.pop(\"address\", None)\n        settings.pop(\"auto_update\", None)\n\n\nif __name__ == \"__main__\":  # pragma: no cover\n    WidgetPreview(OWPCA).run(Table(\"housing\"))\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:** PCA Widget Does Not Clear Output When Input Data Is Removed

**Description:**
When the input data is removed by setting it to `None`, the PCA widget is expected to clear its outputs. However, the output data remains populated instead of being cleared, causing unexpected behavior.

**Reproduction Steps:**
In a test scenario, after setting the input data to `None`, the output should also be `None`. For example:

```python
widget.set_input_data(None)
output = widget.get_output_data()
```

**Expected Behavior:**
After removing the input data, the output data should be cleared, and `output` should be `None`.

**Actual Behavior:**
The output data remains populated with the previous data instead of being cleared, resulting in `output` not being `None`.

[/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/unsupervised/owpca.py
import numbers

import numpy
from AnyQt.QtWidgets import QFormLayout
from AnyQt.QtCore import Qt

from Orange.data import Table, Domain, StringVariable, ContinuousVariable
from Orange.data.util import get_unique_names
from Orange.data.sql.table import SqlTable, AUTO_DL_LIMIT
from Orange.preprocess import preprocess
from Orange.projection import PCA
from Orange.widgets import widget, gui, settings
from Orange.widgets.utils.slidergraph import SliderGraph
from Orange.widgets.utils.widgetpreview import WidgetPreview
from Orange.widgets.widget import Input, Output


# Maximum number of PCA components that we can set in the widget
MAX_COMPONENTS = 100
LINE_NAMES = ["component variance", "cumulative variance"]


class OWPCA(widget.OWWidget):
    name = "PCA"
    description = "Principal component analysis with a scree-diagram."
    icon = "icons/PCA.svg"
    priority = 3050
    keywords = ["principal component analysis", "linear transformation"]

    class Inputs:
        data = Input("Data", Table)

    class Outputs:
        transformed_data = Output("Transformed Data", Table, replaces=["Transformed data"])
        data = Output("Data", Table, default=True)
        components = Output("Components", Table)
        pca = Output("PCA", PCA, dynamic=False)

    ncomponents = settings.Setting(2)
    variance_covered = settings.Setting(100)
    auto_commit = settings.Setting(True)
    normalize = settings.Setting(True)
    maxp = settings.Setting(20)
    axis_labels = settings.Setting(10)

    graph_name = "plot.plotItem"

    class Warning(widget.OWWidget.Warning):
        trivial_components = widget.Msg(
            "All components of the PCA are trivial (explain 0 variance). "
            "Input data is constant (or near constant).")

    class Error(widget.OWWidget.Error):
        no_features = widget.Msg("At least 1 feature is required")
        no_instances = widget.Msg("At least 1 data instance is required")

    def __init__(self):
        super().__init__()
        self.data = None

        self._pca = None
        self._transformed = None
        self._variance_ratio = None
        self._cumulative = None
        self._init_projector()

        # Components Selection
        box = gui.vBox(self.controlArea, "Components Selection")
        form = QFormLayout()
        box.layout().addLayout(form)

        self.components_spin = gui.spin(
            box, self, "ncomponents", 1, MAX_COMPONENTS,
            callback=self._update_selection_component_spin,
            keyboardTracking=False
        )
        self.components_spin.setSpecialValueText("All")

        self.variance_spin = gui.spin(
            box, self, "variance_covered", 1, 100,
            callback=self._update_selection_variance_spin,
            keyboardTracking=False
        )
        self.variance_spin.setSuffix("%")

        form.addRow("Components:", self.components_spin)
        form.addRow("Explained variance:", self.variance_spin)

        # Options
        self.options_box = gui.vBox(self.controlArea, "Options")
        self.normalize_box = gui.checkBox(
            self.options_box, self, "normalize",
            "Normalize variables", callback=self._update_normalize
        )

        self.maxp_spin = gui.spin(
            self.options_box, self, "maxp", 1, MAX_COMPONENTS,
            label="Show only first", callback=self._setup_plot,
            keyboardTracking=False
        )

        self.controlArea.layout().addStretch()

        gui.auto_apply(self.controlArea, self, "auto_commit")

        self.plot = SliderGraph(
            "Principal Components", "Proportion of variance",
            self._on_cut_changed)

        self.mainArea.layout().addWidget(self.plot)
        self._update_normalize()

    @Inputs.data
    def set_data(self, data):
        self.clear_messages()
        self.clear()
        self.information()
        self.data = None
        if isinstance(data, SqlTable):
            if data.approx_len() < AUTO_DL_LIMIT:
                data = Table(data)
            else:
                self.information("Data has been sampled")
                data_sample = data.sample_time(1, no_cache=True)
                data_sample.download_data(2000, partial=True)
                data = Table(data_sample)
        if isinstance(data, Table):
            if not data.domain.attributes:
                self.Error.no_features()
                self.clear_outputs()
                return
            if not data:
                self.Error.no_instances()
                self.clear_outputs()
                return

        self._init_projector()

        self.data = data
        self.fit()

    def fit(self):
        self.clear()
        self.Warning.trivial_components.clear()
        if self.data is None:
            return

        data = self.data

        if self.normalize:
            self._pca_projector.preprocessors = \
                self._pca_preprocessors + [preprocess.Normalize(center=False)]
        else:
            self._pca_projector.preprocessors = self._pca_preprocessors

        if not isinstance(data, SqlTable):
            pca = self._pca_projector(data)
            variance_ratio = pca.explained_variance_ratio_
            cumulative = numpy.cumsum(variance_ratio)

            if numpy.isfinite(cumulative[-1]):
                self.components_spin.setRange(0, len(cumulative))
                self._pca = pca
                self._variance_ratio = variance_ratio
                self._cumulative = cumulative
                self._setup_plot()
            else:
                self.Warning.trivial_components()

            self.unconditional_commit()

    def clear(self):
        self._pca = None
        self._transformed = None
        self._variance_ratio = None
        self._cumulative = None
        self.plot.clear_plot()

    def clear_outputs(self):
        self.Outputs.transformed_data.send(None)
        self.Outputs.data.send(None)
        self.Outputs.components.send(None)
        self.Outputs.pca.send(self._pca_projector)

    def _setup_plot(self):
        if self._pca is None:
            self.plot.clear_plot()
            return

        explained_ratio = self._variance_ratio
        explained = self._cumulative
        cutpos = self._nselected_components()
        p = min(len(self._variance_ratio), self.maxp)

        self.plot.update(
            numpy.arange(1, p+1), [explained_ratio[:p], explained[:p]],
            [Qt.red, Qt.darkYellow], cutpoint_x=cutpos, names=LINE_NAMES)

        self._update_axis()

    def _on_cut_changed(self, components):
        if components == self.ncomponents \
                or self.ncomponents == 0 \
                or self._pca is not None \
                and components == len(self._variance_ratio):
            return

        self.ncomponents = components
        if self._pca is not None:
            var = self._cumulative[components - 1]
            if numpy.isfinite(var):
                self.variance_covered = int(var * 100)

        self._invalidate_selection()

    def _update_selection_component_spin(self):
        # cut changed by "ncomponents" spin.
        if self._pca is None:
            self._invalidate_selection()
            return

        if self.ncomponents == 0:
            # Special "All" value
            cut = len(self._variance_ratio)
        else:
            cut = self.ncomponents

        var = self._cumulative[cut - 1]
        if numpy.isfinite(var):
            self.variance_covered = int(var * 100)

        self.plot.set_cut_point(cut)
        self._invalidate_selection()

    def _update_selection_variance_spin(self):
        # cut changed by "max variance" spin.
        if self._pca is None:
            return

        cut = numpy.searchsorted(self._cumulative,
                                 self.variance_covered / 100.0) + 1
        cut = min(cut, len(self._cumulative))
        self.ncomponents = cut
        self.plot.set_cut_point(cut)
        self._invalidate_selection()

    def _update_normalize(self):
        self.fit()
        if self.data is None:
            self._invalidate_selection()

    def _init_projector(self):
        self._pca_projector = PCA(n_components=MAX_COMPONENTS, random_state=0)
        self._pca_projector.component = self.ncomponents
        self._pca_preprocessors = PCA.preprocessors

    def _nselected_components(self):
        """Return the number of selected components."""
        if self._pca is None:
            return 0

        if self.ncomponents == 0:
            # Special "All" value
            max_comp = len(self._variance_ratio)
        else:
            max_comp = self.ncomponents

        var_max = self._cumulative[max_comp - 1]
        if var_max != numpy.floor(self.variance_covered / 100.0):
            cut = max_comp
            assert numpy.isfinite(var_max)
            self.variance_covered = int(var_max * 100)
        else:
            self.ncomponents = cut = numpy.searchsorted(
                self._cumulative, self.variance_covered / 100.0) + 1
        return cut

    def _invalidate_selection(self):
        self.commit()

    def _update_axis(self):
        p = min(len(self._variance_ratio), self.maxp)
        axis = self.plot.getAxis("bottom")
        d = max((p-1)//(self.axis_labels-1), 1)
        axis.setTicks([[(i, str(i)) for i in range(1, p + 1, d)]])

    def commit(self):
        transformed = data = components = None
        if self._pca is not None:
            if self._transformed is None:
                # Compute the full transform (MAX_COMPONENTS components) once.
                self._transformed = self._pca(self.data)
            transformed = self._transformed

            domain = Domain(
                transformed.domain.attributes[:self.ncomponents],
                self.data.domain.class_vars,
                self.data.domain.metas
            )
            transformed = transformed.from_table(domain, transformed)
            # prevent caching new features by defining compute_value
            proposed = [a.name for a in self._pca.orig_domain.attributes]
            meta_name = get_unique_names(proposed, 'components')
            dom = Domain(
                [ContinuousVariable(name, compute_value=lambda _: None)
                 for name in proposed],
                metas=[StringVariable(name=meta_name)])
            metas = numpy.array([['PC{}'.format(i + 1)
                                  for i in range(self.ncomponents)]],
                                dtype=object).T
            components = Table(dom, self._pca.components_[:self.ncomponents],
                               metas=metas)
            components.name = 'components'

            data_dom = Domain(
                self.data.domain.attributes,
                self.data.domain.class_vars,
                self.data.domain.metas + domain.attributes)
            data = Table.from_numpy(
                data_dom, self.data.X, self.data.Y,
                numpy.hstack((self.data.metas, transformed.X)),
                ids=self.data.ids)

        self._pca_projector.component = self.ncomponents
        self.Outputs.transformed_data.send(transformed)
        self.Outputs.components.send(components)
        self.Outputs.data.send(data)
        self.Outputs.pca.send(self._pca_projector)

    def send_report(self):
        if self.data is None:
            return
        self.report_items((
            ("Normalize data", str(self.normalize)),
            ("Selected components", self.ncomponents),
            ("Explained variance", "{:.3f} %".format(self.variance_covered))
        ))
        self.report_plot()

    @classmethod
    def migrate_settings(cls, settings, version):
        if "variance_covered" in settings:
            # Due to the error in gh-1896 the variance_covered was persisted
            # as a NaN value, causing a TypeError in the widgets `__init__`.
            vc = settings["variance_covered"]
            if isinstance(vc, numbers.Real):
                if numpy.isfinite(vc):
                    vc = int(vc)
                else:
                    vc = 100
                settings["variance_covered"] = vc
        if settings.get("ncomponents", 0) > MAX_COMPONENTS:
            settings["ncomponents"] = MAX_COMPONENTS

        # Remove old `decomposition_idx` when SVD was still included
        settings.pop("decomposition_idx", None)

        # Remove RemotePCA settings
        settings.pop("batch_size", None)
        settings.pop("address", None)
        settings.pop("auto_update", None)


if __name__ == "__main__":  # pragma: no cover
    WidgetPreview(OWPCA).run(Table("housing"))

```
--- 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