Skip to content
Closed
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
31 changes: 31 additions & 0 deletions cluster/matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,30 @@

logger = logging.getLogger(__name__)

def _encapsulate_item_for_combinfunc(item):
"""
This function has been extracted in order to
make Github issue #28 easier to investigate.
It replaces the following two lines of code,
which occur twice in method genmatrix, just
before the invocation of combinfunc.
if not hasattr(item, '__iter__') or isinstance(item, tuple):
item = [item]
Logging has been added to the original two lines
and shows that the behaviour of this snippet
has changed between Python2.7 and Python3.5.
"""
encapsulated_item = None
if not hasattr(item, '__iter__') or isinstance(item, tuple):
encapsulated_item = [item]
else:
encapsulated_item = item
logging.debug(
"item class:%s encapsulated as:%s ",
item.__class__, encapsulated_item.__class__
)
return encapsulated_item


class Matrix(object):
"""
Expand Down Expand Up @@ -123,10 +147,17 @@ def genmatrix(self, num_processes=1):
num_tasks_completed += 1
else:
# Otherwise do it here, in line
"""
if not hasattr(item, '__iter__') or isinstance(item, tuple):
item = [item]
if not hasattr(item2, '__iter__') or isinstance(item2, tuple):
item2 = [item2]
"""
# See the comment in function _encapsulate_item_for_combinfunc
# for details of why the lines above have been replaced
# by function invocations
item = _encapsulate_item_for_combinfunc(item)
item2 = _encapsulate_item_for_combinfunc(item2)
row[col_index] = self.combinfunc(item, item2)

if self.symmetric:
Expand Down
30 changes: 30 additions & 0 deletions cluster/test/test_hierarchical.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,12 +231,42 @@ def euclidian_distance(a, b):
result = cl.getlevel(40)
self.assertIsNotNone(result)

class Issue28TestCase(Py23TestCase):
'''
Test case to cover the case where the data consist
of dictionary keys, and the distance function executes
on the values these keys are associated with in the
dictionary, rather than the keys themselves.

Behaviour for this test case differs between Python2.7
and Python3.5: on 2.7 the test behaves as expected,

See Github issue #28.
'''

def testIssue28(self):
"Issue28 (Hierarchical Clustering)"

points1D = {
'p4' : 5, 'p2' : 6, 'p7' : 10,
'p9' : 120, 'p10' : 121, 'p11' : 119,
}

distance_func = lambda a,b : abs(points1D[a]-points1D[b])
cl = HierarchicalClustering(list(points1D.keys()), distance_func)
result = cl.getlevel(20)
self.assertIsNotNone(result)

if __name__ == '__main__':

import logging

suite = unittest.TestSuite((
unittest.makeSuite(HClusterIntegerTestCase),
unittest.makeSuite(HClusterSmallListTestCase),
unittest.makeSuite(HClusterStringTestCase),
unittest.makeSuite(Issue28TestCase),
))

logging.basicConfig(level=logging.DEBUG)
unittest.TextTestRunner(verbosity=2).run(suite)
11 changes: 11 additions & 0 deletions cluster/test/test_linkage.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,14 @@ def test_average_distance(self):
result = average(self.set_a, self.set_b, self.dist)
expected = 22.5
self.assertEqual(result, expected)

if __name__ == '__main__':

import logging

suite = unittest.TestSuite((
unittest.makeSuite(LinkageMethods),
))

logging.basicConfig(level=logging.DEBUG)
unittest.TextTestRunner(verbosity=2).run(suite)