Как читать файл макета v7.3 через h5py?
У меня есть массив struct, созданный matlab и хранящийся в файле макета формата 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>
Ответы
Ответ 1
Формат файла 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'
Ответ 2
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]
Ответ 3
Я начал бы запускать интерпретатор и запускать help
на struarray
. Он должен дать вам достаточно информации, чтобы вы начали. В противном случае вы можете сбросить атрибуты любого объекта Python с помощью print
с атрибутом __dict__
.
Ответ 4
Извините, но я думаю, будет сложно получить содержимое ячеек/структур из-за пределов Matlab. Если вы просмотрите созданные файлы (например, с HDFView), вы увидите, что есть много перекрестных ссылок и нет очевидного способа продолжения.
Если вы придерживаетесь простых числовых массивов, он отлично работает. Если у вас небольшие массивы ячеек, содержащие числовые массивы, вы можете преобразовать их в отдельные переменные (например, cellcontents1, cellcontents2 и т.д.), Которые обычно составляют всего несколько строк и позволяют им сохранять и загружать напрямую. Поэтому в вашем примере я бы сохранил файл с vars name1, name2, name3, id1, id2, id3 ...
и т.д.
EDIT: вы указали h5py в вопросе, так что я ответил, но стоит упомянуть, что с scipy.io.loadmat
вы должны иметь возможность получить исходные переменные, преобразованные в эквиваленты numpy (например, массивы объектов).
Ответ 5
Это действительно проблема с Matlab 7.3 и h5py.
Мой трюк заключается в преобразовании типа h5py._hl.dataset.Dataset
в массив numpy
.
Например,
np.array(data['data'])
решит вашу проблему с полем 'data'
.