SciPy (произносится как сай пай) — это пакет прикладных математических процедур, основанный на расширении Numpy Python. С SciPy интерактивный сеанс Python превращается в такую же полноценную среду обработки данных и прототипирования сложных систем, как MATLAB, IDL, Octave, R-Lab и SciLab. В этом посте я хотел бы рассказать о возможностях пакета ввода/вывода scipy.io, который позволяет работать с файлами данных Octave и MATLAB.
Введение
Для начала импортируем пакет scipy.io следующим образом:
import scipy.io as sio
Основные процедуры пакета scipy.io, которые позволяют работать с файлами MATLAB:
sio.loadmat
sio.savemat
sio.whosmat
Чтобы не нарушать лицензионного соглашения MATLAB, работать будем в среде GNU Octave, которая имеет совместимые с MATLAB функции сохранения и загрузки. Введем в командной строке Octave:
octave:1> a = 1:12
a =
1 2 3 4 5 6 7 8 9 10 11 12
octave:2> a = reshape(a, [1 3 4])
a =
ans(:,:,1) =
1 2 3
ans(:,:,2) =
4 5 6
ans(:,:,3) =
7 8 9
ans(:,:,4) =
10 11 12
octave:3> save -6 octave_a.mat a % MATLAB 6 compatible
octave:4> ls octave_a.mat
octave_a.mat
Код для импорта MATLAB файла в Python:
mat_contents = sio.loadmat('octave_a.mat')
mat_contents
{'__header__': b'MATLAB 5.0 MAT-file, written by Octave 4.2.2, 2019-02-02 20:26:43 UTC',
'__version__': '1.0',
'__globals__': [],
'a': array([[[ 1., 4., 7., 10.],
[ 2., 5., 8., 11.],
[ 3., 6., 9., 12.]]])}
oct_a = mat_contents['a']
oct_a
array([[[ 1., 4., 7., 10.],
[ 2., 5., 8., 11.],
[ 3., 6., 9., 12.]]])
oct_a.shape
(1, 3, 4)
Как видим, файл прочитан верно. Теперь рассмотрим экспорт из SciPy в MATLAB:
import numpy as np
vect = np.arange (10)
vect.shape
(10,)
sio.savemat ('np_vector.mat', {'vect': vect})
Импортируем Python файл в Octave:
octave:8> load np_vector.mat
octave:9> vect
vect =
0 1 2 3 4 5 6 7 8 9
octave:10> size(vect)
ans =
1 10
Чтобы проверить содержимое файла MATLAB без чтения данных в памяти, используем команду whosmat:
sio.whosmat ('octave_a.mat')
[('a', (1, 3, 4), 'double')]
Функция whosmat возвращает список кортежей, по одному для каждого массива (или другого объекта), который содержится в файле MATLAB. Каждый кортеж содержит имя, содержимое файла и тип данных.
Структуры MATLAB
Структуры MATLAB похожи на словари Python dicts. Отличие заключается в том, что именем поля обязательно должна быть строка. Значением поля может быть любой объект.
Вспомним, что MATLAB — акроним MATrix LABoratory. Т.к. основное назначение MATLAB — работа с матрицами, поэтому все объекты в ней — это матрицы. Даже одно число представляется в виде матрицы размера (1, 1).
octave:11> my_struct = struct('field1', 1, 'field2', 2)
my_struct =
{
field1 = 1
field2 = 2
}
octave:12> save -6 octave_struct.mat my_struct
Загрузим MATLAB структуру в Python:
mat_contents = sio.loadmat('octave_struct.mat')
mat_contents
{'__header__': b'MATLAB 5.0 MAT-file, written by Octave 4.2.2, 2019-02-02 20:34:26 UTC',
'__version__': '1.0',
'__globals__': [],
'my_struct': array([[(array([[1.]]), array([[2.]]))]],
dtype=[('field1', 'O'), ('field2', 'O')])}
oct_struct = mat_contents['my_struct']
oct_struct.shape
(1, 1)
val = oct_struct[0,0]
val
(array([[1.]]), array([[2.]]))
val['field1']
array([[1.]])
val['field2']
array([[2.]])
val.dtype
dtype([('field1', 'O'), ('field2', 'O')])
В версиях SciPy от 0.12.0 структуры MATLAB возвращаются как структурированные массивы numpy. Названия полей массива numpy являются названием полей структуры MATLAB. Имена полей можно прочитать с помощью команды dtype, как в примере выше. Подробнее про типы данных structed arrays .
Таким образом, в MATLAB массив структур имеет размер как минимум 2D, что повторяется при чтении в SciPy. Чтобы сократить размерность до 1, используем параметр squeeze_me:
mat_contents = sio.loadmat ('octave_struct.mat', squeeze_me = True)
oct_struct = mat_contents ['my_struct']
oct_struct.shape
()
Иногда удобнее загружать структуры MATLAB как объекты python, а не numpy массивы. Чтобы это сделать, используйте параметр struct_as_record = False для загрузки.
mat_contents = sio.loadmat ('octave_struct.mat', struct_as_record = False)
oct_struct = mat_contents ['my_struct']
oct_struct[0,0].field1
array([[1.]])
Параметр struct_as_record = False прекрасно работает совместно с параметром squeeze_me:
mat_contents = sio.loadmat('octave_struct.mat', struct_as_record=False, squeeze_me=True)
oct_struct = mat_contents['my_struct']
oct_struct.shape # выдаст ошибку, т.к. у скаляра нет аттрибута shape
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-23-d41d0a59bb9b> in <module>
1 mat_contents = sio.loadmat('octave_struct.mat', struct_as_record=False, squeeze_me=True)
2 oct_struct = mat_contents['my_struct']
----> 3 oct_struct.shape # выдаст ошибку, т.к. это скаляр
AttributeError: 'mat_struct' object has no attribute 'shape'
type(oct_struct)
scipy.io.matlab.mio5_params.mat_struct
oct_struct.field1
1.0
Самый простой способ экспорта структур из python в MATLAB — с помощью словарей dicts:
a_dict = {'field1': 0.5, 'field2': 'a string'}
sio.savemat ('saved_struct.mat', {'a_dict': a_dict})
В MATLAB загружается как:
octave:21> load saved_struct
octave:22> a_dict
a_dict =
scalar structure containing the fields:
field1 = 0.50000
field2 = a string
Также можно экспортировать структуры из python в MATLAB с помощью массивов numpy:
dt = [('f1', 'f8'), ('f2', 'S10')]
arr = np.zeros ((2,), dtype = dt)
arr
array([(0., b''), (0., b'')], dtype=[('f1', '<f8'), ('f2', 'S10')])
arr [0] ['f1'] = 0.5
arr [0] ['f2'] = 'python'
arr [1] ['f1'] = 99
arr [1] ['f2'] = 'not perl'
sio.savemat ('np_struct_arr.mat', {'arr': arr})
Массивы ячеек (cell) MATLAB
Массивы ячеек (cell) в MATLAB похожи на списки python. Элементы в массивах ячеек могут содержать любой тип объекта MATLAB. Также cell очень похожи на массивы объектов numpy. Рассмотрим пример экспорта cell из MATLAB в numpy.
octave:14> my_cells = {1, [2, 3]}
my_cells =
{
[1,1] = 1
[1,2] =
2 3
}
octave:15> save -6 octave_cells.mat my_cells
Вернемся к Python:
mat_contents = sio.loadmat ('octave_cells.mat')
oct_cells = mat_contents ['my_cells']
print (oct_cells.dtype)
object
val = oct_cells [0,0]
val
array([[1.]])
print (val.dtype)
float64
Экспорт из numpy в массив cell MATLAB сделаем c помощью numpy-массива объектов:
obj_arr = np.zeros ((2,), dtype = np.object)
obj_arr [0] = 1
obj_arr [1] = 'a string'
obj_arr
array([1, 'a string'], dtype=object)
sio.savemat ('np_cells.mat', {'obj_arr': obj_arr})
Проверим правильность экспорта cell из numpy в Octave:
octave:16> load np_cells.mat
octave:17> obj_arr
obj_arr =
{
[1,1] = 1
[2,1] = a string
}
На этом, пожалуй, закончим. Надеюсь для кого-то эта статья послужит поводом для интеграции исследований в MATLAB со свободным программным обеспечением.
Источник: Документация scipy
Автор: Андрей Лабинцев