Skip to content

Commit 524b40d

Browse files
authored
Merge pull request #4469 from janezd/match-identity
Variable: Match two variables if they have the same compute_value=Identity
2 parents d2a6da7 + cb13e03 commit 524b40d

File tree

4 files changed

+110
-4
lines changed

4 files changed

+110
-4
lines changed

Orange/data/tests/test_variable.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from Orange.data import Variable, ContinuousVariable, DiscreteVariable, \
2020
StringVariable, TimeVariable, Unknown, Value
2121
from Orange.data.io import CSVReader
22+
from Orange.preprocess.transformation import Identity
2223
from Orange.tests.base import create_pickling_tests
2324
from Orange.util import OrangeDeprecationWarning
2425

@@ -144,6 +145,67 @@ def test_strange_eq(self):
144145
self.assertNotEqual(a, "somestring")
145146
self.assertEqual(hash(a), hash(b))
146147

148+
def test_eq_with_compute_value(self):
149+
a = ContinuousVariable("a")
150+
b = ContinuousVariable("a")
151+
self.assertEqual(a, a)
152+
self.assertEqual(a, b)
153+
self.assertIsNot(a, b)
154+
155+
a._compute_value = lambda x: x
156+
self.assertEqual(a, a)
157+
self.assertNotEqual(a, b)
158+
159+
a1 = ContinuousVariable("a")
160+
a2 = ContinuousVariable("a")
161+
c = ContinuousVariable("c")
162+
163+
a._compute_value = Identity(a1)
164+
self.assertEqual(a, a)
165+
self.assertEqual(a, b)
166+
167+
b._compute_value = a.compute_value
168+
self.assertEqual(a, b)
169+
170+
b._compute_value = Identity(a1)
171+
self.assertEqual(a, b)
172+
173+
b._compute_value = Identity(a2)
174+
self.assertEqual(a, b)
175+
176+
b._compute_value = Identity(c)
177+
self.assertNotEqual(a, b)
178+
179+
b._compute_value = Identity(a2)
180+
a1._compute_value = lambda x: x
181+
self.assertNotEqual(a, b)
182+
183+
a1._compute_value = Identity(c)
184+
self.assertNotEqual(a, b)
185+
186+
a2._compute_value = Identity(c)
187+
self.assertEqual(a, b)
188+
189+
def test_hash(self):
190+
a = ContinuousVariable("a")
191+
b = ContinuousVariable("a")
192+
self.assertEqual(hash(a), hash(b))
193+
194+
a._compute_value = lambda x: x
195+
self.assertNotEqual(hash(a), hash(b))
196+
197+
b._compute_value = lambda x: x
198+
self.assertNotEqual(hash(a), hash(b))
199+
200+
a1 = ContinuousVariable("a")
201+
a2 = ContinuousVariable("a")
202+
203+
a._compute_value = Identity(a1)
204+
self.assertNotEqual(hash(a), hash(b))
205+
206+
b._compute_value = Identity(a2)
207+
self.assertEqual(hash(a), hash(b))
208+
147209

148210
def variabletest(varcls):
149211
def decorate(cls):

Orange/data/variable.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -347,12 +347,34 @@ def make_proxy(self):
347347
return var
348348

349349
def __eq__(self, other):
350-
return type(self) is type(other) \
351-
and self.name == other.name \
352-
and self._compute_value == other._compute_value
350+
# pylint: disable=protected-access,import-outside-toplevel
351+
352+
def to_match(var):
353+
if var._compute_value is None:
354+
return var
355+
elif isinstance(var._compute_value, Identity):
356+
return var._compute_value.variable
357+
return None
358+
359+
from Orange.preprocess.transformation import Identity
360+
return type(self) is type(other) and (
361+
self.name == other.name
362+
and self._compute_value == other._compute_value
363+
or
364+
(self.compute_value or other.compute_value)
365+
and to_match(self) == to_match(other) != None)
353366

354367
def __hash__(self):
355-
return hash((self.name, type(self), self._compute_value))
368+
# Two variables that are not equal can have the same hash.
369+
# This happens if one has compute_value == Identity and the other
370+
# doesn't have compute_value, or they have a different Identity.
371+
# Having the same hash while not being equal is of course allowed.
372+
# pylint: disable=import-outside-toplevel
373+
from Orange.preprocess.transformation import Identity
374+
compute_value = self._compute_value
375+
if isinstance(self._compute_value, Identity):
376+
compute_value = None
377+
return hash((self.name, type(self), compute_value))
356378

357379
@classmethod
358380
def make(cls, name, *args, **kwargs):

Orange/preprocess/transformation.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ class Identity(Transformation):
5555
def transform(self, c):
5656
return c
5757

58+
def __eq__(self, other):
59+
return type(other) is type(self) and self.variable == other.variable
60+
61+
def __hash__(self):
62+
return hash((type(self), self.variable))
63+
5864

5965
class Indicator(Transformation):
6066
"""

Orange/tests/test_transformation.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ def test_transform_fails(self):
4343
trans = Transformation(self.data.domain[2])
4444
self.assertRaises(NotImplementedError, trans, self.data)
4545

46+
47+
class IdentityTest(unittest.TestCase):
4648
def test_identity(self):
4749
domain = Domain([ContinuousVariable("X")],
4850
[DiscreteVariable("C", values=("0", "1", "2"))],
@@ -62,6 +64,20 @@ def test_identity(self):
6264
np.testing.assert_equal(D1.Y, D.Y)
6365
np.testing.assert_equal(D1.metas, D.metas)
6466

67+
def test_eq_and_hash(self):
68+
x = ContinuousVariable("x")
69+
id_x1 = Identity(x)
70+
id_x1b = Identity(x)
71+
id_x2 = Identity(ContinuousVariable("x"))
72+
self.assertEqual(id_x1, id_x1b)
73+
self.assertEqual(hash(id_x1), hash(id_x1b))
74+
self.assertEqual(id_x1, id_x2)
75+
self.assertEqual(hash(id_x1), hash(id_x2))
76+
77+
id_y = Identity(ContinuousVariable("y"))
78+
self.assertNotEqual(id_x1, id_y)
79+
self.assertNotEqual(hash(id_x1), hash(id_y))
80+
6581

6682
class LookupTest(unittest.TestCase):
6783
def test_transform(self):

0 commit comments

Comments
 (0)