Python scipy.io.loadmat嵌套结构(即字典)

Python scipy.io.loadmat嵌套结构(即字典),python,nested,structure,scipy,dictionary,Python,Nested,Structure,Scipy,Dictionary,使用给定的例程(如何使用scipy加载Matlab.mat文件),我无法访问更深层次的嵌套结构以将它们恢复到字典中 为了更详细地介绍我遇到的问题,我给出了以下玩具示例: 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'

使用给定的例程(如何使用scipy加载Matlab.mat文件),我无法访问更深层次的嵌套结构以将它们恢复到字典中

为了更详细地介绍我遇到的问题,我给出了以下玩具示例:

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.
现在可以通过计算机访问它了

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

找到解决方案后,可以通过以下方式访问“scipy.io.matlab.mio5_params.mat_struct object”的内容:

v['b'].__dict__['c'].__dict__['d']

以下是重建字典的函数,只需使用此loadmat而不是scipy.io的loadmat即可:

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

这只是对mergen答案的一个增强,不幸的是,如果它到达对象的单元格数组,它将停止递归。下面的版本将生成它们的列表,如果可能,将继续递归到单元数组元素中

import scipy as spio
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)

在scipy邮件列表()中,我被告知还有两种方法可以访问这些数据

这项工作:

import scipy.io as spio
vig=spio.loadmat('xy.mat')
print vig['b'][0, 0]['c'][0, 0]['d'][0, 0]
我的机器上的输出: 三,

这种访问的原因:“出于历史原因,在Matlab中,所有东西至少都是2D数组,甚至是标量。”
因此,scipy.io.loadmat根据默认值模拟Matlab行为

另一种有效的方法:

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

三,

我在scipy邮件列表中也学到了这种方法。我当然还不明白为什么要添加“.item()”,并且:

print vig['b']['c']['d']
将抛出一个错误:

索引器错误:只有整数、片(
)、省略号(
)、numpy.newaxis(
None
)和整数或布尔数组是有效的索引

但我知道后会回来补充解释的。numpy.ndarray.item的说明(来自UMPY参考): 将数组的元素复制到标准Python标量并返回它


(请注意,此答案与hpaulj对初始问题的评论基本相同,但我觉得该评论不够“可见”或可理解。几周前,当我第一次搜索解决方案时,我当然没有注意到它)。

您在loadmat中使用了哪些选项?使用默认设置,可以用如下表达式挖掘嵌套:
vig['b']['c'].item()['d'].item()
,分析结构化数组和对象数组的混合。“['b']是字典索引,其他的是字段名索引。这需要更好地宣传。scipy的loadmat的当前实现是一个真正的难题。干得好!实际上,@jpapon下面的方法更好,而且在处理像图像这样的数组时是必要的。非常感谢!这太棒了!起来,起来,起来你应该走!请把这个发给Mathworks,告诉他们一起行动。干得好。这段代码将包含双数组的字段的Matlab结构转换为包含双数组列表的python dict,这可能是作者的意图,但可能不是大多数人想要的。更好的返回值是使用ndarray作为值的dict。我建议在将ndarray转换为列表之前测试结构的数组内容。打印vig['b']['c']['d']不起作用的原因:vig['b']['c']返回numpy.void对象,因此如果您试图直接访问其中的项,python将抛出错误。方法item()返回缓冲区对象(),然后可以访问其内容。
print vig['b']['c']['d']