web-dev-qa-db-ja.com

scipy.io.loadmatネスト構造(つまり、辞書)

与えられたルーチン(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-Fileを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)
33
mergen

以下は、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
44
mergen

マージの回答に対する単なる拡張機能です。残念ながら、オブジェクトのセル配列に到達すると再帰が停止します。次のバージョンでは、代わりにそれらのリストを作成し、可能であればセル配列要素への再帰を続行します。

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)
16
jpapon

私はscipyメーリングリスト( https://mail.python.org/pipermail/scipy-user/ )で、このデータにアクセスする方法が2つあるとアドバイスされました。

これは機能します:

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

私のマシンの出力:3

この種のアクセスの理由:「歴史的な理由により、Matlabではすべてがスカラーでさえ、少なくとも2D配列です。」したがって、scipy.io.loadmatはデフォルトでMatlabの動作を模倣します。

2

解決策が見つかると、「scipy.io.matlab.mio5_params.mat_structオブジェクト」のコンテンツにアクセスできます。

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

機能する別の方法:

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']

代わりにエラーをスローします:

IndexError:整数、スライスのみ(:)、省略記号(...)、numpy.newaxis(None)および整数またはブール配列は有効なインデックスです

しかし、私がそれを知っているとき、私は説明を補足するために戻ってきます。 numpy.ndarray.itemの説明(numpyリファレンスから):配列の要素を標準のPythonスカラーにコピーして返します。

(この回答は基本的に最初の質問に対するhpauljのコメントと同じですが、コメントは「見えない」または十分に理解できないと感じました。最初の解決策を探したとき、私は確かにそれに気付きませんでした。時間、数週間前)。

0