[Scipy-svn] r5186 - in trunk/scipy/cluster: . tests

scipy-svn@scip... scipy-svn@scip...
Mon Nov 24 22:23:51 CST 2008


Author: damian.eads
Date: 2008-11-24 22:23:49 -0600 (Mon, 24 Nov 2008)
New Revision: 5186

Modified:
   trunk/scipy/cluster/hierarchy.py
   trunk/scipy/cluster/tests/test_hierarchy.py
Log:
Covered a few more edge conditions for scipy.cluster.hierarchy.is_valid_linkage.

Modified: trunk/scipy/cluster/hierarchy.py
===================================================================
--- trunk/scipy/cluster/hierarchy.py	2008-11-25 03:41:51 UTC (rev 5185)
+++ trunk/scipy/cluster/hierarchy.py	2008-11-25 04:23:49 UTC (rev 5186)
@@ -1231,6 +1231,21 @@
                     raise ValueError('Linkage \'%s\' contains negative counts.' % name)
                 else:
                     raise ValueError('Linkage contains negative counts.')
+        if _check_hierarchy_uses_cluster_before_formed(Z):
+            if name:
+                raise ValueError('Linkage \'%s\' uses non-singleton cluster before its formed.' % name)
+            else:
+                raise ValueError('Linkage uses non-singleton cluster before its formed.')
+        if _check_hierarchy_uses_cluster_more_than_once(Z):
+            if name:
+                raise ValueError('Linkage \'%s\' uses the same cluster more than once.' % name)
+            else:
+                raise ValueError('Linkage uses the same cluster more than once.')
+        if _check_hierarchy_not_all_clusters_used(Z):
+            if name:
+                raise ValueError('Linkage \'%s\' does not use all clusters.' % name)
+            else:
+                raise ValueError('Linkage does not use all clusters.')
     except Exception, e:
         if throw:
             raise
@@ -1239,6 +1254,32 @@
         valid = False
     return valid
 
+def _check_hierarchy_uses_cluster_before_formed(Z):
+    n = Z.shape[0] + 1
+    for i in xrange(0, n - 1):
+        if Z[i, 0] >= n + i or Z[i, 1] >= n + i:
+            return True
+    return False
+
+def _check_hierarchy_uses_cluster_more_than_once(Z):
+    n = Z.shape[0] + 1
+    chosen = set([])
+    for i in xrange(0, n - 1):
+        if (Z[i, 0] in chosen) or (Z[i, 1] in chosen) or Z[i, 0] == Z[i, 1]:
+            return True
+        chosen.add(Z[i, 0])
+        chosen.add(Z[i, 1])
+    return False
+
+def _check_hierarchy_not_all_clusters_used(Z):
+    n = Z.shape[0] + 1
+    chosen = set([])
+    for i in xrange(0, n - 1):
+        chosen.add(int(Z[i, 0]))
+        chosen.add(int(Z[i, 1]))
+    must_chosen = set(range(0, 2 * n - 2))
+    return len(must_chosen.difference(chosen)) > 0
+
 def num_obs_linkage(Z):
     """
     Returns the number of original observations of the linkage matrix

Modified: trunk/scipy/cluster/tests/test_hierarchy.py
===================================================================
--- trunk/scipy/cluster/tests/test_hierarchy.py	2008-11-25 03:41:51 UTC (rev 5185)
+++ trunk/scipy/cluster/tests/test_hierarchy.py	2008-11-25 04:23:49 UTC (rev 5186)
@@ -547,23 +547,27 @@
         Z = np.asarray([[0,   1, 3.0, 2],
                         [3,   2, 4.0, 3]], dtype=np.int)
         self.failUnless(is_valid_linkage(Z) == False)
+        self.failUnlessRaises(TypeError, is_valid_linkage, Z, throw=True)
 
     def test_is_valid_linkage_5_columns(self):
         "Tests is_valid_linkage(Z) with 5 columns."
         Z = np.asarray([[0,   1, 3.0, 2, 5],
                         [3,   2, 4.0, 3, 3]], dtype=np.double)
         self.failUnless(is_valid_linkage(Z) == False)
+        self.failUnlessRaises(ValueError, is_valid_linkage, Z, throw=True)
 
     def test_is_valid_linkage_3_columns(self):
         "Tests is_valid_linkage(Z) with 3 columns."
         Z = np.asarray([[0,   1, 3.0],
                         [3,   2, 4.0]], dtype=np.double)
         self.failUnless(is_valid_linkage(Z) == False)
+        self.failUnlessRaises(ValueError, is_valid_linkage, Z, throw=True)
 
     def test_is_valid_linkage_empty(self):
         "Tests is_valid_linkage(Z) with empty linkage."
         Z = np.zeros((0, 4), dtype=np.double)
         self.failUnless(is_valid_linkage(Z) == False)
+        self.failUnlessRaises(ValueError, is_valid_linkage, Z, throw=True)
 
     def test_is_valid_linkage_1x4(self):
         "Tests is_valid_linkage(Z) on linkage over 2 observations."
@@ -576,6 +580,35 @@
                         [3,   2, 4.0, 3]], dtype=np.double)
         self.failUnless(is_valid_linkage(Z) == True)
 
+    def test_is_valid_linkage_2x4_before1(self):
+        "Tests is_valid_linkage(Z) on linkage over 3 observations with clusters used before they're formed (case 1)."
+        Z = np.asarray([[0,   4, 3.0, 2],
+                        [2,   3, 4.0, 3]], dtype=np.double)
+        self.failUnless(is_valid_linkage(Z) == False)
+        self.failUnlessRaises(ValueError, is_valid_linkage, Z, throw=True)
+
+
+    def test_is_valid_linkage_2x4_before2(self):
+        "Tests is_valid_linkage(Z) on linkage over 3 observations with clusters used before they're formed (case 1)."
+        Z = np.asarray([[0,   1, 3.0, 2],
+                        [2,   5, 4.0, 3]], dtype=np.double)
+        self.failUnless(is_valid_linkage(Z) == False)
+        self.failUnlessRaises(ValueError, is_valid_linkage, Z, throw=True)
+
+    def test_is_valid_linkage_2x4_twice1(self):
+        "Tests is_valid_linkage(Z) on linkage over 3 observations with clusters used twice (case 1)."
+        Z = np.asarray([[0,   1, 3.0, 2],
+                        [3,   3, 4.0, 3]], dtype=np.double)
+        self.failUnless(is_valid_linkage(Z) == False)
+        self.failUnlessRaises(ValueError, is_valid_linkage, Z, throw=True)
+
+    def test_is_valid_linkage_2x4_twice2(self):
+        "Tests is_valid_linkage(Z) on linkage over 3 observations with clusters used twice (case 1)."
+        Z = np.asarray([[0,   1, 3.0, 2],
+                        [0,   1, 4.0, 3]], dtype=np.double)
+        self.failUnless(is_valid_linkage(Z) == False)
+        self.failUnlessRaises(ValueError, is_valid_linkage, Z, throw=True)
+
     def test_is_valid_linkage_4_and_up(self):
         "Tests is_valid_linkage(Z) on linkage on observation sets between sizes 4 and 15 (step size 3)."
         for i in xrange(4, 15, 3):
@@ -590,6 +623,7 @@
             Z = linkage(y)            
             Z[int(i/2),0] = -2
             self.failUnless(is_valid_linkage(Z) == False)
+            self.failUnlessRaises(ValueError, is_valid_linkage, Z, throw=True)
 
     def test_is_valid_linkage_4_and_up_neg_index_right(self):
         "Tests is_valid_linkage(Z) on linkage on observation sets between sizes 4 and 15 (step size 3) with negative indices (right)."
@@ -598,6 +632,7 @@
             Z = linkage(y)            
             Z[int(i/2),1] = -2
             self.failUnless(is_valid_linkage(Z) == False)
+            self.failUnlessRaises(ValueError, is_valid_linkage, Z, throw=True)
 
     def test_is_valid_linkage_4_and_up_neg_dist(self):
         "Tests is_valid_linkage(Z) on linkage on observation sets between sizes 4 and 15 (step size 3) with negative distances."
@@ -606,6 +641,7 @@
             Z = linkage(y)            
             Z[int(i/2),2] = -0.5
             self.failUnless(is_valid_linkage(Z) == False)
+            self.failUnlessRaises(ValueError, is_valid_linkage, Z, throw=True)
 
     def test_is_valid_linkage_4_and_up_neg_counts(self):
         "Tests is_valid_linkage(Z) on linkage on observation sets between sizes 4 and 15 (step size 3) with negative counts."
@@ -614,6 +650,7 @@
             Z = linkage(y)            
             Z[int(i/2),3] = -2
             self.failUnless(is_valid_linkage(Z) == False)
+            self.failUnlessRaises(ValueError, is_valid_linkage, Z, throw=True)
 
 class TestNumObsLinkage(TestCase):
 
@@ -718,28 +755,28 @@
         "Tests is_monotonic(Z) on 3x4 linkage. Expecting True."
         Z = np.asarray([[0, 1, 0.3, 2],
                         [2, 3, 0.4, 2],
-                        [3, 4, 0.6, 4]], dtype=np.double)
+                        [4, 5, 0.6, 4]], dtype=np.double)
         self.failUnless(is_monotonic(Z) == True)
 
     def test_is_monotonic_3x4_F1(self):
         "Tests is_monotonic(Z) on 3x4 linkage (case 1). Expecting False."
         Z = np.asarray([[0, 1, 0.3, 2],
                         [2, 3, 0.2, 2],
-                        [3, 4, 0.6, 4]], dtype=np.double)
+                        [4, 5, 0.6, 4]], dtype=np.double)
         self.failUnless(is_monotonic(Z) == False)
 
     def test_is_monotonic_3x4_F2(self):
         "Tests is_monotonic(Z) on 3x4 linkage (case 2). Expecting False."
         Z = np.asarray([[0, 1, 0.8, 2],
                         [2, 3, 0.4, 2],
-                        [3, 4, 0.6, 4]], dtype=np.double)
+                        [4, 5, 0.6, 4]], dtype=np.double)
         self.failUnless(is_monotonic(Z) == False)
 
     def test_is_monotonic_3x4_F3(self):
         "Tests is_monotonic(Z) on 3x4 linkage (case 3). Expecting False"
         Z = np.asarray([[0, 1, 0.3, 2],
                         [2, 3, 0.4, 2],
-                        [3, 4, 0.2, 4]], dtype=np.double)
+                        [4, 5, 0.2, 4]], dtype=np.double)
         self.failUnless(is_monotonic(Z) == False)
 
     def test_is_monotonic_tdist_linkage(self):



More information about the Scipy-svn mailing list