mio.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. """
  2. Module for reading and writing matlab (TM) .mat files
  3. """
  4. # Authors: Travis Oliphant, Matthew Brett
  5. from __future__ import division, print_function, absolute_import
  6. from scipy._lib.six import string_types
  7. from .miobase import get_matfile_version, docfiller
  8. from .mio4 import MatFile4Reader, MatFile4Writer
  9. from .mio5 import MatFile5Reader, MatFile5Writer
  10. __all__ = ['mat_reader_factory', 'loadmat', 'savemat', 'whosmat']
  11. def _open_file(file_like, appendmat):
  12. """
  13. Open `file_like` and return as file-like object. First, check if object is
  14. already file-like; if so, return it as-is. Otherwise, try to pass it
  15. to open(). If that fails, and `file_like` is a string, and `appendmat` is true,
  16. append '.mat' and try again.
  17. """
  18. try:
  19. file_like.read(0)
  20. return file_like, False
  21. except AttributeError:
  22. pass
  23. try:
  24. return open(file_like, 'rb'), True
  25. except IOError:
  26. # Probably "not found"
  27. if isinstance(file_like, string_types):
  28. if appendmat and not file_like.endswith('.mat'):
  29. file_like += '.mat'
  30. return open(file_like, 'rb'), True
  31. else:
  32. raise IOError('Reader needs file name or open file-like object')
  33. @docfiller
  34. def mat_reader_factory(file_name, appendmat=True, **kwargs):
  35. """
  36. Create reader for matlab .mat format files.
  37. Parameters
  38. ----------
  39. %(file_arg)s
  40. %(append_arg)s
  41. %(load_args)s
  42. %(struct_arg)s
  43. Returns
  44. -------
  45. matreader : MatFileReader object
  46. Initialized instance of MatFileReader class matching the mat file
  47. type detected in `filename`.
  48. file_opened : bool
  49. Whether the file was opened by this routine.
  50. """
  51. byte_stream, file_opened = _open_file(file_name, appendmat)
  52. mjv, mnv = get_matfile_version(byte_stream)
  53. if mjv == 0:
  54. return MatFile4Reader(byte_stream, **kwargs), file_opened
  55. elif mjv == 1:
  56. return MatFile5Reader(byte_stream, **kwargs), file_opened
  57. elif mjv == 2:
  58. raise NotImplementedError('Please use HDF reader for matlab v7.3 files')
  59. else:
  60. raise TypeError('Did not recognize version %s' % mjv)
  61. @docfiller
  62. def loadmat(file_name, mdict=None, appendmat=True, **kwargs):
  63. """
  64. Load MATLAB file.
  65. Parameters
  66. ----------
  67. file_name : str
  68. Name of the mat file (do not need .mat extension if
  69. appendmat==True). Can also pass open file-like object.
  70. mdict : dict, optional
  71. Dictionary in which to insert matfile variables.
  72. appendmat : bool, optional
  73. True to append the .mat extension to the end of the given
  74. filename, if not already present.
  75. byte_order : str or None, optional
  76. None by default, implying byte order guessed from mat
  77. file. Otherwise can be one of ('native', '=', 'little', '<',
  78. 'BIG', '>').
  79. mat_dtype : bool, optional
  80. If True, return arrays in same dtype as would be loaded into
  81. MATLAB (instead of the dtype with which they are saved).
  82. squeeze_me : bool, optional
  83. Whether to squeeze unit matrix dimensions or not.
  84. chars_as_strings : bool, optional
  85. Whether to convert char arrays to string arrays.
  86. matlab_compatible : bool, optional
  87. Returns matrices as would be loaded by MATLAB (implies
  88. squeeze_me=False, chars_as_strings=False, mat_dtype=True,
  89. struct_as_record=True).
  90. struct_as_record : bool, optional
  91. Whether to load MATLAB structs as numpy record arrays, or as
  92. old-style numpy arrays with dtype=object. Setting this flag to
  93. False replicates the behavior of scipy version 0.7.x (returning
  94. numpy object arrays). The default setting is True, because it
  95. allows easier round-trip load and save of MATLAB files.
  96. verify_compressed_data_integrity : bool, optional
  97. Whether the length of compressed sequences in the MATLAB file
  98. should be checked, to ensure that they are not longer than we expect.
  99. It is advisable to enable this (the default) because overlong
  100. compressed sequences in MATLAB files generally indicate that the
  101. files have experienced some sort of corruption.
  102. variable_names : None or sequence
  103. If None (the default) - read all variables in file. Otherwise
  104. `variable_names` should be a sequence of strings, giving names of the
  105. MATLAB variables to read from the file. The reader will skip any
  106. variable with a name not in this sequence, possibly saving some read
  107. processing.
  108. Returns
  109. -------
  110. mat_dict : dict
  111. dictionary with variable names as keys, and loaded matrices as
  112. values.
  113. Notes
  114. -----
  115. v4 (Level 1.0), v6 and v7 to 7.2 matfiles are supported.
  116. You will need an HDF5 python library to read MATLAB 7.3 format mat
  117. files. Because scipy does not supply one, we do not implement the
  118. HDF5 / 7.3 interface here.
  119. Examples
  120. --------
  121. >>> from os.path import dirname, join as pjoin
  122. >>> import scipy.io as sio
  123. Get the filename for an example .mat file from the tests/data directory.
  124. >>> data_dir = pjoin(dirname(sio.__file__), 'matlab', 'tests', 'data')
  125. >>> mat_fname = pjoin(data_dir, 'testdouble_7.4_GLNX86.mat')
  126. Load the .mat file contents.
  127. >>> mat_contents = sio.loadmat(mat_fname)
  128. The result is a dictionary, one key/value pair for each variable:
  129. >>> sorted(mat_contents.keys())
  130. ['__globals__', '__header__', '__version__', 'testdouble']
  131. >>> mat_contents['testdouble']
  132. array([[0. , 0.78539816, 1.57079633, 2.35619449, 3.14159265,
  133. 3.92699082, 4.71238898, 5.49778714, 6.28318531]])
  134. By default SciPy reads MATLAB structs as structured NumPy arrays where the
  135. dtype fields are of type `object` and the names correspond to the MATLAB
  136. struct field names. This can be disabled by setting the optional argument
  137. `struct_as_record=False`.
  138. Get the filename for an example .mat file that contains a MATLAB struct
  139. called `teststruct` and load the contents.
  140. >>> matstruct_fname = pjoin(data_dir, 'teststruct_7.4_GLNX86.mat')
  141. >>> matstruct_contents = sio.loadmat(matstruct_fname)
  142. >>> teststruct = matstruct_contents['teststruct']
  143. >>> teststruct.dtype
  144. dtype([('stringfield', 'O'), ('doublefield', 'O'), ('complexfield', 'O')])
  145. The size of the structured array is the size of the MATLAB struct, not the
  146. number of elements in any particular field. The shape defaults to 2-D
  147. unless the optional argument `squeeze_me=True`, in which case all length 1
  148. dimensions are removed.
  149. >>> teststruct.size
  150. 1
  151. >>> teststruct.shape
  152. (1, 1)
  153. Get the 'stringfield' of the first element in the MATLAB struct.
  154. >>> teststruct[0, 0]['stringfield']
  155. array(['Rats live on no evil star.'],
  156. dtype='<U26')
  157. Get the first element of the 'doublefield'.
  158. >>> teststruct['doublefield'][0, 0]
  159. array([[ 1.41421356, 2.71828183, 3.14159265]])
  160. Load the MATLAB struct, squeezing out length 1 dimensions, and get the item
  161. from the 'complexfield'.
  162. >>> matstruct_squeezed = sio.loadmat(matstruct_fname, squeeze_me=True)
  163. >>> matstruct_squeezed['teststruct'].shape
  164. ()
  165. >>> matstruct_squeezed['teststruct']['complexfield'].shape
  166. ()
  167. >>> matstruct_squeezed['teststruct']['complexfield'].item()
  168. array([ 1.41421356+1.41421356j, 2.71828183+2.71828183j,
  169. 3.14159265+3.14159265j])
  170. """
  171. variable_names = kwargs.pop('variable_names', None)
  172. MR, file_opened = mat_reader_factory(file_name, appendmat, **kwargs)
  173. matfile_dict = MR.get_variables(variable_names)
  174. if mdict is not None:
  175. mdict.update(matfile_dict)
  176. else:
  177. mdict = matfile_dict
  178. if file_opened:
  179. MR.mat_stream.close()
  180. return mdict
  181. @docfiller
  182. def savemat(file_name, mdict,
  183. appendmat=True,
  184. format='5',
  185. long_field_names=False,
  186. do_compression=False,
  187. oned_as='row'):
  188. """
  189. Save a dictionary of names and arrays into a MATLAB-style .mat file.
  190. This saves the array objects in the given dictionary to a MATLAB-
  191. style .mat file.
  192. Parameters
  193. ----------
  194. file_name : str or file-like object
  195. Name of the .mat file (.mat extension not needed if ``appendmat ==
  196. True``).
  197. Can also pass open file_like object.
  198. mdict : dict
  199. Dictionary from which to save matfile variables.
  200. appendmat : bool, optional
  201. True (the default) to append the .mat extension to the end of the
  202. given filename, if not already present.
  203. format : {'5', '4'}, string, optional
  204. '5' (the default) for MATLAB 5 and up (to 7.2),
  205. '4' for MATLAB 4 .mat files.
  206. long_field_names : bool, optional
  207. False (the default) - maximum field name length in a structure is
  208. 31 characters which is the documented maximum length.
  209. True - maximum field name length in a structure is 63 characters
  210. which works for MATLAB 7.6+.
  211. do_compression : bool, optional
  212. Whether or not to compress matrices on write. Default is False.
  213. oned_as : {'row', 'column'}, optional
  214. If 'column', write 1-D numpy arrays as column vectors.
  215. If 'row', write 1-D numpy arrays as row vectors.
  216. See also
  217. --------
  218. mio4.MatFile4Writer
  219. mio5.MatFile5Writer
  220. """
  221. file_opened = False
  222. if hasattr(file_name, 'write'):
  223. # File-like object already; use as-is
  224. file_stream = file_name
  225. else:
  226. if isinstance(file_name, string_types):
  227. if appendmat and not file_name.endswith('.mat'):
  228. file_name = file_name + ".mat"
  229. file_stream = open(file_name, 'wb')
  230. file_opened = True
  231. if format == '4':
  232. if long_field_names:
  233. raise ValueError("Long field names are not available for version 4 files")
  234. MW = MatFile4Writer(file_stream, oned_as)
  235. elif format == '5':
  236. MW = MatFile5Writer(file_stream,
  237. do_compression=do_compression,
  238. unicode_strings=True,
  239. long_field_names=long_field_names,
  240. oned_as=oned_as)
  241. else:
  242. raise ValueError("Format should be '4' or '5'")
  243. MW.put_variables(mdict)
  244. if file_opened:
  245. file_stream.close()
  246. @docfiller
  247. def whosmat(file_name, appendmat=True, **kwargs):
  248. """
  249. List variables inside a MATLAB file.
  250. Parameters
  251. ----------
  252. %(file_arg)s
  253. %(append_arg)s
  254. %(load_args)s
  255. %(struct_arg)s
  256. Returns
  257. -------
  258. variables : list of tuples
  259. A list of tuples, where each tuple holds the matrix name (a string),
  260. its shape (tuple of ints), and its data class (a string).
  261. Possible data classes are: int8, uint8, int16, uint16, int32, uint32,
  262. int64, uint64, single, double, cell, struct, object, char, sparse,
  263. function, opaque, logical, unknown.
  264. Notes
  265. -----
  266. v4 (Level 1.0), v6 and v7 to 7.2 matfiles are supported.
  267. You will need an HDF5 python library to read matlab 7.3 format mat
  268. files. Because scipy does not supply one, we do not implement the
  269. HDF5 / 7.3 interface here.
  270. .. versionadded:: 0.12.0
  271. """
  272. ML, file_opened = mat_reader_factory(file_name, **kwargs)
  273. variables = ML.list_variables()
  274. if file_opened:
  275. ML.mat_stream.close()
  276. return variables