Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ENH] Output to OWCorrespondence #4180

Merged
merged 1 commit into from
Nov 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 31 additions & 6 deletions Orange/widgets/unsupervised/owcorrespondence.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@
from AnyQt.QtCore import QEvent, QItemSelectionModel, QItemSelection

import pyqtgraph as pg
import Orange.data
from Orange.data import Table, Domain, ContinuousVariable, StringVariable
PrimozGodec marked this conversation as resolved.
Show resolved Hide resolved
from Orange.statistics import contingency

from Orange.widgets import widget, gui, settings
from Orange.widgets.utils import itemmodels, colorpalette
from Orange.widgets.utils.widgetpreview import WidgetPreview

from Orange.widgets.visualize.owscatterplotgraph import ScatterPlotItem
from Orange.widgets.widget import Input
from Orange.widgets.widget import Input, Output
from Orange.widgets.settings import Setting


class ScatterPlotItem(pg.ScatterPlotItem):
Expand Down Expand Up @@ -49,13 +50,17 @@ class OWCorrespondenceAnalysis(widget.OWWidget):
keywords = []

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

class Outputs:
coordinates = Output("Coordinates", Table)

Invalidate = QEvent.registerEventType()

settingsHandler = settings.DomainContextHandler()

selected_var_indices = settings.ContextSetting([])
auto_commit = Setting(True)

graph_name = "plot.plotItem"

Expand Down Expand Up @@ -96,6 +101,8 @@ def __init__(self):
gui.vBox(self.controlArea, "Contribution to Inertia"), "\n"
)

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

gui.rubber(self.controlArea)

self.plot = pg.PlotWidget(background="w")
Expand Down Expand Up @@ -127,6 +134,24 @@ def set_data(self, data):
self._restore_selection()
self._update_CA()

def commit(self):
output_table = None
if self.ca is not None:
sel_vars = self.selected_vars()
if len(sel_vars) == 2:
rf = np.vstack((self.ca.row_factors, self.ca.col_factors))
else:
rf = self.ca.row_factors
vars_data = [(val.name, var) for val in sel_vars for var in val.values]
output_table = Table(
Domain([ContinuousVariable(f"Component {i + 1}")
for i in range(rf.shape[1])],
metas=[StringVariable("Variable"),
StringVariable("Value")]),
rf, metas=vars_data
)
self.Outputs.coordinates.send(output_table)

def clear(self):
self.data = None
self.ca = None
Expand All @@ -145,8 +170,7 @@ def restore(view, indices):
restore(self.varview, self.selected_var_indices)

def _p_axes(self):
# return (0, 1)
return (self.component_x, self.component_y)
return self.component_x, self.component_y

def _var_changed(self):
self.selected_var_indices = sorted(
Expand Down Expand Up @@ -182,6 +206,7 @@ def _update_CA(self):

self._setup_plot()
self._update_info()
self.commit()

def update_XY(self):
self.axis_x_cb.clear()
Expand Down Expand Up @@ -406,4 +431,4 @@ def inertia_of_axis(self):


if __name__ == "__main__": # pragma: no cover
WidgetPreview(OWCorrespondenceAnalysis).run(Orange.data.Table("smokers_ct"))
WidgetPreview(OWCorrespondenceAnalysis).run(Table("titanic"))
17 changes: 16 additions & 1 deletion Orange/widgets/unsupervised/tests/test_owcorrespondence.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
from Orange.data import Table, Domain, DiscreteVariable, ContinuousVariable
from Orange.widgets.tests.base import WidgetTest
from Orange.widgets.unsupervised.owcorrespondence \
import OWCorrespondenceAnalysis
import OWCorrespondenceAnalysis, select_rows


class TestOWCorrespondence(WidgetTest):
def setUp(self):
self.widget = self.create_widget(OWCorrespondenceAnalysis)
self.data = Table("titanic")

def test_no_data(self):
"""Check that the widget doesn't crash on empty data"""
Expand Down Expand Up @@ -73,3 +74,17 @@ def test_no_discrete_variables(self):
self.assertTrue(self.widget.Error.no_disc_vars.is_shown())
self.send_signal(self.widget.Inputs.data, Table("iris"))
self.assertFalse(self.widget.Error.no_disc_vars.is_shown())

def test_outputs(self):
w = self.widget

self.assertIsNone(self.get_output(w.Outputs.coordinates), None)
self.send_signal(self.widget.Inputs.data, self.data)
self.assertTupleEqual(self.get_output(w.Outputs.coordinates).X.shape,
(6, 2))
select_rows(w.varview, [0, 1, 2])
w.commit()
self.assertTupleEqual(self.get_output(w.Outputs.coordinates).X.shape,
(8, 8))
self.send_signal(self.widget.Inputs.data, None)
self.assertIsNone(self.get_output(w.Outputs.coordinates), None)
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ Correspondence analysis for categorical multivariate data.

- Data: input dataset

**Outputs**

- Coordinates: coordinates of all components

[Correspondence Analysis](https://en.wikipedia.org/wiki/Correspondence_analysis) (CA) computes the CA linear transformation of the input data. While it is similar to PCA, CA computes linear transformation on discrete rather than on continuous data.

![](images/CorrespondenceAnalysis-stamped.png)
Expand Down