Как читать мат файл v7.3 через h5py?

У меня есть структурный массив, созданный matlab и хранящийся в файле mat формата v7.3:

struArray = struct('name', {'one', 'two', 'three'}, 
                   'id', {1,2,3}, 
                   'data', {[1:10], [3:9], [0]})
save('test.mat', 'struArray', '-v7.3')

Теперь я хочу прочитать этот файл через python, используя h5py:

data = h5py.File('test.mat')
struArray = data['/struArray']

Понятия не имею, как получить данные структуры одну за другой из struArray:

for index in range(<the size of struArray>):
    elem = <the index th struct in struArray>
    name = <the name of elem>
    id = <the id of elem>
    data = <the data of elem>

person Eastsun    schedule 11.10.2013    source источник
comment
Вы нашли для этого реальное решение?   -  person Pastafarian    schedule 29.04.2015
comment
У меня был похожий вопрос с частичным решением: stackoverflow.com/questions/29852481/   -  person CodyF    schedule 25.01.2016


Ответы (5)


Формат файла Matlab 7.3 не очень прост для работы с h5py. Он основан на ссылке HDF5, ср. h5py-документация по ссылкам.

>>> import h5py
>>> f = h5py.File('test.mat')
>>> list(f.keys())
['#refs#', 'struArray']
>>> struArray = f['struArray']
>>> struArray['name'][0, 0]  # this is the HDF5 reference
<HDF5 object reference>
>>> f[struArray['name'][0, 0]].value  # this is the actual data
array([[111],
       [110],
       [101]], dtype=uint16)

Чтобы прочитать struArray(i).id:

>>> f[struArray['id'][0, 0]][0, 0]
1.0
>>> f[struArray['id'][1, 0]][0, 0]
2.0
>>> f[struArray['id'][2, 0]][0, 0]
3.0

Обратите внимание, что Matlab хранит число как массив размером (1, 1), следовательно, последний [0, 0] для получения числа.

Чтобы прочитать struArray(i).data:

>>> f[struArray['data'][0, 0]].value
array([[  1.],
       [  2.],
       [  3.],
       [  4.],
       [  5.],
       [  6.],
       [  7.],
       [  8.],
       [  9.],
       [ 10.]])

Чтобы прочитать struArray(i).name, необходимо преобразовать массив целых чисел в строку:

>>> f[struArray['name'][0, 0]].value.tobytes()[::2].decode()
'one'
>>> f[struArray['name'][1, 0]].value.tobytes()[::2].decode()
'two'
>>> f[struArray['name'][2, 0]].value.tobytes()[::2].decode()
'three'
person nbedou    schedule 09.08.2017

visit или visititems - это быстрый способ увидеть общую структуру h5py файла:

fs['struArray'].visititems(lambda n,o:print(n, o))

Когда я запускаю это в файле, созданном Octave save -hdf5, я получаю:

type <HDF5 dataset "type": shape (), type "|S7">
value <HDF5 group "/struArray/value" (3 members)>
value/data <HDF5 group "/struArray/value/data" (2 members)>
value/data/type <HDF5 dataset "type": shape (), type "|S5">
value/data/value <HDF5 group "/struArray/value/data/value" (4 members)>
value/data/value/_0 <HDF5 group "/struArray/value/data/value/_0" (2 members)>
value/data/value/_0/type <HDF5 dataset "type": shape (), type "|S7">
value/data/value/_0/value <HDF5 dataset "value": shape (10, 1), type "<f8">
value/data/value/_1 <HDF5 group "/struArray/value/data/value/_1" (2 members)>
...
value/data/value/dims <HDF5 dataset "dims": shape (2,), type "<i4">
value/id <HDF5 group "/struArray/value/id" (2 members)>
value/id/type <HDF5 dataset "type": shape (), type "|S5">
value/id/value <HDF5 group "/struArray/value/id/value" (4 members)>
value/id/value/_0 <HDF5 group "/struArray/value/id/value/_0" (2 members)>
...
value/id/value/_2/value <HDF5 dataset "value": shape (), type "<f8">
value/id/value/dims <HDF5 dataset "dims": shape (2,), type "<i4">
value/name <HDF5 group "/struArray/value/name" (2 members)>
...
value/name/value/dims <HDF5 dataset "dims": shape (2,), type "<i4">

Это может быть не то же самое, что производит MATLAB 7.3, но это дает представление о сложности структуры.

Более точный обратный вызов может отображать значения и может быть отправной точкой для воссоздания объекта Python (словаря, списков и т. Д.).

def callback(name, obj):
    if name.endswith('type'):
        print('type:', obj.value)
    elif name.endswith('value'):
        if type(obj).__name__=='Dataset':
            print(obj.value.T)  # http://stackoverflow.com/questions/21624653
    elif name.endswith('dims'):
        print('dims:', obj.value)
    else:
        print('name:', name)

fs.visititems(callback)

производит:

name: struArray
type: b'struct'
name: struArray/value/data
type: b'cell'
name: struArray/value/data/value/_0
type: b'matrix'
[[  1.   2.   3.   4.   5.   6.   7.   8.   9.  10.]]
name: struArray/value/data/value/_1
type: b'matrix'
[[ 3.  4.  5.  6.  7.  8.  9.]]
name: struArray/value/data/value/_2
type: b'scalar'
0.0
dims: [3 1]
name: struArray/value/id
type: b'cell'
name: struArray/value/id/value/_0
type: b'scalar'
1.0
...
dims: [3 1]
name: struArray/value/name
type: b'cell'
name: struArray/value/name/value/_0
type: b'sq_string'
[[111 110 101]]
...
dims: [3 1]
person hpaulj    schedule 30.12.2014

Извините, но я думаю, что получить содержимое ячеек / структур извне Matlab будет довольно сложно. Если вы просмотрите созданные файлы (например, с помощью HDFView), вы увидите, что есть много перекрестных ссылок и нет очевидного способа продолжить.

Если вы придерживаетесь простых числовых массивов, он отлично работает. Если у вас есть небольшие массивы ячеек, содержащие числовые массивы, вы можете преобразовать их в отдельные переменные (например, cellcontents1, cellcontents2 и т. Д.), Что обычно занимает всего несколько строк и позволяет сохранять и загружать их напрямую. Итак, в вашем примере я бы сохранил файл с vars name1, name2, name3, id1, id2, id3 ... и т. Д.

РЕДАКТИРОВАТЬ: вы указали h5py в вопросе, поэтому я ответил на это, но стоит упомянуть, что с scipy.io.loadmat вы сможете преобразовать исходные переменные в эквиваленты numpy (например, массивы объектов).

person robince    schedule 11.10.2013
comment
Спасибо, в любом случае! Я борюсь с этой проблемой несколько дней. Я всегда получал что-то вроде <HDF5 object reference>, а не реальную ценность. Однако scipy.io.loadmat не работает для формата файла mat v7.3. - person Eastsun; 11.10.2013

Я бы начал с запуска интерпретатора и запуска help на struarray. Он должен дать вам достаточно информации, чтобы вы начали. В противном случае вы можете сбросить атрибуты любого объекта Python, print установив атрибут __dict__.

person Alex Chamberlain    schedule 11.10.2013

Это действительно проблема с Matlab 7.3 и h5py. Моя уловка - преобразовать тип h5py._hl.dataset.Dataset в массив numpy. Например,

np.array(data['data'])

решит вашу проблему с полем 'data'.

person Noam Peled    schedule 12.01.2014
comment
Не работает. Просто добавляет еще один слой массива поверх существующего. Например. array([[<HDF5 object reference>, <HDF5 object reference>, <HDF5 object reference>]], dtype=object) И существующие данные имеют тип h5py._hl.dataset.Dataset - person Pastafarian; 29.04.2015