[Scipy-svn] r2344 - in trunk/Lib/io: . tests

scipy-svn at scipy.org scipy-svn at scipy.org
Fri Dec 1 10:32:56 EST 2006


Author: matthew.brett at gmail.com
Date: 2006-12-01 09:32:51 -0600 (Fri, 01 Dec 2006)
New Revision: 2344

Modified:
   trunk/Lib/io/recaster.py
   trunk/Lib/io/tests/test_recaster.py
Log:
Added options to recaster, minor refactoring

Modified: trunk/Lib/io/recaster.py
===================================================================
--- trunk/Lib/io/recaster.py	2006-11-30 19:14:12 UTC (rev 2343)
+++ trunk/Lib/io/recaster.py	2006-12-01 15:32:51 UTC (rev 2344)
@@ -66,26 +66,49 @@
 
     _sctype_attributes = sctype_attributes()
 
-    def __init__(self, sctype_list=None, sctype_tols=None):
+    def __init__(self, sctype_list=None,
+                 downcast_fp_to_fp = True,
+                 downcast_fp_to_int = True,
+                 downcast_int_to_int = True,
+                 upcast_int_to_fp = True,
+                 sctype_tols=None):
         ''' Set types for which we are attempting to downcast
 
         Input
         sctype_list  - list of acceptable scalar types
                      If None defaults to all system types
+        downcast_fp_to_fp - if True, tries to downcast floats and complex
+                       to smaller size of same type
+        downcast_fp_to_int - if True, tries to downcast floats and complex
+                       to integers
+        downcast_int_to_int - if True, tries to downcast integers to
+                       smaller of same type
+        upcast_int_to_fp - if True, tries to upcast integers that could not
+                       be downcast to floating point type
         sctype_tols  - dictionary key datatype, values rtol, tol
-                     to specify tolerances for checking near equality in downcasting
+                     to specify tolerances for checking near equality in
+                     downcasting
+
+        Note that tolerance values for integers are used for upcasting
+        integers to floats
         '''
         if sctype_list is None:
             sctype_list = self._sctype_attributes.keys()
         self.sctype_list = sctype_list
+        # Casting options
         self.sctype_tols = self.default_sctype_tols()
+        self.downcast_fp_to_fp = downcast_fp_to_fp
+        self.downcast_fp_to_int = downcast_fp_to_int
+        self.downcast_int_to_int = downcast_int_to_int
+        self.upcast_int_to_fp = upcast_int_to_fp
+        # Tolerances
         if sctype_tols is not None:
             self.sctype_tols.update(sctype_tols)
         # Cache sctype sizes, 
         self.sized_sctypes = {}
         for k in ('c', 'f', 'i', 'u'):
             self.sized_sctypes[k] = self.sctypes_by_size(k)
-        # All integer sizes
+        # Cache all integer sizes
         self.ints_sized_sctypes = []
         for k, v in self.sized_sctypes.items():
             if k in ('u', 'i'):
@@ -93,7 +116,7 @@
                     self.ints_sized_sctypes.append(e)
         if self.ints_sized_sctypes:
             self.ints_sized_sctypes.sort(lambda x, y: cmp(y[1], x[1]))
-        # Capable types list
+        # Cache capable types list
         self._capable_sctypes = {}
         for k in self._sctype_attributes:
             self._capable_sctypes[k] = self.get_capable_sctype(k)
@@ -193,7 +216,13 @@
         ''' Return rtol and atol for sctype '''
         tols = self.sctype_tols[sctype]
         return tols['rtol'], tols['atol']
-        
+
+    def arr_if_valid(self, arr):
+        ''' Returns array if of valid sctype, None otherwise '''
+        if arr.dtype.type not in self.sctype_list:
+            return None
+        return arr
+
     def smallest_same_kind(self, arr):
         ''' Return arr maybe downcast to same kind, smaller storage
 
@@ -221,19 +250,16 @@
         
         Returns None if no recast is within tolerance
         '''
-        dt = arr.dtype.type
-        rtol, atol = self.tols_from_sctype(dt)
+        sct = arr.dtype.type
+        rtol, atol = self.tols_from_sctype(sct)
         ret_arr = arr
         for T in sctypes:
             test_arr = arr.astype(T)
             if allclose(test_arr, arr, rtol, atol):
                 ret_arr = test_arr
-                can_downcast = True
             else:
                 break
-        if ret_arr.dtype.type not in self.sctype_list:
-            return None
-        return ret_arr
+        return self.arr_if_valid(ret_arr)
         
     def smallest_int_sctype(self, mx, mn):
         ''' Return integer type with smallest storage containing mx and mn
@@ -256,39 +282,46 @@
     def downcast(self, arr):
         dtk = arr.dtype.kind
         if dtk == 'c':
-            return self.downcast_complex(arr)
+            ret = self.downcast_complex(arr)
         elif dtk == 'f':
-            return self.downcast_float(arr)
+            ret =  self.downcast_float(arr)
         elif dtk in ('u', 'i'):
-            return self.downcast_integer(arr)
+            ret =  self.downcast_integer(arr)
         else:
             raise TypeError, 'Do not recognize array kind %s' % dtk
-            
+        if ret is None:
+            raise ValueError, 'Could not downcast array within precision'
+        return ret
+        
     def downcast_complex(self, arr):
         ''' Downcasts complex array to smaller type if possible '''
         # can we downcast to float?
-        dt = arr.dtype
-        dti = ceil(dt.itemsize / 2)
-        sctypes = self.sized_sctypes['f']
-        flts = [t[0] for i, t in enumerate(sctypes) if t[1] <= dti]
-        if flts: # There are smaller floats to try
-            test_arr = arr.astype(flts[0])
-            rtol, atol = self.tols_from_sctype(dt.type)
-            if allclose(arr, test_arr, rtol, atol):
-                arr = test_arr
+        if self.downcast_fp_to_fp:
+            dt = arr.dtype
+            dti = ceil(dt.itemsize / 2)
+            sctypes = self.sized_sctypes['f']
+            flts = [t[0] for i, t in enumerate(sctypes) if t[1] <= dti]
+            if flts: # There are smaller floats to try
+                test_arr = arr.astype(flts[0])
+                rtol, atol = self.tols_from_sctype(dt.type)
+                if allclose(arr, test_arr, rtol, atol):
+                    arr = test_arr
         # try downcasting to int or another complex type
         return self.downcast_to_int_or_same(arr)
     
     def downcast_to_int_or_same(self, arr):
         ''' Downcast to integer or smaller of same kind '''
         # Try integer
-        test_arr = self.downcast_integer(arr)
-        rtol, atol = self.tols_from_sctype(arr.dtype.type)
-        if allclose(arr, test_arr, rtol, atol):
-            return test_arr
+        if self.downcast_fp_to_int:
+            test_arr = self.downcast_integer(arr)
+            rtol, atol = self.tols_from_sctype(arr.dtype.type)
+            if allclose(arr, test_arr, rtol, atol):
+                return test_arr
         # Otherwise descend the types of same kind
-        return self.smallest_same_kind(arr)
-
+        if self.downcast_fp_to_fp:
+            return self.smallest_same_kind(arr)
+        return self.arr_if_valid(arr)
+    
     downcast_float = downcast_to_int_or_same
 
     def downcast_integer(self, arr):
@@ -297,6 +330,8 @@
         Returns None if range of arr cannot be contained in acceptable
         integer types
         '''
+        if not self.downcast_int_to_int:
+            return arr_if_valid(arr)
         mx = amax(arr)
         mn = amin(arr)
         idt = self.smallest_int_sctype(mx, mn)
@@ -306,21 +341,42 @@
 
     def recast(self, arr):
         ''' Try arr downcast, upcast if necesary to get compatible type '''
-        dt = arr.dtype.type
-        ret_arr = self.downcast(arr)
-        if ret_arr is not None:
-            return ret_arr
+        try:
+            return self.downcast(arr)
+        except ValueError:
+            pass
+        dt = arr.dtype
         # Could not downcast, arr dtype not in known list
         # Try upcast to larger dtype of same kind
-        udt = self.capable_dtype[dt]
+        sct = dt.type
+        udt = self.capable_sctype[sct]
         if udt is not None:
             return arr.astype(udt)
+        # Could be an integer type that we have not tried
+        # to downcast
+        if not self.downcast_int_to_int and dt.kind in ('u', 'i'):
+            arr = self.downcast_integer(arr)
+            if arr is not None:
+                return arr
         # We are stuck for floats and complex now
         # Can try casting integers to floats
-        if arr.dt.kind in ('i', 'u'):
+        if self.upcast_int_to_fp and dt.kind in ('i', 'u'):
             sctypes = self.sized_sctypes['f']
             arr = self._smallest_from_sctypes(arr, sctypes)
             if arr is not None:
                 return arr
         raise ValueError, 'Could not recast array within precision'
         
+    def recast_best_sctype(self, arr):
+        ''' Recast array, return closest sctype to original
+
+        Returns tuple of recast array and best sctype to contain
+        original data before recasting
+        '''
+        sct = arr.dtype.type
+        arr = self.recast(arr)
+        if sct not in self.sctype_list:
+            sct = self.capable_sctype[sct]
+            if sct is None:
+                sct = arr.dtype.type
+        return arr, sct

Modified: trunk/Lib/io/tests/test_recaster.py
===================================================================
--- trunk/Lib/io/tests/test_recaster.py	2006-11-30 19:14:12 UTC (rev 2343)
+++ trunk/Lib/io/tests/test_recaster.py	2006-12-01 15:32:51 UTC (rev 2344)
@@ -58,6 +58,7 @@
                 if expect_none:
                     assert C is None, 'Expecting None for %s' % T
                 else:
+                    assert C is not None, 'Got unexpected None from %s' % T
                     assert C.dtype.type == req_type, \
                            'Expected %s type, got %s type' % \
                            (C.dtype.type, req_type)




More information about the Scipy-svn mailing list