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

scipy-svn at scipy.org scipy-svn at scipy.org
Tue Sep 19 18:14:48 EDT 2006


Author: matthew.brett at gmail.com
Date: 2006-09-19 17:14:27 -0500 (Tue, 19 Sep 2006)
New Revision: 2203

Modified:
   trunk/Lib/io/mio.py
   trunk/Lib/io/mio4.py
   trunk/Lib/io/mio5.py
   trunk/Lib/io/miobase.py
   trunk/Lib/io/tests/test_mio.py
Log:
Added mat4 sparse save; separated mat4 and mat5 tests; docstring edits

Modified: trunk/Lib/io/mio.py
===================================================================
--- trunk/Lib/io/mio.py	2006-09-19 15:16:02 UTC (rev 2202)
+++ trunk/Lib/io/mio.py	2006-09-19 22:14:27 UTC (rev 2203)
@@ -1,7 +1,7 @@
 # Authors: Travis Oliphant, Matthew Brett
 
 """
-Module for reading and writing matlab .mat files
+Module for reading and writing matlab (TM) .mat files
 """
 
 import os
@@ -15,7 +15,7 @@
 
 
 def mat_reader_factory(file_name, append_mat=True):
-    """Create reader for matlab format files
+    """Create reader for matlab (TM) .mat format files
     
     If name not a full path name, search for the file on the sys.path
     list and use the first one found (the current directory is
@@ -25,7 +25,8 @@
 
     Inputs:
 
-      file_name -- name of the mat file (don't need .mat extension if append_mat=True)
+      file_name -- name of the mat file (don't need .mat extension
+                   if append_mat=True)
       append_mat -- True to append the .mat extension to the end of the
                    given filename.
       """
@@ -74,8 +75,9 @@
 def savemat(file_name, mdict, appendmat=True):
     """Save a dictionary of names and arrays into the MATLAB-style .mat file.
 
-    This saves the arrayobjects in the given dictionary to a matlab Version 4
-    style .mat file.
+    This saves the arrayobjects in the given dictionary to a matlab
+    Version 4 style .mat file.
+    
     @appendmat  - if true, appends '.mat' extension to filename, if not present
     """
     if appendmat and file_name[-4:] != ".mat":

Modified: trunk/Lib/io/mio4.py
===================================================================
--- trunk/Lib/io/mio4.py	2006-09-19 15:16:02 UTC (rev 2202)
+++ trunk/Lib/io/mio4.py	2006-09-19 22:14:27 UTC (rev 2203)
@@ -1,4 +1,4 @@
-''' Classes for read / write of matlab 4 files
+''' Classes for read / write of matlab (TM) 4 files
 '''
 
 from numpy import *
@@ -60,11 +60,11 @@
         name
         dims - shape of matrix as stored (see sparse reader)
         dtype - numpy dtype of matrix
-        mclass - matlab code for class of matrix
+        mclass - matlab (TM) code for class of matrix
         is_char    - True if these are char data
         is_numeric - True if these are numeric data
         is_complex - True if data are complex
-        original_dtype - data type in matlab workspace
+        original_dtype - data type in matlab (TM) workspace
     '''
     def __init__(self):
         self.next_position = None
@@ -159,26 +159,26 @@
 class Mat4SparseGetter(Mat4MatrixGetter):
     ''' Read sparse matrix type 
 
-    Matlab 4 real sparse arrays are saved in a N+1 by 3 array format,
-    where N is the number of non-zero values.  Column 1 values [0:N]
-    are the (1-based) row indices of the each non-zero value, column 2
-    [0:N] are the column indices, column 3 [0:N] are the (real)
-    values.  The last values [-1,0:2] of the rows, column indices are
-    shape[0] and shape[1] respectively of the output matrix. The last
-    value for the values column is a padding 0. mrows and ncols values
-    from the header give the shape of the stored matrix, here [N+1,
-    3].  Complex data is saved as a 4 column matrix, where the fourth
-    column contains the imaginary component of the data; the last
-    value is again 0.  Complex sparse data do _not_ have the header
-    imagf field set to True; the fact that the data are complex is
-    only detectable because there are 4 storage columns
+    Matlab (TM) 4 real sparse arrays are saved in a N+1 by 3 array
+    format, where N is the number of non-zero values.  Column 1 values
+    [0:N] are the (1-based) row indices of the each non-zero value,
+    column 2 [0:N] are the column indices, column 3 [0:N] are the
+    (real) values.  The last values [-1,0:2] of the rows, column
+    indices are shape[0] and shape[1] respectively of the output
+    matrix. The last value for the values column is a padding 0. mrows
+    and ncols values from the header give the shape of the stored
+    matrix, here [N+1, 3].  Complex data is saved as a 4 column
+    matrix, where the fourth column contains the imaginary component;
+    the last value is again 0.  Complex sparse data do _not_ have the
+    header imagf field set to True; the fact that the data are complex
+    is only detectable because there are 4 storage columns
     '''
     def get_raw_array(self):
         self.header.original_dtype = dtype(float64)
         res = self.read_hdr_array()
         tmp = res[:-1,:]
         dims = res[-1,0:2]
-        ij = transpose(tmp[:,0:2]) - 1 # for matlab 1-based indexing
+        ij = transpose(tmp[:,0:2]) - 1 # for 1-based indexing
         vals = tmp[:,2]
         if res.shape[1] == 4:
             self.header.is_complex = True
@@ -207,7 +207,7 @@
         return self._array_reader.matrix_getter_factory()
 
     def format_looks_right(self):
-        # Matlab 4 files have a zero somewhere in first 4 bytes
+        # Mat4 files have a zero somewhere in first 4 bytes
         self.mat_stream.seek(0)
         mopt_bytes = self.read_bytes(4)
         self.mat_stream.seek(0)
@@ -224,8 +224,15 @@
 
 class Mat4MatrixWriter(MatStreamWriter):
 
-    def write_header(self, P,  T, dims, imagf):
-        ''' Write header for data type, matrix class, dims, complex flag '''
+    def write_header(self, P=0,  T=0, imagf=0, dims=None):
+        ''' Write header for given data options
+        @P      - mat4 data type
+        @T      - mat4 matrix class
+        @imagf  - complex flag
+        @dims   - matrix dimensions
+        '''
+        if dims is None:
+            dims = self.arr.shape
         header = empty((), mdtypes_template['header'])
         M = not ByteOrder.little_endian
         O = 0
@@ -264,7 +271,9 @@
             else:
                 self.arr = self.arr.astype('f8')
             P = miDOUBLE
-        self.write_header(P, 0, self.arr.shape, imagf)
+        self.write_header(P=P,
+                          T=mxFULL_CLASS,
+                          imagf=imagf)
         if imagf:
             self.write_bytes(self.arr.real)
             self.write_bytes(self.arr.imag)
@@ -278,7 +287,8 @@
         self.arr_to_chars()
         self.arr_to_2d()
         dims = self.arr.shape
-        self.write_header(miUINT8, 1, dims, 0)
+        self.write_header(P=miUINT8,
+                          T=mxCHAR_CLASS)
         if self.arr.dtype.kind == 'U':
             # Recode unicode to ascii
             n_chars = product(dims)
@@ -290,18 +300,45 @@
         self.write_bytes(self.arr)
 
 
+class Mat4SparseWriter(Mat4MatrixWriter):
+
+    def write(self):
+        ''' Sparse matrices are 2D
+        See docstring for Mat4SparseGetter
+        '''
+        imagf = self.arr.dtype.kind == 'c'
+        N = self.arr.nnz
+        ijd = zeros((N+1, 3+imagf), dtype='f8')
+        for i in range(N):
+            ijd[i,0], ijd[i,1] = self.arr.rowcol(i)
+        ijd[:-1,0:2] += 1 # 1 based indexing
+        if imagf:
+            ijd[:-1,2] = self.arr.data.real
+            ijd[:-1,3] = self.arr.data.imag
+        else:
+            ijd[:-1,2] = self.arr.data
+        ijd[-1,0:2] = self.arr.shape
+        self.write_header(P=miDOUBLE,
+                          T=mxSPARSE_CLASS,
+                          dims=ijd.shape)
+        self.write_bytes(ijd)
+        
+            
 def matrix_writer_factory(stream, arr, name):
     ''' Factory function to return matrix writer given variable to write
     @stream      - file or file-like stream to write to
     @arr         - array to write
-    @name        - name in matlab workspace
+    @name        - name in matlab (TM) workspace
     '''
+    if have_sparse:
+        if scipy.sparse.issparse(arr):
+            return Mat4SparseWriter(stream, arr, name)
     arr = array(arr)
     if arr.dtype.hasobject:
         raise TypeError, 'Cannot save object arrays in Mat4'
     if have_sparse:
         if scipy.sparse.issparse(arr):
-            raise TypeError, 'Cannot save sparse arrays yet'
+            return Mat4SparseWriter(stream, arr, name)
     if arr.dtype.kind in ('U', 'S'):
         return Mat4CharWriter(stream, arr, name)
     else:

Modified: trunk/Lib/io/mio5.py
===================================================================
--- trunk/Lib/io/mio5.py	2006-09-19 15:16:02 UTC (rev 2202)
+++ trunk/Lib/io/mio5.py	2006-09-19 22:14:27 UTC (rev 2203)
@@ -1,4 +1,4 @@
-''' Classes for read / write of matlab 5 files
+''' Classes for read / write of matlab (TM) 5 files
 '''
 
 # Small fragments of current code adapted from matfile.py by Heiko
@@ -102,10 +102,10 @@
     mxDOUBLE_CLASS: 'f8',
     }
 
-''' Before release v7.1 (release 14) matlab used the system default
-character encoding scheme padded out to 16-bits. Release 14 and later
-use Unicode. When saving character data, matlab R14 checks if it can
-be encoded in 7-bit ascii, and saves in that format if so.'''
+''' Before release v7.1 (release 14) matlab (TM) used the system
+default character encoding scheme padded out to 16-bits. Release 14
+and later use Unicode. When saving character data, R14 checks if it
+can be encoded in 7-bit ascii, and saves in that format if so.'''
 
 codecs_template = {
     miUTF8: {'codec': 'utf_8', 'width': 1},
@@ -145,13 +145,13 @@
     next_position - start position of next matrix
     name
     dtype - numpy dtype of matrix
-    mclass - matlab code for class of matrix
+    mclass - matlab (TM) code for class of matrix
     dims - shape of matrix as stored (see sparse reader)
     is_complex - True if data are complex
     is_char    - True if these are char data
-    is_global  - is a global variable in matlab workspace
+    is_global  - is a global variable in matlab (TM) workspace
     is_numeric - is basic numeric matrix
-    original_dtype - data type when saved from matlab
+    original_dtype - data type when saved from matlab (TM)
     '''
     def __init__(self):
         self.next_position = None
@@ -219,7 +219,8 @@
             try:
                 dt = self.dtypes[tag.mdtype]
             except KeyError:
-                raise TypeError, 'Do not know matlab data code %d' % tag.mdtype
+                raise TypeError, 'Do not know matlab (TM) data code %d' \
+                      % tag.mdtype
             el_count = tag.byte_count / dt.itemsize
             el = self.read_array(dt, a_shape=(el_count), copy=copy)
         if tag.skip:
@@ -369,7 +370,7 @@
             res = res + (res_j * 1j)
         else:
             res = self.read_element()
-        ''' From the matlab API documentation, last found here:
+        ''' From the matlab (TM) API documentation, last found here:
         http://www.mathworks.com/access/helpdesk/help/techdoc/matlab_external/
         @rowind are simply the row indices for all the (@res) non-zero
         entries in the sparse array.  @rowind has nzmax entries, so
@@ -555,7 +556,7 @@
         return hdict
         
     def format_looks_right(self):
-        # Matlab 4 files have a zero somewhere in first 4 bytes
+        # Mat4 files have a zero somewhere in first 4 bytes
         self.mat_stream.seek(0)
         mopt_bytes = self.read_bytes(4)
         self.mat_stream.seek(0)

Modified: trunk/Lib/io/miobase.py
===================================================================
--- trunk/Lib/io/miobase.py	2006-09-19 15:16:02 UTC (rev 2202)
+++ trunk/Lib/io/miobase.py	2006-09-19 22:14:27 UTC (rev 2203)
@@ -1,7 +1,7 @@
 # Authors: Travis Oliphant, Matthew Brett
 
 """
-Base classes for matlab file stream reading
+Base classes for matlab (TM) file stream reading
 """
 
 import sys
@@ -100,7 +100,7 @@
                           or in ('little', '<')
                           or in ('BIG', '>')
     @base_name          - base name for unnamed variables
-    @matlab_compatible  - return arrays as matlab saved them
+    @matlab_compatible  - return arrays as matlab (TM) saved them
     @squeeze_me         - whether to squeeze unit dimensions or not
     @chars_as_strings   - whether to convert char arrays to string arrays
 
@@ -184,7 +184,7 @@
         ''' Processing to apply to read matrices
 
         Function applies options to matrices. We have to pass this
-        function into the reader routines because Matlab 5 matrices
+        function into the reader routines because Mat5 matrices
         occur as submatrices - in cell arrays, structs and objects -
         so we will not see these in the main variable getting routine
         here.
@@ -206,11 +206,12 @@
                 else: # return string
                     arr = self.chars_to_str(arr)
             if self.matlab_compatible:
-                # Apply options to replicate matlab's load into workspace
+                # Apply options to replicate matlab's (TM)
+                # load into workspace
                 if header.is_logical:
                     arr = arr.astype(bool)
                 elif header.is_numeric:
-                    # Cast as original matlab type
+                    # Cast as original matlab (TM) type
                     if header.original_dtype:
                         arr = arr.astype(header.original_dtype)
             if self.squeeze_me:
@@ -331,7 +332,7 @@
         return dtype(self.arr.dtype.str[:2] + str(num))
 
     def arr_to_chars(self):
-        ''' Converts string array to matlab char array '''
+        ''' Convert string array to char array '''
         dims = list(self.arr.shape)
         if not dims:
             dims = [1]

Modified: trunk/Lib/io/tests/test_mio.py
===================================================================
--- trunk/Lib/io/tests/test_mio.py	2006-09-19 15:16:02 UTC (rev 2202)
+++ trunk/Lib/io/tests/test_mio.py	2006-09-19 22:14:27 UTC (rev 2203)
@@ -100,79 +100,80 @@
 
     # Define cases to test
     theta = pi/4*arange(9,dtype=float)
-    case_table = [
+    case_table4 = [
         {'name': 'double',
          'expected': {'testdouble': theta}
          }]
-    case_table.append(
+    case_table4.append(
         {'name': 'string',
          'expected': {'teststring': u'"Do nine men interpret?" "Nine men," I nod.'},
          })
-    case_table.append(
+    case_table4.append(
         {'name': 'complex',
          'expected': {'testcomplex': cos(theta) + 1j*sin(theta)}
          })
-    case_table.append(
-        {'name': 'cell',
-         'expected': {'testcell':
-                      array([u'This cell contains this string and 3 arrays of '+\
-                             'increasing length',
-                             array(1), array([1,2]), array([1,2,3])], 
-                            dtype=object)}
-         })
-    st = mat_struct()
-    st.stringfield = u'Rats live on no evil star.'
-    st.doublefield = array([sqrt(2),exp(1),pi])
-    st.complexfield = (1+1j)*array([sqrt(2),exp(1),pi])
-    case_table.append(
-        {'name': 'struct', 
-         'expected': {'teststruct': st}
-         })
     A = zeros((3,5))
     A[0] = range(1,6)
     A[:,0] = range(1,4)
-    case_table.append(
+    case_table4.append(
         {'name': 'matrix',
          'expected': {'testmatrix': A},
          })
-    case_table.append(
-        {'name': '3dmatrix',
-         'expected': {'test3dmatrix': transpose(reshape(range(1,25), (4,3,2)))}
-         })
-    case_table.append(
+    case_table4.append(
         {'name': 'sparse',
          'expected': {'testsparse': SP.csc_matrix(A)},
          })
     B = A.astype(complex)
     B[0,0] += 1j
-    case_table.append(
+    case_table4.append(
         {'name': 'sparsecomplex',
          'expected': {'testsparsecomplex': SP.csc_matrix(B)},
          })
-    case_table.append(
+    case_table4.append(
         {'name': 'multi',
          'expected': {'theta': theta,
                       'a': A},
          })
-    case_table.append(
+    case_table4.append(
         {'name': 'minus',
          'expected': {'testminus': array(-1)},
          })
-    case_table.append(
+    case_table4.append(
         {'name': 'onechar',
          'expected': {'testonechar': u'r'},
          })
-    case_table.append(
+    case_table5 = [
+        {'name': 'cell',
+         'expected': {'testcell':
+                      array([u'This cell contains this string and 3 arrays of '+\
+                             'increasing length',
+                             array(1), array([1,2]), array([1,2,3])], 
+                            dtype=object)}
+         }]
+    case_table5.append(
         {'name': 'stringarray',
-         'expected': {'teststringarray': array([u'one  ', u'two  ', u'three'], dtype=object)},
+         'expected': {'teststringarray': array(
+        [u'one  ', u'two  ', u'three'], dtype=object)},
          })
+    case_table5.append(
+        {'name': '3dmatrix',
+         'expected': {'test3dmatrix': transpose(reshape(range(1,25), (4,3,2)))}
+         })
+    st = mat_struct()
+    st.stringfield = u'Rats live on no evil star.'
+    st.doublefield = array([sqrt(2),exp(1),pi])
+    st.complexfield = (1+1j)*array([sqrt(2),exp(1),pi])
+    case_table5.append(
+        {'name': 'struct', 
+         'expected': {'teststruct': st}
+         })
     a = array([array(1),
                array([array(2), array(3),
                       array([array(4), array(5)],
                             dtype=object)],
                      dtype=object)],
               dtype=object)
-    case_table.append(
+    case_table5.append(
         {'name': 'cellnest',
          'expected': {'testcellnest': a},
          })
@@ -180,7 +181,7 @@
     st.one = array(1)
     st.two = mat_struct()
     st.two.three = u'number 3'
-    case_table.append(
+    case_table5.append(
         {'name': 'structnest',
          'expected': {'teststructnest': st}
          })
@@ -189,7 +190,7 @@
     a[0].two = array(2)
     a[1].one = u'number 1'
     a[1].two = u'number 2'
-    case_table.append(
+    case_table5.append(
         {'name': 'structarr',
          'expected': {'teststructarr': a}
          })
@@ -201,19 +202,19 @@
     a.isEmpty = array(0)
     a.numArgs = array(1)
     a.version = array(1)
-    case_table.append(
+    case_table5.append(
         {'name': 'object',
          'expected': {'testobject': a}
          })
     u_str = file(
         os.path.join(test_data_path, 'japanese_utf8.txt'),
         'rb').read().decode('utf-8')
-    case_table.append(
+    case_table5.append(
         {'name': 'unicode',
         'expected': {'testunicode': u_str}
         })
     # add load tests
-    for case in case_table:
+    for case in case_table4 + case_table5:
         name = case['name']
         expected = case['expected']
         filt = os.path.join(test_data_path, 'test%s_*.mat' % name)
@@ -221,24 +222,7 @@
         assert files, "No files for test %s using filter %s" % (name, filt)
         exec 'check_%s = _make_check_case(name, files, expected)' % name
     # round trip tests
-    case_table = [
-        {'name': 'double',
-         'expected': {'testdouble': theta}
-         }]
-    case_table.append(
-        {'name': 'string',
-         'expected': {'teststring': u'"Do nine men interpret?" "Nine men," I nod.'},
-         })
-    case_table.append(
-        {'name': 'complex',
-         'expected': {'testcomplex': cos(theta) + 1j*sin(theta)}
-         })
-    case_table.append(
-        {'name': 'multi',
-         'expected': {'theta': theta,
-                      'a': A},
-         })
-    for case in case_table:
+    for case in case_table4:
         name = case['name'] + '_round_trip'
         expected = case['expected']
         exec 'check_%s = _make_rt_check_case(name, expected)' % name




More information about the Scipy-svn mailing list