We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
PCA: Apply automatically does not work when changing the threshold by moving a bar in the scree diagram
**Describe the bug**
When Apply automatically is on in the PCA widget, moving the vertical threshold bar with a mouse in the scree plot should update the output data. It does not.

**To Reproduce**
File (say, Iris) -> PCA -> Data Table
make sure Apply Automatically is clicked
now move the bar
no change in the Data Table

**Orange version:**
3.23.0

**Expected behavior**
In the PCA widget, changing the number of components by moving the "threshold bar" in the scree diagram with a mouse should apply this setting and output a new data set when "Apply automatically" is set on.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `Orange/widgets/unsupervised/owpca.py`
Content:
```
1 import numbers
2 
3 import numpy
4 from AnyQt.QtWidgets import QFormLayout
5 from AnyQt.QtCore import Qt
6 
7 from Orange.data import Table, Domain, StringVariable, ContinuousVariable
8 from Orange.data.sql.table import SqlTable, AUTO_DL_LIMIT
9 from Orange.preprocess import preprocess
10 from Orange.projection import PCA
11 from Orange.widgets import widget, gui, settings
12 from Orange.widgets.utils.slidergraph import SliderGraph
13 from Orange.widgets.utils.widgetpreview import WidgetPreview
14 from Orange.widgets.widget import Input, Output
15 
16 
17 # Maximum number of PCA components that we can set in the widget
18 MAX_COMPONENTS = 100
19 
20 
21 class OWPCA(widget.OWWidget):
22     name = "PCA"
23     description = "Principal component analysis with a scree-diagram."
24     icon = "icons/PCA.svg"
25     priority = 3050
26     keywords = ["principal component analysis", "linear transformation"]
27 
28     class Inputs:
29         data = Input("Data", Table)
30 
31     class Outputs:
32         transformed_data = Output("Transformed Data", Table, replaces=["Transformed data"])
33         components = Output("Components", Table)
34         pca = Output("PCA", PCA, dynamic=False)
35 
36     settingsHandler = settings.DomainContextHandler()
37 
38     ncomponents = settings.Setting(2)
39     variance_covered = settings.Setting(100)
40     auto_commit = settings.Setting(True)
41     normalize = settings.ContextSetting(True)
42     maxp = settings.Setting(20)
43     axis_labels = settings.Setting(10)
44 
45     graph_name = "plot.plotItem"
46 
47     class Warning(widget.OWWidget.Warning):
48         trivial_components = widget.Msg(
49             "All components of the PCA are trivial (explain 0 variance). "
50             "Input data is constant (or near constant).")
51 
52     class Error(widget.OWWidget.Error):
53         no_features = widget.Msg("At least 1 feature is required")
54         no_instances = widget.Msg("At least 1 data instance is required")
55 
56     def __init__(self):
57         super().__init__()
58         self.data = None
59 
60         self._pca = None
61         self._transformed = None
62         self._variance_ratio = None
63         self._cumulative = None
64         self._init_projector()
65 
66         # Components Selection
67         box = gui.vBox(self.controlArea, "Components Selection")
68         form = QFormLayout()
69         box.layout().addLayout(form)
70 
71         self.components_spin = gui.spin(
72             box, self, "ncomponents", 1, MAX_COMPONENTS,
73             callback=self._update_selection_component_spin,
74             keyboardTracking=False
75         )
76         self.components_spin.setSpecialValueText("All")
77 
78         self.variance_spin = gui.spin(
79             box, self, "variance_covered", 1, 100,
80             callback=self._update_selection_variance_spin,
81             keyboardTracking=False
82         )
83         self.variance_spin.setSuffix("%")
84 
85         form.addRow("Components:", self.components_spin)
86         form.addRow("Variance covered:", self.variance_spin)
87 
88         # Options
89         self.options_box = gui.vBox(self.controlArea, "Options")
90         self.normalize_box = gui.checkBox(
91             self.options_box, self, "normalize",
92             "Normalize data", callback=self._update_normalize
93         )
94 
95         self.maxp_spin = gui.spin(
96             self.options_box, self, "maxp", 1, MAX_COMPONENTS,
97             label="Show only first", callback=self._setup_plot,
98             keyboardTracking=False
99         )
100 
101         self.controlArea.layout().addStretch()
102 
103         gui.auto_apply(self.controlArea, self, "auto_commit")
104 
105         self.plot = SliderGraph(
106             "Principal Components", "Proportion of variance",
107             self._on_cut_changed)
108 
109         self.mainArea.layout().addWidget(self.plot)
110         self._update_normalize()
111 
112     @Inputs.data
113     def set_data(self, data):
114         self.closeContext()
115         self.clear_messages()
116         self.clear()
117         self.information()
118         self.data = None
119         if isinstance(data, SqlTable):
120             if data.approx_len() < AUTO_DL_LIMIT:
121                 data = Table(data)
122             else:
123                 self.information("Data has been sampled")
124                 data_sample = data.sample_time(1, no_cache=True)
125                 data_sample.download_data(2000, partial=True)
126                 data = Table(data_sample)
127         if isinstance(data, Table):
128             if not data.domain.attributes:
129                 self.Error.no_features()
130                 self.clear_outputs()
131                 return
132             if not data:
133                 self.Error.no_instances()
134                 self.clear_outputs()
135                 return
136 
137         self.openContext(data)
138         self._init_projector()
139 
140         self.data = data
141         self.fit()
142 
143     def fit(self):
144         self.clear()
145         self.Warning.trivial_components.clear()
146         if self.data is None:
147             return
148 
149         data = self.data
150 
151         if self.normalize:
152             self._pca_projector.preprocessors = \
153                 self._pca_preprocessors + [preprocess.Normalize(center=False)]
154         else:
155             self._pca_projector.preprocessors = self._pca_preprocessors
156 
157         if not isinstance(data, SqlTable):
158             pca = self._pca_projector(data)
159             variance_ratio = pca.explained_variance_ratio_
160             cumulative = numpy.cumsum(variance_ratio)
161 
162             if numpy.isfinite(cumulative[-1]):
163                 self.components_spin.setRange(0, len(cumulative))
164                 self._pca = pca
165                 self._variance_ratio = variance_ratio
166                 self._cumulative = cumulative
167                 self._setup_plot()
168             else:
169                 self.Warning.trivial_components()
170 
171             self.unconditional_commit()
172 
173     def clear(self):
174         self._pca = None
175         self._transformed = None
176         self._variance_ratio = None
177         self._cumulative = None
178         self.plot.clear_plot()
179 
180     def clear_outputs(self):
181         self.Outputs.transformed_data.send(None)
182         self.Outputs.components.send(None)
183         self.Outputs.pca.send(self._pca_projector)
184 
185     def _setup_plot(self):
186         if self._pca is None:
187             self.plot.clear_plot()
188             return
189 
190         explained_ratio = self._variance_ratio
191         explained = self._cumulative
192         cutpos = self._nselected_components()
193         p = min(len(self._variance_ratio), self.maxp)
194 
195         self.plot.update(
196             numpy.arange(1, p+1), [explained_ratio[:p], explained[:p]],
197             [Qt.red, Qt.darkYellow], cutpoint_x=cutpos)
198 
199         self._update_axis()
200 
201     def _on_cut_changed(self, components):
202 
203         if not (self.ncomponents == 0 and
204                 components == len(self._variance_ratio)):
205             self.ncomponents = components
206 
207         if self._pca is not None:
208             var = self._cumulative[components - 1]
209             if numpy.isfinite(var):
210                 self.variance_covered = int(var * 100)
211 
212         if components != self._nselected_components():
213             self._invalidate_selection()
214 
215     def _update_selection_component_spin(self):
216         # cut changed by "ncomponents" spin.
217         if self._pca is None:
218             self._invalidate_selection()
219             return
220 
221         if self.ncomponents == 0:
222             # Special "All" value
223             cut = len(self._variance_ratio)
224         else:
225             cut = self.ncomponents
226 
227         var = self._cumulative[cut - 1]
228         if numpy.isfinite(var):
229             self.variance_covered = int(var * 100)
230 
231         self.plot.set_cut_point(cut)
232         self._invalidate_selection()
233 
234     def _update_selection_variance_spin(self):
235         # cut changed by "max variance" spin.
236         if self._pca is None:
237             return
238 
239         cut = numpy.searchsorted(self._cumulative,
240                                  self.variance_covered / 100.0) + 1
241         cut = min(cut, len(self._cumulative))
242         self.ncomponents = cut
243         self.plot.set_cut_point(cut)
244         self._invalidate_selection()
245 
246     def _update_normalize(self):
247         self.fit()
248         if self.data is None:
249             self._invalidate_selection()
250 
251     def _init_projector(self):
252         self._pca_projector = PCA(n_components=MAX_COMPONENTS, random_state=0)
253         self._pca_projector.component = self.ncomponents
254         self._pca_preprocessors = PCA.preprocessors
255 
256     def _nselected_components(self):
257         """Return the number of selected components."""
258         if self._pca is None:
259             return 0
260 
261         if self.ncomponents == 0:
262             # Special "All" value
263             max_comp = len(self._variance_ratio)
264         else:
265             max_comp = self.ncomponents
266 
267         var_max = self._cumulative[max_comp - 1]
268         if var_max != numpy.floor(self.variance_covered / 100.0):
269             cut = max_comp
270             assert numpy.isfinite(var_max)
271             self.variance_covered = int(var_max * 100)
272         else:
273             self.ncomponents = cut = numpy.searchsorted(
274                 self._cumulative, self.variance_covered / 100.0) + 1
275         return cut
276 
277     def _invalidate_selection(self):
278         self.commit()
279 
280     def _update_axis(self):
281         p = min(len(self._variance_ratio), self.maxp)
282         axis = self.plot.getAxis("bottom")
283         d = max((p-1)//(self.axis_labels-1), 1)
284         axis.setTicks([[(i, str(i)) for i in range(1, p + 1, d)]])
285 
286     def commit(self):
287         transformed = components = None
288         if self._pca is not None:
289             if self._transformed is None:
290                 # Compute the full transform (MAX_COMPONENTS components) once.
291                 self._transformed = self._pca(self.data)
292             transformed = self._transformed
293 
294             domain = Domain(
295                 transformed.domain.attributes[:self.ncomponents],
296                 self.data.domain.class_vars,
297                 self.data.domain.metas
298             )
299             transformed = transformed.from_table(domain, transformed)
300             # prevent caching new features by defining compute_value
301             dom = Domain(
302                 [ContinuousVariable(a.name, compute_value=lambda _: None)
303                  for a in self._pca.orig_domain.attributes],
304                 metas=[StringVariable(name='component')])
305             metas = numpy.array([['PC{}'.format(i + 1)
306                                   for i in range(self.ncomponents)]],
307                                 dtype=object).T
308             components = Table(dom, self._pca.components_[:self.ncomponents],
309                                metas=metas)
310             components.name = 'components'
311 
312         self._pca_projector.component = self.ncomponents
313         self.Outputs.transformed_data.send(transformed)
314         self.Outputs.components.send(components)
315         self.Outputs.pca.send(self._pca_projector)
316 
317     def send_report(self):
318         if self.data is None:
319             return
320         self.report_items((
321             ("Normalize data", str(self.normalize)),
322             ("Selected components", self.ncomponents),
323             ("Explained variance", "{:.3f} %".format(self.variance_covered))
324         ))
325         self.report_plot()
326 
327     @classmethod
328     def migrate_settings(cls, settings, version):
329         if "variance_covered" in settings:
330             # Due to the error in gh-1896 the variance_covered was persisted
331             # as a NaN value, causing a TypeError in the widgets `__init__`.
332             vc = settings["variance_covered"]
333             if isinstance(vc, numbers.Real):
334                 if numpy.isfinite(vc):
335                     vc = int(vc)
336                 else:
337                     vc = 100
338                 settings["variance_covered"] = vc
339         if settings.get("ncomponents", 0) > MAX_COMPONENTS:
340             settings["ncomponents"] = MAX_COMPONENTS
341 
342         # Remove old `decomposition_idx` when SVD was still included
343         settings.pop("decomposition_idx", None)
344 
345         # Remove RemotePCA settings
346         settings.pop("batch_size", None)
347         settings.pop("address", None)
348         settings.pop("auto_update", None)
349 
350 
351 if __name__ == "__main__":  # pragma: no cover
352     WidgetPreview(OWPCA).run(Table("housing"))
353
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/Orange/widgets/unsupervised/owpca.py b/Orange/widgets/unsupervised/owpca.py
--- a/Orange/widgets/unsupervised/owpca.py
+++ b/Orange/widgets/unsupervised/owpca.py
@@ -199,18 +199,19 @@
         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
 
-        if not (self.ncomponents == 0 and
-                components == len(self._variance_ratio)):
-            self.ncomponents = components
-
+        self.ncomponents = components
         if self._pca is not None:
             var = self._cumulative[components - 1]
             if numpy.isfinite(var):
                 self.variance_covered = int(var * 100)
 
-        if components != self._nselected_components():
-            self._invalidate_selection()
+        self._invalidate_selection()
 
     def _update_selection_component_spin(self):
         # cut changed by "ncomponents" spin.

{"golden_diff": "diff --git a/Orange/widgets/unsupervised/owpca.py b/Orange/widgets/unsupervised/owpca.py\n--- a/Orange/widgets/unsupervised/owpca.py\n+++ b/Orange/widgets/unsupervised/owpca.py\n@@ -199,18 +199,19 @@\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-        if not (self.ncomponents == 0 and\n-                components == len(self._variance_ratio)):\n-            self.ncomponents = components\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-        if components != self._nselected_components():\n-            self._invalidate_selection()\n+        self._invalidate_selection()\n \n     def _update_selection_component_spin(self):\n         # cut changed by \"ncomponents\" spin.\n", "issue": "PCA: Apply automatically does not work when changing the threshold by moving a bar in the scree diagram\n**Describe the bug**\r\nWhen Apply automatically is on in the PCA widget, moving the vertical threshold bar with a mouse in the scree plot should update the output data. It does not.\r\n\r\n**To Reproduce**\r\nFile (say, Iris) -> PCA -> Data Table\r\nmake sure Apply Automatically is clicked\r\nnow move the bar\r\nno change in the Data Table\r\n\r\n**Orange version:**\r\n3.23.0\r\n\r\n**Expected behavior**\r\nIn the PCA widget, changing the number of components by moving the \"threshold bar\" in the scree diagram with a mouse should apply this setting and output a new data set when \"Apply automatically\" is set on.\r\n\n", "before_files": [{"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.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\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        components = Output(\"Components\", Table)\n        pca = Output(\"PCA\", PCA, dynamic=False)\n\n    settingsHandler = settings.DomainContextHandler()\n\n    ncomponents = settings.Setting(2)\n    variance_covered = settings.Setting(100)\n    auto_commit = settings.Setting(True)\n    normalize = settings.ContextSetting(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(\"Variance covered:\", 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 data\", 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.closeContext()\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.openContext(data)\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.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)\n\n        self._update_axis()\n\n    def _on_cut_changed(self, components):\n\n        if not (self.ncomponents == 0 and\n                components == len(self._variance_ratio)):\n            self.ncomponents = components\n\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        if components != self._nselected_components():\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 = 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            dom = Domain(\n                [ContinuousVariable(a.name, compute_value=lambda _: None)\n                 for a in self._pca.orig_domain.attributes],\n                metas=[StringVariable(name='component')])\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        self._pca_projector.component = self.ncomponents\n        self.Outputs.transformed_data.send(transformed)\n        self.Outputs.components.send(components)\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", "path": "Orange/widgets/unsupervised/owpca.py"}], "after_files": [{"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.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\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        components = Output(\"Components\", Table)\n        pca = Output(\"PCA\", PCA, dynamic=False)\n\n    settingsHandler = settings.DomainContextHandler()\n\n    ncomponents = settings.Setting(2)\n    variance_covered = settings.Setting(100)\n    auto_commit = settings.Setting(True)\n    normalize = settings.ContextSetting(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(\"Variance covered:\", 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 data\", 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.closeContext()\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.openContext(data)\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.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)\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 = 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            dom = Domain(\n                [ContinuousVariable(a.name, compute_value=lambda _: None)\n                 for a in self._pca.orig_domain.attributes],\n                metas=[StringVariable(name='component')])\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        self._pca_projector.component = self.ncomponents\n        self.Outputs.transformed_data.send(transformed)\n        self.Outputs.components.send(components)\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", "path": "Orange/widgets/unsupervised/owpca.py"}]}