[Scipy-svn] r5563 - in trunk/scipy/io/matlab: . tests

scipy-svn at scipy.org scipy-svn at scipy.org
Wed Feb 18 23:30:32 EST 2009


Author: matthew.brett at gmail.com
Date: 2009-02-18 22:30:03 -0600 (Wed, 18 Feb 2009)
New Revision: 5563

Modified:
   trunk/scipy/io/matlab/mio5.py
   trunk/scipy/io/matlab/miobase.py
   trunk/scipy/io/matlab/tests/test_mio.py
Log:
More specific shapes for 1d arrays, with tests

Modified: trunk/scipy/io/matlab/mio5.py
===================================================================
--- trunk/scipy/io/matlab/mio5.py	2009-02-19 02:58:23 UTC (rev 5562)
+++ trunk/scipy/io/matlab/mio5.py	2009-02-19 04:30:03 UTC (rev 5563)
@@ -22,7 +22,7 @@
 import scipy.sparse
 
 from miobase import MatFileReader, MatArrayReader, MatMatrixGetter, \
-     MatFileWriter, MatStreamWriter, filldoc
+     MatFileWriter, MatStreamWriter, filldoc, matdims
 
 miINT8 = 1
 miUINT8 = 2
@@ -706,9 +706,7 @@
         if mclass is None:
             mclass = self.default_mclass
         if shape is None:
-            shape = self.arr.shape
-            if len(shape) < 2:
-                shape = shape + (0,) * (len(shape)-2)
+            shape = matdims(self.arr)
         self._mat_tag_pos = self.file_stream.tell()
         self.write_dtype(self.mat_tag)
         # write array flags (complex, global, logical, class, nzmax)

Modified: trunk/scipy/io/matlab/miobase.py
===================================================================
--- trunk/scipy/io/matlab/miobase.py	2009-02-19 02:58:23 UTC (rev 5562)
+++ trunk/scipy/io/matlab/miobase.py	2009-02-19 04:30:03 UTC (rev 5563)
@@ -125,6 +125,60 @@
         raise ValueError('Unknown mat file type, version %s' % ret)
 
 
+def matdims(arr):
+    ''' Determine equivalent matlab dimensions for given array 
+    
+    Parameters
+    ----------
+    arr : ndarray
+
+    Returns
+    -------
+    dims : shape as matlab expects
+
+    Examples
+    --------
+    >>> matdims(np.array(1)) # numpy scalar
+    (1, 1)
+    >>> matdims(np.array([1])) # 1d array, 1 element
+    (1, 1)
+    >>> matdims(np.array([1,2])) # 1d array, 2 elements
+    (2, 1)
+    >>> matdims(np.array([[2],[3]])) # 2d array, column vector
+    (2, 1)
+    >>> matdims(np.array([[2,3]])) # 2d array, row vector
+    (1, 2)
+    >>> matdims(np.array([[[2,3]]])) # 3d array, rowish vector
+    (1, 1, 2)
+    >>> matdims(np.array([])) # empty 1d array
+    (0, 0)
+    >>> matdims(np.array([[]])) # empty 2d
+    (0, 0)
+    >>> matdims(np.array([[[]]])) # empty 3d
+    (0, 0, 0)
+
+    Notes
+    -----
+    We had to decide what shape a 1 dimensional array would be.
+    ``np.atleast_2d thinks it is a row vector.  The default for a
+    vector in matlab (e.g. ``>> 1:12``) is a row vector. 
+
+    Versions of scipy up to and including 0.7 have resulted
+    (accidentally) in 1d arrays being read as column vectors.  For the
+    moment, we maintain the same tradition here.
+    '''
+    if arr.size == 0: # empty
+        return (0,) * np.max([arr.ndim, 2])
+    shape = arr.shape
+    if shape == (): # scalar
+        return (1,1)
+    if len(shape) == 1: 
+        # 1d array -> column vector. This is what matlab gives from
+        # shape 1,0 if passed in a mat file - the behavior up to and
+        # including scipy 0.7
+        return shape + (1,)
+    return shape
+
 class ByteOrder(object):
     ''' Namespace for byte ordering '''
     little_endian = boc.sys_is_le

Modified: trunk/scipy/io/matlab/tests/test_mio.py
===================================================================
--- trunk/scipy/io/matlab/tests/test_mio.py	2009-02-19 02:58:23 UTC (rev 5562)
+++ trunk/scipy/io/matlab/tests/test_mio.py	2009-02-19 04:30:03 UTC (rev 5563)
@@ -24,6 +24,7 @@
 from numpy import array
 import scipy.sparse as SP
 
+from scipy.io.matlab.miobase import matdims
 from scipy.io.matlab.mio import loadmat, savemat, find_mat_file
 from scipy.io.matlab.mio5 import MatlabObject, MatFile5Writer, \
      Mat5NumericWriter
@@ -32,15 +33,11 @@
 
 def mlarr(*args, **kwargs):
     ''' Convenience function to return matlab-compatible 2D array
-    Note that matlab writes empty shape as (0,0) - replicated here
     '''
     arr = np.array(*args, **kwargs)
-    if arr.size:
-        return np.atleast_2d(arr)
-    # empty elements return as shape (0,0)
-    return arr.reshape((0,0))
+    arr.shape = matdims(arr)
+    return arr
 
-
 # Define cases to test
 theta = np.pi/4*np.arange(9,dtype=float).reshape(1,9)
 case_table4 = [
@@ -87,22 +84,22 @@
      'expected': {'testonechar': array([u'r'])},
      })
 # Cell arrays stored as object arrays
-CA = mlarr([
-    [], # placeholder, object array constructor wierdness otherwise
-    mlarr(1),
-    mlarr([1,2]),
-    mlarr([1,2,3])], dtype=object).reshape(1,-1)
+CA = mlarr(( # tuple for object array creation
+        [],
+        mlarr([1]),
+        mlarr([[1,2]]),
+        mlarr([[1,2,3]])), dtype=object).reshape(1,-1)
 CA[0,0] = array(
     [u'This cell contains this string and 3 arrays of increasing length'])
 case_table5 = [
     {'name': 'cell',
      'expected': {'testcell': CA}}]
-CAE = mlarr([
+CAE = mlarr(( # tuple for object array creation
     mlarr(1),
     mlarr(2),
     mlarr([]),
     mlarr([]),
-    mlarr(3)], dtype=object).reshape(1,-1)
+    mlarr(3)), dtype=object).reshape(1,-1)
 case_table5.append(
     {'name': 'emptycell',
      'expected': {'testemptycell': CAE}})
@@ -194,12 +191,29 @@
 case_table5_rt.append(
     {'name': 'objectarray',
      'expected': {'testobjectarray': np.repeat(MO, 2).reshape(1,2)}})
-''' This test fails - exclude for now
+''' Test fails;  consider also savemat('A', {'A':np.array(1, dtype=object)})
 case_table5_rt.append(
     {'name': 'scalarobject',
-    'expected': {'testscalarobject': mlarr(array([1], dtype=object))}
+    'expected': {'testscalarobject': mlarr(1, dtype=object)}
     })
 '''
+
+def types_compatible(var1, var2):
+    ''' Check if types are same or compatible
+    
+    0d numpy scalars are compatible with bare python scalars
+    '''
+    type1 = type(var1)
+    type2 = type(var2)
+    if type1 is type2:
+        return True
+    if type1 is np.ndarray and var1.shape == ():
+        return type(var1.item()) is type2
+    if type2 is np.ndarray and var2.shape == ():
+        return type(var2.item()) is type1
+    return False
+
+
 def _check_level(label, expected, actual):
     """ Check one level of a potentially nested array """
     if SP.issparse(expected): # allow different types of sparse matrices
@@ -210,10 +224,9 @@
                                   decimal = 5)
         return
     # Check types are as expected
-    typex = type(expected)
-    typac = type(actual)
-    assert_true(typex is typac, \
-           "Expected type %s, got %s at %s" % (typex, typac, label))
+    assert_true(types_compatible(expected, actual), \
+           "Expected type %s, got %s at %s" % 
+                (type(expected), type(actual), label))
     # A field in a record array may not be an ndarray
     # A scalar from a record array will be type np.void
     if not isinstance(expected,




More information about the Scipy-svn mailing list