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

[FIX] owcorrespondence: Handle variables with one value #2066

Merged
merged 1 commit into from
Mar 10, 2017
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
43 changes: 31 additions & 12 deletions Orange/widgets/unsupervised/owcorrespondence.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from AnyQt.QtWidgets import QListView, QApplication
from AnyQt.QtGui import QBrush, QColor, QPainter
from AnyQt.QtCore import Qt, QEvent, QItemSelectionModel, QItemSelection
from AnyQt.QtCore import QEvent, QItemSelectionModel, QItemSelection

import pyqtgraph as pg
import Orange.data
Expand All @@ -14,7 +14,6 @@
from Orange.widgets.utils import itemmodels, colorpalette

from Orange.widgets.visualize.owscatterplotgraph import ScatterPlotItem
from Orange.widgets.io import FileFormat


class ScatterPlotItem(pg.ScatterPlotItem):
Expand Down Expand Up @@ -140,6 +139,12 @@ def _p_axes(self):
def _var_changed(self):
self.selected_var_indices = sorted(
ind.row() for ind in self.varview.selectionModel().selectedRows())
rfs = self.update_XY()
if rfs is not None:
if self.component_x >= rfs:
self.component_x = rfs-1
if self.component_y >= rfs:
self.component_y = rfs-1
self._invalidate()

def _component_changed(self):
Expand All @@ -160,6 +165,15 @@ def customEvent(self, event):
return super().customEvent(event)

def _update_CA(self):
self.update_XY()
self.component_x, self.component_y = self.component_x, self.component_y

self._setup_plot()
self._update_info()

def update_XY(self):
self.axis_x_cb.clear()
self.axis_y_cb.clear()
ca_vars = self.selected_vars()
if len(ca_vars) == 0:
return
Expand All @@ -171,26 +185,24 @@ def _update_CA(self):
ctable = contingency.get_contingency(self.data, *ca_vars[::-1])

self.ca = correspondence(ctable, )
rfs = self.ca.row_factors.shape[1]
axes = ["{}".format(i + 1)
for i in range(self.ca.row_factors.shape[1])]
self.axis_x_cb.clear()
for i in range(rfs)]
self.axis_x_cb.addItems(axes)
self.axis_y_cb.clear()
self.axis_y_cb.addItems(axes)
self.component_x, self.component_y = self.component_x, self.component_y

self._setup_plot()
self._update_info()
return rfs

def _setup_plot(self):
self.plot.clear()

points = self.ca
variables = self.selected_vars()
colors = colorpalette.ColorPaletteGenerator(len(variables))

p_axes = self._p_axes()

if points == None:
return

if len(variables) == 2:
row_points = self.ca.row_factors[:, p_axes]
col_points = self.ca.col_factors[:, p_axes]
Expand Down Expand Up @@ -220,7 +232,10 @@ def _setup_plot(self):
item.setPos(point[0], point[1])

inertia = self.ca.inertia_of_axis()
inertia = 100 * inertia / numpy.sum(inertia)
if numpy.sum(inertia) == 0:
inertia = 100 * inertia
else:
inertia = 100 * inertia / numpy.sum(inertia)

ax = self.plot.getAxis("bottom")
ax.setLabel("Component {} ({:.1f}%)"
Expand All @@ -236,7 +251,10 @@ def _update_info(self):
fmt = ("Axis 1: {:.2f}\n"
"Axis 2: {:.2f}")
inertia = self.ca.inertia_of_axis()
inertia = 100 * inertia / numpy.sum(inertia)
if numpy.sum(inertia) == 0:
inertia = 100 * inertia
else:
inertia = 100 * inertia / numpy.sum(inertia)

ax1, ax2 = self._p_axes()
self.infotext.setText(fmt.format(inertia[ax1], inertia[ax2]))
Expand Down Expand Up @@ -314,6 +332,7 @@ def correspondence(A):
E = row_sum * col_sum

D_r, D_c = row_sum.ravel() ** -1, col_sum.ravel() ** -1
D_r, D_c = numpy.nan_to_num(D_r), numpy.nan_to_num(D_c)

def gsvd(M, Wu, Wv):
assert len(M.shape) == 2
Expand Down
24 changes: 23 additions & 1 deletion Orange/widgets/unsupervised/tests/test_owcorrespondence.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Test methods with long descriptive names can omit docstrings
# pylint: disable=missing-docstring
from Orange.data import Table
from Orange.data import Table, Domain, DiscreteVariable, ContinuousVariable
from Orange.widgets.tests.base import WidgetTest
from Orange.widgets.unsupervised.owcorrespondence \
import OWCorrespondenceAnalysis
Expand All @@ -15,3 +15,25 @@ def test_no_data(self):
self.send_signal("Data", Table(Table("iris").domain))
self.assertTrue(self.widget.Error.empty_data.is_shown())
self.assertIsNone(self.widget.data)

def test_data_values_in_column(self):
"""
Check that the widget does not crash when:
1) Domain has a two or more discrete variables but less than in a table
2) There is at least one NaN value in a column.
GH-2066
"""
table = Table(
Domain(
[ContinuousVariable("a"),
DiscreteVariable("b", values=["t", "f"]),
DiscreteVariable("c", values=["y", "n"]),
DiscreteVariable("d", values=["k", "l", "z"])]
),
list(zip(
[42.48, 16.84, 15.23, 23.8],
["t", "t", "", "f"],
"yyyy",
"klkk"
)))
self.send_signal("Data", table)