Вложенные структуры scipy.io.loadmat (то есть словари) - PullRequest
31 голосов
/ 10 августа 2011

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

Чтобы представить проблему, с которой я столкнулся, я приведу следующий пример игрушки:

load scipy.io as spio
a = {'b':{'c':{'d': 3}}}
# my dictionary: a['b']['c']['d'] = 3
spio.savemat('xy.mat',a)

Теперь я хочу прочитать файл mat обратно в python. Я попробовал следующее:

vig=spio.loadmat('xy.mat',squeeze_me=True)

Если я теперь хочу получить доступ к полям, я получаю:

>> vig['b']
array(((array(3),),), dtype=[('c', '|O8')])
>> vig['b']['c']
array(array((3,), dtype=[('d', '|O8')]), dtype=object)
>> vig['b']['c']['d']
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)

/<ipython console> in <module>()

ValueError: field named d not found.

Однако, используя опцию struct_as_record=False, можно получить доступ к полю:

v=spio.loadmat('xy.mat',squeeze_me=True,struct_as_record=False)

Теперь можно было получить к нему доступ

>> v['b'].c.d
array(3)

Ответы [ 5 ]

43 голосов
/ 12 января 2012

Вот функции, которые восстанавливают словари, просто используйте этот loadmat вместо loadmat scipy.io:

import scipy.io as spio

def loadmat(filename):
    '''
    this function should be called instead of direct spio.loadmat
    as it cures the problem of not properly recovering python dictionaries
    from mat files. It calls the function check keys to cure all entries
    which are still mat-objects
    '''
    data = spio.loadmat(filename, struct_as_record=False, squeeze_me=True)
    return _check_keys(data)

def _check_keys(dict):
    '''
    checks if entries in dictionary are mat-objects. If yes
    todict is called to change them to nested dictionaries
    '''
    for key in dict:
        if isinstance(dict[key], spio.matlab.mio5_params.mat_struct):
            dict[key] = _todict(dict[key])
    return dict        

def _todict(matobj):
    '''
    A recursive function which constructs from matobjects nested dictionaries
    '''
    dict = {}
    for strg in matobj._fieldnames:
        elem = matobj.__dict__[strg]
        if isinstance(elem, spio.matlab.mio5_params.mat_struct):
            dict[strg] = _todict(elem)
        else:
            dict[strg] = elem
    return dict
16 голосов
/ 18 марта 2015

Просто улучшение ответа Мергена, который, к сожалению, перестанет повторяться, если он достигнет массива объектов.Следующая версия создаст их списки и продолжит рекурсию в элементы массива ячеек, если это возможно.

import scipy
import numpy as np


def loadmat(filename):
    '''
    this function should be called instead of direct spio.loadmat
    as it cures the problem of not properly recovering python dictionaries
    from mat files. It calls the function check keys to cure all entries
    which are still mat-objects
    '''
    def _check_keys(d):
        '''
        checks if entries in dictionary are mat-objects. If yes
        todict is called to change them to nested dictionaries
        '''
        for key in d:
            if isinstance(d[key], spio.matlab.mio5_params.mat_struct):
                d[key] = _todict(d[key])
        return d

    def _todict(matobj):
        '''
        A recursive function which constructs from matobjects nested dictionaries
        '''
        d = {}
        for strg in matobj._fieldnames:
            elem = matobj.__dict__[strg]
            if isinstance(elem, spio.matlab.mio5_params.mat_struct):
                d[strg] = _todict(elem)
            elif isinstance(elem, np.ndarray):
                d[strg] = _tolist(elem)
            else:
                d[strg] = elem
        return d

    def _tolist(ndarray):
        '''
        A recursive function which constructs lists from cellarrays
        (which are loaded as numpy ndarrays), recursing into the elements
        if they contain matobjects.
        '''
        elem_list = []
        for sub_elem in ndarray:
            if isinstance(sub_elem, spio.matlab.mio5_params.mat_struct):
                elem_list.append(_todict(sub_elem))
            elif isinstance(sub_elem, np.ndarray):
                elem_list.append(_tolist(sub_elem))
            else:
                elem_list.append(sub_elem)
        return elem_list
    data = scipy.io.loadmat(filename, struct_as_record=False, squeeze_me=True)
    return _check_keys(data)
2 голосов
/ 27 февраля 2017

В списке рассылки scipy мне сообщили (https://mail.python.org/pipermail/scipy-user/)), что есть еще два способа доступа к этим данным.

Это работает:

import scipy.io as spio
vig=spio.loadmat('xy.mat')
print vig['b'][0, 0]['c'][0, 0]['d'][0, 0]

Вывод на мою машину: 3

Причина такого доступа: «По историческим причинам в Matlab все является, по крайней мере, двумерным массивом, даже скалярами». Так что scipy.io.loadmat имитирует поведение Matlab по умолчанию.

2 голосов
/ 14 сентября 2011

Найдя решение, можно получить доступ к содержимому «объекта scipy.io.matlab.mio5_params.mat_struct», нажав:

v['b'].__dict__['c'].__dict__['d']
0 голосов
/ 27 февраля 2017

Другой метод, который работает:

import scipy.io as spio
vig=spio.loadmat('xy.mat',squeeze_me=True)
print vig['b']['c'].item()['d']

Вывод:

3

Я также изучил этот метод в списке рассылки scipy.Я, конечно, не понимаю (пока), почему нужно добавить .item (), и:

print vig['b']['c']['d']

вместо этого выдаст ошибку:

IndexError: только целые числа,ломтики (:), многоточие (...), numpy.newaxis (None) и целые или логические массивы являются допустимыми индексами

, но я вернусь, чтобы дополнить объяснение, когда узнаю его,Объяснение numpy.ndarray.item (из ссылки на thenumpy): Скопируйте элемент массива в стандартный скаляр Python и верните его.

(Обратите внимание, что этот ответ в основном такой же, как и комментарий hpaulj кпервоначальный вопрос, но я чувствовал, что комментарий не является «видимым» или недостаточно понятным. Я, конечно, не заметил его, когда искал решение впервые, несколько недель назад).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...